Formato de dados da gravação usando a estrutura da Fila de áudio

Estou escrevendo um aplicativo para iPhone que deve gravar a voz do usuário e alimentar os dados de áudio em uma biblioteca para modificações como alteração de andamento e afinação. Comecei com o código de exemplo SpeakHere da Apple:

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

Esse projeto estabelece as bases para gravar a voz do usuário e reproduzi-la. Isso funciona bem.

Agora estou mergulhando no código e preciso descobrir como alimentar os dados de áudio na biblioteca do SoundTouch (http://www.surina.net/soundtouch/) para alterar o tom. Familiarizei-me com a estrutura da Fila de áudio durante a leitura do código e encontrei o local onde recebo os dados de áudio da gravação.

Essencialmente, você chamaAudioQueueNewInput para criar uma nova fila de entrada. Você passa uma função de retorno de chamada que é chamada toda vez que um pedaço de dados de áudio está disponível. É nesse retorno de chamada que preciso passar os pedaços de dados para o SoundTouch.

Eu tenho tudo configurado, mas o ruído que reproduzo da biblioteca SoundTouch é muito estático (quase se parece com o original). Se eu não passar pelo SoundTouch e reproduzir o áudio original, ele funcionará bem.

Basicamente, estou perdendo algo sobre o que os dados reais que estou recebendo representam. Eu estava assumindo que estou recebendo um fluxo deshorts que são amostras, 1 amostra para cada canal. É assim que o SoundTouch espera, portanto não deve estar certo de alguma forma.

Aqui está o código que configura a fila de áudio para que você possa ver como ela 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;
}
}

E aqui está parte do código que realmente o configura:

    SetupAudioFormat(kAudioFormatLinearPCM);

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

E finalmente, aqui está o retorno de chamada que lida com novos dados de áudio:

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));
}
}

Você pode ver que estou passando os dadosinBuffer->mAudioData ao SoundTouch. No meu retorno de chamada, quais são exatamente os bytes que representam, ou seja, como extraio amostras demAudioData?

questionAnswers(2)

yourAnswerToTheQuestion