Iphone 使用音频队列框架录制的数据格式

Iphone 使用音频队列框架录制的数据格式,iphone,Iphone,我正在编写一个iPhone应用程序,它可以记录用户的声音,并将音频数据输入到一个库中进行修改,比如改变节奏和音高。我从Apple的SpeakHere示例代码开始: 该项目为录制用户的声音并回放奠定了基础。它工作得很好 现在我正在深入研究代码,我需要弄清楚如何将音频数据输入SoundTouch库()以更改音高。在浏览代码时,我熟悉了音频队列框架,并找到了从录音中接收音频数据的位置 本质上,您可以调用AudioQueueNewInput来创建一个新的输入队列。您传递一个回调函数,该函数在每次音频数

我正在编写一个iPhone应用程序,它可以记录用户的声音,并将音频数据输入到一个库中进行修改,比如改变节奏和音高。我从Apple的SpeakHere示例代码开始:

该项目为录制用户的声音并回放奠定了基础。它工作得很好

现在我正在深入研究代码,我需要弄清楚如何将音频数据输入SoundTouch库()以更改音高。在浏览代码时,我熟悉了音频队列框架,并找到了从录音中接收音频数据的位置

本质上,您可以调用
AudioQueueNewInput
来创建一个新的输入队列。您传递一个回调函数,该函数在每次音频数据块可用时调用。在这个回调中,我需要将数据块传递到SoundTouch

我有它的所有设置,但我从SoundTouch库播放的噪音非常静态(它几乎与原始的相似)。如果我不通过SoundTouch播放原始音频,效果会很好

基本上,我缺少一些关于我得到的实际数据代表什么的信息。我假设我得到一个
short
s流,它们是样本,每个通道1个样本。这就是SoundTouch对它的期望,所以它一定不是正确的

下面是设置音频队列的代码,您可以看到它是如何配置的

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;
}
}
下面是代码的一部分,它实际设置了:

    SetupAudioFormat(kAudioFormatLinearPCM);

    // create the queue
    XThrowIfError(AudioQueueNewInput(
                                  &mRecordFormat,
                                  MyInputBufferHandler,
                                  this /* userData */,
                                  NULL /* run loop */, NULL /* run loop mode */,
                                  0 /* flags */, &mQueue), "AudioQueueNewInput failed");
最后,这里是处理新音频数据的回调:

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

您可以看到,我正在将
inBuffer->mAudioData
中的数据传递给SoundTouch。在我的回调中,字节究竟代表什么,即如何从
mAudioData
中提取样本?

您必须检查所获得的数据的endianess、signedness等是否符合库的预期。使用
AudioStreamBasicDescription
mFormatFlags
确定源格式。然后,您可能需要转换样本(例如,
newSample=sample+0x8000

音频队列的默认结束度可能与您期望的相反。您可能需要在录制后和播放前交换每个16位音频样本的上下字节

sample_le = (0xff00 & (sample_be << 8)) | (0x00ff & (sample_be >> 8)) ;
sample_le=(0xff00&(sample_be>8));

我终于发现SoundTouch希望样本是浮动的。现在可以用int样本类型重新编译它了。谢谢我也想到了这一点,我找到了一个可以在格式选项中传递的标志,使其成为big-endian而不是small-endian,这样我就不必手动执行。这不是问题,但谢谢!您计划如何将SoundTouch library的LGPL许可证与拒绝iOS应用程序库动态链接的苹果政策相匹配?我们是否可以合法地在iOS中使用SoundTouch library?