android


Generate audio graph from 3gpp


I'm writing audio recorder as part of application which has audio graph as well. For recording and showing live audio graph it was pretty easy, just call MediaRecorder.GetMaxAmplitude() every X ms and based on that update canvas which represents the audio graph.
The problem - I want to have same audio graph when one of the recording is opened, so now I can't use .GetMaxAmplitude() method since I would need to fully play the recording to generate the graph, which would take too much time and is just silly.
If recording output would be .wav, it would be pretty straightforward, lots of material out where how to do it, however MediaRecorder doesn't support .wav and I don't really want embed full ffmpeg with wrapper to my app just for this small functionality to decode 3gpp into wav.
What are my options here?
Using MediaExtractor and MediaCodec you can decode your 3gp (or any other supported mime type) into a series of PCM-16bit (mime audio/raw) buffers and from that you can subsample to obtain your desired amplitude graph at a "graphable" sampling rate.
This is an example using synchronous processing on the input/output buffer so run it on non-UI thread.
Synchronous PCM-16bit MediaCodec Example:
var file = new Java.IO.File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDownloads), "someaudiofile.mp3");
if (file.CanRead())
{
var mediaExtractor = new MediaExtractor();
mediaExtractor.SetDataSource(file.ToString());
mediaExtractor.SelectTrack(0); // which track? lets assume single/mono for this example
var mediaFormat = mediaExtractor.GetTrackFormat(0);
var mime = mediaFormat.GetString(MediaFormat.KeyMime);
var mediaCodec = MediaCodec.CreateDecoderByType(mime);
mediaCodec.Configure(mediaFormat, null, null, MediaCodecConfigFlags.None);
mediaCodec.Start();
var bufferInfo = new MediaCodec.BufferInfo();
var inputDone = false;
while (true)
{
if (!inputDone) // process input stream and queue it up for output processing
{
int inputBufferIndex = mediaCodec.DequeueInputBuffer(10000);
if (inputBufferIndex >= 0)
{
var inputBuffer = mediaCodec.GetInputBuffer(inputBufferIndex);
int chunkSize = mediaExtractor.ReadSampleData(inputBuffer, 0);
Log.Debug("SO", $"Input Buffer: {inputBufferIndex}");
if (chunkSize <= 0)
{
mediaCodec.QueueInputBuffer(inputBufferIndex, 0, 0, 0L, MediaCodecBufferFlags.EndOfStream);
inputDone = true;
}
else
{
mediaCodec.QueueInputBuffer(inputBufferIndex, 0, chunkSize, mediaExtractor.SampleTime, MediaCodecBufferFlags.None);
mediaExtractor.Advance();
}
}
}
int outputBufferIndex = mediaCodec.DequeueOutputBuffer(bufferInfo, 1000000);
if (outputBufferIndex >= 0)
{
Log.Debug("SO", $"Output Buffer: {outputBufferIndex}");
if (bufferInfo.Size != 0)
{
var outputBuffer = mediaCodec.GetOutputBuffer(outputBufferIndex); // PCM 16-bit output
var outpuFormat = mediaCodec.GetOutputFormat(outputBufferIndex);
outputBuffer.Position(0);
// !!! Sub-sample the buffer based upon your needed sampling rate for display
var pcm16bitBuffer = outputBuffer.AsShortBuffer();
while (pcm16bitBuffer.HasRemaining)
{
var x = pcm16bitBuffer.Get();
// store the prior values and avg./max/... them for later display based upon some subsampling rate
}
pcm16bitBuffer.Dispose();
mediaCodec.ReleaseOutputBuffer(outputBufferIndex, false);
if (bufferInfo.Flags.HasFlag(MediaCodecBufferFlags.EndOfStream))
break;
}
else
break;
}
else if (outputBufferIndex == -2)
{
Log.Debug("SO", "Output buffer is not available yet, feed more input");
}
}
mediaCodec.Stop();
mediaCodec.Release();
}
Note: There are asynchronous methods available also, consult the MediaCodec docs for Android API levels for what is available for your app's targeted audience.
Output sample using the above method:
Re: MediaCodec

Related Links

BroadcastReceiver automatically reads SMS in Android
Android OkHttp addPathSegment replaces slashes
Dagger 2 Constructor Injection and more
Check if user is already logged in to Facebook
WCF JSON missunderstand
OnReceive OnReceive firing twice
Android : How to poplulate Spinner content based on the selection of previous spinner?
Android Cannot POST /api response
Android Lollipop - WiFi Hotspot setWifiApEnabled() get InvocationTargetException
Asus zenphone 2 USB debugging
Android Speech Recognition with RecyclerView
Android - Overriding a Style per Theme
How to avoid redundancy in database?
How to pass intent from activity to fragment in Android
Application crashes after Toast disappears
Create a different toolBars on different Fragments in one Activity

Categories

HOME
assembly
fpga
amp-html
case
sh
graphics
owl-carousel
pdfbox
simulink
web-hosting
nested-if
onclick
bookmarks
android-json
angularfire2
accessibility
32bit-64bit
commonjs
siddhi
project-intu
3nf
magnetic-cards
do-while
alpacajs
searchview
forgot-password
template-engine
git-extensions
foreign-keys
nim
database-connection
libigl
inno-download-plugin
epsilon
windows-server-2003
data.stackexchange.com
motion-detection
intersystems-cache
spotipy
quantlib
user-defined-functions
alm
dst
utorrent
cart
fstream
papaparse
sharding
xajax
parse-tree
demo
mcustomscrollbar
apriori
lpsolve
show-hide
openresty
carrot2
filehandle
bbpress
gameplay-kit
django-1.10
rsa-archer-grc
pocketsphinx-android
actor-platform
iotivity
xdoclet
shopping
nss
kiosk
pushbots
user-management
concept-insights
terracotta
fuelcms
haskell-warp
git-ftp
snackbar
teaspoon
personalization
xml-dsig
functional-java
wxformbuilder
alternate
authlogic
cfcache
eclipse-kepler
notorm
code-duplication
windows-phone-8-sdk
jeromq
poker
abstract-data-type
css-reset
stacky
fb.ui
dopostback
panda3d
saleslogix
appjs
cross-database
luabind
flex-mobile
filemerge
resharper-6.0
interprocess
burndowncharts
lobo-cobra
wordprocessingml
fault-tolerance
firefox-3
java1.4

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App