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

how to retrieve same image to android phones with different screen densities+json
How to make my app look the same in all devices?
Android: How to lauch an app from browser?
Android Html.fromHtml tag “strike” does not works
ListView not updating after web service Sync
How to control what happens when a view's wrap_content exceeds its parent's width/height?
Android Java Widget showing no icon on list
How to remove html tags from listview?
Android EditText convert string to integer error
Error in ArrayAdapter - layout inflator thows error
progress bar is smaller than its drawable causing the image to truncate
Automatically open/close keyboard using SearchView in Toolbar
Refresh listview setvisible all checkbox
How Gradle subtasks be executed in build task “assembleDebug”?
Android Gradle Plugin 1.1.0 - getNdkFolder() not found anymore, any replacement?
FragmentDialog with custom layout and without title not maximized

Categories

HOME
sonarqube
django
jpa
gdb
ng-idle
pdfbox
tivoli
bookmarks
x264
mvc5
mel
alamofire
intercom
dynatrace
internationalization
intershop
mapstruct
gmm
topology
ag
cgbitmapcontext
pagespeed
visual-studio-extensions
definitelytyped
interbase
fable-f#
pylons
wcag
mql
database-comparison
getpixel
sharding
pdw-file-browser
mnist
amazon-ses
oracle-service-bus
fwrite
dynamic-linq
httrack
magento-2.0.7
automapper-5
serde
netbeans6.8
configurationmanager
twilio-click-to-call
custom-font
flyout
py2neo
flex-monkey
actor-platform
signalr.client
windows-azure-queues
tooleap
delta
dimension
shopping
nupic
fuelcms
coda
.bash-profile
doctype
uncompress
keyup
cwrsync
ansi
jquery-forms-plugin
nodelist
addressing-mode
live555
dsym
twitter-bootstrap-rails
koala
sbcl
mbunit
scringo
traceability
onload-event
wimax
robotlegs
correlated-subquery
subproject
multiplexing
jquery-address
convention
role
umra
strsep
timthumb
collectionviewsource
qglwidget
objective-j
blender-2.49
visualj#
interop-domino
soappy

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