QuickTime Audio - Easy Frequency Level Metering with MovieAudio APIs

Q: How can I retrieve the frequency levels of an audio file or a QuickTime Movie and display them like iTunes or QuickTime Player does?

A: QuickTime 7 introduced a number of 'MovieAudio' APIs that now make performing tasks such as volume and frequency metering, adjusting gain, setting audio balance and mute very easy. While some of these APIs may duplicate existing functionality, the 'MovieAudio' APIs present a simpler, more consistent programmer interface and are now the preferred way to perform these tasks.

For example, to monitor frequency levels it is no longer necessary to drop down to the media handler level and use MediaSetSoundEqualizerBands and MediaGetSoundEqualizerBandLevels.

Given a QuickTime Movie, it's easy to perform frequency metering using two 'MovieAudio' APIs. First call SetMovieAudioFrequencyMeteringNumBands to configure frequency metering for a particular audio mix (see the References section for a description of the available mixes), then call GetMovieAudioFrequencyLevels to retrieve the current frequency meter levels whenever you want them. The values are returned in a QTAudioFrequencyLevels structure you allocate according to the number of bands you would like to monitor.

Note: These Metering APIs will only function during Movie playback since they get their audio data from the playback chain.

GetMovieAudioFrequencyLevels is performing FFTs (Fast Fourier Transform) to calculate the meter levels. The FFT window is proportional to the number of spectral bands you are interested in viewing:

  • 1-4 bands -> 256-pt FFT

  • 5-15 bands -> 512-pt FFT

  • 16-128 bands -> 1024-pt FFT

Note: The FFT is performed each time you request meter levels so the amount of overhead incurred is proportional to the frequency of the call. Additionally there is an FFT for each channel you request, this means that if you're trying to meter N-channel surround sound data without a pre-mix (for example, using kQTAudioMeter_DeviceMix) then N FFTs are being performed per call.

Listing 1: Setting up frequency metering.

UInt32 numberOfBandLevels = 7; // increase this number for more frequency bands
UInt32 numberOfChannels = 2; // for StereoMix - if using DeviceMix,
                                 // you need to get the channel count of the device

QTAudioFrequencyLevels *freqResults = NULL;

...

// call this once per movie to establish metering
err = SetMovieAudioFrequencyMeteringNumBands(myMovie,
                                             kQTAudioMeter_StereoMix,
                                             &numberOfBandLevels);
if (err) goto bail;

freqResults = malloc(offsetof(QTAudioFrequencyLevels,
                              level[numberOfBandLevels * numberOfChannels]));
if (freqResults == NULL) {
    err = memFullErr;
    goto bail:
}

freqResults->numChannels = numberOfChannels;
freqResults->numFrequencyBands = numberOfBandLevels;

...

Listing 2: Getting the metered levels.

...

// call each time you are ready to display meter levels
if (freqResults != NULL) {
    err = GetMovieAudioFrequencyLevels(myMovie, kQTAudioMeter_StereoMix, freqResults);
    if (err) goto bail;

    for (i = 0; i < freqResults->numChannels; i++) {
        for (j = 0; j < freqResults->numFrequencyBands; j++) {
            // the frequency levels are Float32 values between 0. and 1.
            Float32 value = freqResults->level[(i *
                                                freqResults->numFrequencyBands) + j];

            // do something with the value
            ...
        }
    }
}

...

Listing 3: Retrieving the channel count when using kQTAudioMeter_DeviceMix.

 AudioChannelLayout *layout = NULL;
    UInt32 size = 0;
    UInt32 numChannels = 0;
    OSStatus err;

...

    // get the size of the device layout
    err = QTGetMoviePropertyInfo(theMovie, kQTPropertyClass_Audio,
                                 kQTAudioPropertyID_DeviceChannelLayout,
                                 NULL, &size, NULL);

    if (err || (0 == size)) goto bail; 

    // allocate memory for the device layout
    layout = (AudioChannelLayout*)calloc(1, size);
    if (NULL == layout) {
        err = memFullErr;
        goto bail;
    } 

    // get the device layout from the movie
    err = QTGetMovieProperty(theMovie, kQTPropertyClass_Audio, 
                             kQTAudioPropertyID_DeviceChannelLayout, 
                             size, 
                             layout, 
                             NULL);
    if (err) goto bail;

    // now get the number of channels
    numChannels = (layout->mChannelLayoutTag ==
                   kAudioChannelLayoutTag_UseChannelDescriptions) ?
                   layout->mNumberChannelDescriptions :
                   AudioChannelLayoutTag_GetNumberOfChannels(layout->mChannelLayoutTag);

    free(layout);

...

References

Movie Audio Mixes

Three constants define the mix of audio channels for frequency metering:

kQTAudioMeter_StereoMix - Meter a stereo (two-channel) mix of the enabled sound tracks in the movie.

kQTAudioMeter_MonoMix - Meter the movie as if it had been mixed to monaural.

kQTAudioMeter_DeviceMix - Meter the movie’s mix to the AudioChannelLayout of the device the
movie is playing to.

Back to Top

Document Revision History

Date Notes
2009-01-29 Editorial
2006-01-26 Describes how to perform frequency band level metering using MovieAudio metering APIs.

Posted: 2009-01-29


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.