Formato de datos de la grabación usando el marco de Audio Queue

Estoy escribiendo una aplicación para iPhone que debería grabar la voz de los usuarios y alimentar los datos de audio en una biblioteca para modificaciones como el cambio de tempo y tono. Comencé con el código de ejemplo SpeakHere de Apple:

http://developer.apple.com/library/ios/#samplecode/SpeakHere/Introduction/Intro.html

Ese proyecto sienta las bases para grabar la voz del usuario y reproducirla. Funciona bien.

Ahora me estoy sumergiendo en el código y necesito descubrir cómo alimentar los datos de audio a la biblioteca SoundTouch (http://www.surina.net/soundtouch/) para cambiar el tono. Me familiaricé con el marco de Audio Queue mientras revisaba el código, y encontré el lugar donde recibo los datos de audio de la grabación.

Esencialmente, llamasAudioQueueNewInput para crear una nueva cola de entrada. Pasa una función de devolución de llamada que se llama cada vez que hay disponible un fragmento de datos de audio. Es dentro de esta devolución de llamada que necesito pasar los fragmentos de datos a SoundTouch.

Lo tengo todo configurado, pero el ruido que reproduzco desde la biblioteca SoundTouch es muy estático (apenas se parece al original). Si no lo paso por SoundTouch y reproduzco el audio original, funciona bien.

Básicamente, me falta algo sobre lo que representan los datos reales que obtengo. Estaba asumiendo que estoy recibiendo una corriente deshorts que son muestras, 1 muestra para cada canal. Así es como SoundTouch lo espera, por lo que no debe ser correcto de alguna manera.

Aquí está el código que configura la cola de audio para que pueda ver cómo está configurada.

void AQRecorder::SetupAudioFormat(UInt32 inFormatID)
{
memset(&mRecordFormat, 0, sizeof(mRecordFormat));

UInt32 size = sizeof(mRecordFormat.mSampleRate);
XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
                                          &size, 
                                          &mRecordFormat.mSampleRate), "couldn't get hardware sample rate");

size = sizeof(mRecordFormat.mChannelsPerFrame);
XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, 
                                          &size, 
                                          &mRecordFormat.mChannelsPerFrame), "couldn't get input channel count");

mRecordFormat.mFormatID = inFormatID;
if (inFormatID == kAudioFormatLinearPCM)
{
    // if we want pcm, default to signed 16-bit little-endian
    mRecordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
    mRecordFormat.mBitsPerChannel = 16;
    mRecordFormat.mBytesPerPacket = mRecordFormat.mBytesPerFrame = (mRecordFormat.mBitsPerChannel / 8) * mRecordFormat.mChannelsPerFrame;
    mRecordFormat.mFramesPerPacket = 1;
}
}

Y aquí hay parte del código que realmente lo configura:

    SetupAudioFormat(kAudioFormatLinearPCM);

    // create the queue
    XThrowIfError(AudioQueueNewInput(
                                  &mRecordFormat,
                                  MyInputBufferHandler,
                                  this /* userData */,
                                  NULL /* run loop */, NULL /* run loop mode */,
                                  0 /* flags */, &mQueue), "AudioQueueNewInput failed");

Y finalmente, aquí está la devolución de llamada que maneja nuevos datos de audio:

void AQRecorder::MyInputBufferHandler(void *inUserData,
                                  AudioQueueRef inAQ,
                                  AudioQueueBufferRef inBuffer,
                                  const AudioTimeStamp *inStartTime,
                                  UInt32 inNumPackets,
                                  const AudioStreamPacketDescription *inPacketDesc) {
AQRecorder *aqr = (AQRecorder *)inUserData;
try {
        if (inNumPackets > 0) {
            CAStreamBasicDescription queueFormat = aqr->DataFormat();
            SoundTouch *soundTouch = aqr->getSoundTouch();

            soundTouch->putSamples((const SAMPLETYPE *)inBuffer->mAudioData,
                                   inBuffer->mAudioDataByteSize / 2 / queueFormat.NumberChannels());

            SAMPLETYPE *samples = (SAMPLETYPE *)malloc(sizeof(SAMPLETYPE) * 10000 * queueFormat.NumberChannels());
            UInt32 numSamples;
            while((numSamples = soundTouch->receiveSamples((SAMPLETYPE *)samples, 10000))) {
                // write packets to file
                XThrowIfError(AudioFileWritePackets(aqr->mRecordFile,
                                                    FALSE,
                                                    numSamples * 2 * queueFormat.NumberChannels(),
                                                    NULL,
                                                    aqr->mRecordPacket,
                                                    &numSamples,
                                                    samples),
                              "AudioFileWritePackets failed");
                aqr->mRecordPacket += numSamples;
            }
            free(samples);
        }

        // if we're not stopping, re-enqueue the buffe so that it gets filled again
        if (aqr->IsRunning())
            XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL), "AudioQueueEnqueueBuffer failed");
} catch (CAXException e) {
    char buf[256];
    fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
}

Puedes ver que estoy pasando los datos eninBuffer->mAudioData a SoundTouch. En mi devolución de llamada, qué representan exactamente los bytes, es decir, cómo extraigo muestras demAudioData?

Respuestas a la pregunta(2)

Su respuesta a la pregunta