5

I need to build a visual graph that represents voice levels (dB) in a recorded file. I tried to do it this way:

NSError *error = nil;
AVAudioPlayer *meterPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:self.recording.fileName] error:&error];

if (error) {
    _lcl_logger(lcl_cEditRecording, lcl_vError, @"Cannot initialize AVAudioPlayer with file %@ due to: %@ (%@)", self.recording.fileName, error, error.userInfo);
} else {
    [meterPlayer prepareToPlay];
    meterPlayer.meteringEnabled = YES;
    
    for (NSTimeInterval i = 0; i <= meterPlayer.duration; ++i) {
        meterPlayer.currentTime = i;
        [meterPlayer updateMeters];
        float averagePower = [meterPlayer averagePowerForChannel:0];
        _lcl_logger(lcl_cEditRecording, lcl_vTrace, @"Second: %f, Level: %f dB", i, averagePower);
    }
}
[meterPlayer release];

It would be cool if it worked out however it didn't. I always get -160 dB. Any other ideas on how to implement that?

UPD: Here is what I got finally:

alt text http://img22.imageshack.us/img22/5778/waveform.png

Community
  • 1
  • 1
Aleks N.
  • 6,051
  • 3
  • 42
  • 42

4 Answers4

9

I just want to help the others who have come into this same question and used a lot of time to search. To save your time, I put out my answer. I dislike somebody here who treat this as kind of secret...

After search around the articles about extaudioservice, audio queue and avfoundation.

I realised that i should use AVFoundation, reason is simple, it is the latest bundle and it is Objective C but not so cpp style.

So the steps to do it is not complicated:

  1. Create AVAsset from the audio file
  2. Create avassetreader from the avasset
  3. Create avassettrack from avasset
  4. Create avassetreadertrackoutput from avassettrack
  5. Add the avassetreadertrackoutput to the previous avassetreader to start reading out the audio data

From the avassettrackoutput you can copyNextSampleBuffer one by one (it is a loop to read all data out).

Each copyNextSampleBuffer gives you a CMSampleBufferRef which can be used to get AudioBufferList by CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer. AudioBufferList is array of AudioBuffer. AudioBuffer is the a bunch of audio data which is stored in its mData part.

You can implement the above in extAudioService as well. But i think the above avfoundation approach is easier.

So next question, what to do with the mData? Note that when you get the avassetreadertrackoutput, you can specify its output format, so we specify the output is lpcm.

Then the mData you finally get is actually a float format amplitude value.

Easy right? Though i used a lot of time to organise this from piece here and there.

Two useful resource for share: Read this article to know basic terms and conceptions: https://www.mikeash.com/pyblog/friday-qa-2012-10-12-obtaining-and-interpreting-audio-data.html

Sample code: https://github.com/iluvcapra/JHWaveform You can copy most of the above mentioned code from this sample directly and used for your own purpose.

user13500
  • 3,817
  • 2
  • 26
  • 33
elephant.lyh
  • 133
  • 1
  • 5
3

I haven't used it myself, but Apple's avTouch iPhone sample has bar graphs powered by AVAudioPlayer, and you can easily check to see how they do it.

mahboudz
  • 39,196
  • 16
  • 97
  • 124
  • I know this sample and the key difference is that they are _playing_ the audio file. I don't need to play it. I need to investigate an arbitrary part of the file at an arbitrary moment of time without actually playing it. And to build a graph that would visualize that characteristics. – Aleks N. Nov 20 '09 at 01:41
  • Ahhhh... You'll have to read the data yourself and find the levels for selected periods. And since the file is not decompressed, you will have to decode it, or use other frameworks to get to the PCM data. I understand that ExtAudio, Audio Queue, and Remote IO AudioUnit would be areas for you to look into. I'm still learning myself. – mahboudz Nov 20 '09 at 08:28
  • >>And since the file is not decompressed, you will have to decode it --> Please, don't reply if you don't know the answer. There is no difference in reading compressed or raw data. SDK is handling all this. Thus you're just misleading people who will reading you afterwards. – Aleks N. Nov 20 '09 at 13:54
  • Are you saying that if you looked at the contents of the file whose URL you are passing to AVAudioPlayer, it would look the same whether it was compressed or decompressed? Then why bother using ExtAudioFile? – mahboudz Nov 20 '09 at 19:28
0

I don't think you can use AVAudioPlayer based on your constraints. Even if you could get it to "start" without actually playing the sound file, it would only help you build a graph as fast as the audio file would stream. What you're talking about is doing static analysis of the sound, which will require a much different approach. You'll need to read in the file yourself and parse it manually. I don't think there's a quick solution using anything in the SDK.

Shaggy Frog
  • 27,575
  • 16
  • 91
  • 128
0

Ok guys, seems I'm going to answer my own question again: http://www.supermegaultragroovy.com/blog/2009/10/06/drawing-waveforms/ No a lot of concretics, but at least you will know what Apple docs to read.

Aleks N.
  • 6,051
  • 3
  • 42
  • 42
  • 1
    I know this is just under a year old now, but in your original post you did say you were going to post an open-source component. I read your blog post but didn't see anything downloadable. Have I missed something or did you change your mind? – Tom Irving Sep 25 '10 at 17:53
  • I didn't change my mind, but I simply never got a chance to do it. Write an email to me with your needs, and probably I'll be able to help you. – Aleks N. Sep 25 '10 at 18:45