简体   繁体   English

AudioConverterRef采样率转换(iOS)

[英]AudioConverterRef sample rate conversion (iOS)

I write a voip app that uses "novocaine" library for recording and playback of sound. 我编写了一个使用“ novocaine”库来录制和播放声音的voip应用程序。 I set sample rate as 8kHz. 我将采样率设置为8kHz。 This sample rate is set in novocaine in AudioStreamBasicDescription of audio unit and as audio session property kAudioSessionProperty_PreferredHardwareSampleRate. 在novocaine中的音频单元的AudioStreamBasicDescription中设置此采样率,并将其设置为音频会话属性kAudioSessionProperty_PreferredHardwareSampleRate。 I understand that setting preferred hardware sample rate has no guarantee that actual hardware sample rate will be changed but it worked for all devices except iPhone6s and iPhone6s+ (when route is changed to speaker). 我知道,设置首选的硬件采样率并不能保证会更改实际的硬件采样率,但它适用于除iPhone6s和iPhone6s +(将路线更改为扬声器)以外的所有设备。 With iPhone6s(+) and speaker route I receive 48kHz sound from microphone. 使用iPhone6s(+)和扬声器路线时,我从麦克风接收48kHz声音。 So I need to somehow convert this 48 kHz sound to 8kHz. 因此,我需要以某种方式将此48 kHz声音转换为8kHz。 In documentation I found that AudioConverterRef can be used in this case but I have troubles with using it. 在文档中,我发现AudioConverterRef可以在这种情况下使用,但是我在使用它时遇到了麻烦。

I use AudioConverterFillComplexBuffer for sample rate conversion but it always returns -50 OSStatus (one or more parameters passed to the function were not valid). 我使用AudioConverterFillComplexBuffer进行采样率转换,但它始终返回-50 OSStatus(传递给该函数的一个或多个参数无效)。 This is how I use audio converter: 这就是我使用音频转换器的方式:

// Setup AudioStreamBasicDescription for input
inputFormat.mSampleRate = 48000.0;
inputFormat.mFormatID = kAudioFormatLinearPCM;
inputFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
inputFormat.mChannelsPerFrame = 1;
inputFormat.mBitsPerChannel = 8 * sizeof(float);
inputFormat.mFramesPerPacket = 1;
inputFormat.mBytesPerFrame = sizeof(float) * inputFormat.mChannelsPerFrame;
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;

// Setup AudioStreamBasicDescription for output
outputFormat.mSampleRate = 8000.0;
outputFormat.mFormatID = kAudioFormatLinearPCM;
outputFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
outputFormat.mChannelsPerFrame = 1;
outputFormat.mBitsPerChannel = 8 * sizeof(float);
outputFormat.mFramesPerPacket = 1;
outputFormat.mBytesPerFrame = sizeof(float) * outputFormat.mChannelsPerFrame;
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;


// Create new instance of audio converter
AudioConverterNew(&inputFormat, &outputFormat, &converter);

// Set conversion quality
UInt32 tmp = kAudioConverterQuality_Medium;
AudioConverterSetProperty( converter, kAudioConverterCodecQuality,
                          sizeof( tmp ), &tmp );
AudioConverterSetProperty( converter, kAudioConverterSampleRateConverterQuality, sizeof( tmp ), &tmp );

// Get the size of the IO buffer(s)
UInt32 bufferSizeFrames = 0;
size = sizeof(UInt32);
AudioUnitGetProperty(self.inputUnit,
                                 kAudioDevicePropertyBufferFrameSize,
                                 kAudioUnitScope_Global,
                                 0,
                                 &bufferSizeFrames,
                                 &size);
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);

// Allocate an AudioBufferList plus enough space for array of AudioBuffers
UInt32 propsize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * outputFormat.mChannelsPerFrame);

// Malloc buffer lists
convertedInputBuffer = (AudioBufferList *)malloc(propsize);
convertedInputBuffer->mNumberBuffers = 1;

// Pre-malloc buffers for AudioBufferLists
convertedInputBuffer->mBuffers[0].mNumberChannels = outputFormat.mChannelsPerFrame;
convertedInputBuffer->mBuffers[0].mDataByteSize = bufferSizeBytes;
convertedInputBuffer->mBuffers[0].mData = malloc(bufferSizeBytes);
memset(convertedInputBuffer->mBuffers[0].mData, 0, bufferSizeBytes);

// Setup callback for converter
static OSStatus inputProcPtr(AudioConverterRef               inAudioConverter,
                                 UInt32*                         ioNumberDataPackets,
                                 AudioBufferList*                ioData,
                                 AudioStreamPacketDescription* __nullable* __nullable  outDataPacketDescription,
                                 void* __nullable                inUserData)
{
    // Read data from buffer
}

// Perform actual sample rate conversion
AudioConverterFillComplexBuffer(converter, inputProcPtr, NULL, &numberOfFrames, convertedInputBuffer,  NULL)

inputProcPtr callback is never called. 永远不会调用inputProcPtr回调。 I tried to set different number of frames but still receive OSStatus -50. 我尝试设置不同的帧数,但仍然收到OSStatus -50。

1) Is using AudioConverterRef is correct way to make sample rate conversion or it could be done in different way? 1)使用AudioConverterRef是进行采样率转换的正确方法还是可以用其他方法完成?

2) What is wrong with my conversion implementation? 2)我的转换实施有什么问题?

Thank you all in advance 谢谢大家

One problem is this: 一个问题是:

AudioUnitGetProperty(self.inputUnit,
                             kAudioDevicePropertyBufferFrameSize,
                             kAudioUnitScope_Global,
                             0,
                             &bufferSizeFrames,
                             &size);

kAudioDevicePropertyBufferFrameSize is an OSX property, and doesn't exist on iOS. kAudioDevicePropertyBufferFrameSize是OSX属性,在iOS上不存在。 How is this code even compiling? 该代码如何编译?

If you've somehow made it compile, check the return code from this function! 如果您以某种方式进行了编译,请检查此函数的返回码! I've got a feeling that it's failing, and bufferSizeFrames is zero. 我感觉到它失败了, bufferSizeFrames为零。 That would make AudioConverterFillComplexBuffer return -50 ( kAudio_ParamError ). 那将使AudioConverterFillComplexBuffer返回-50( kAudio_ParamError )。

So on iOS, either pick a bufferSizeFrames yourself or base it on AVAudioSession 's IOBufferDuration if you must. 因此,在iOS上,您可以自己选择bufferSizeFrames或根据需要使用AVAudioSessionIOBufferDuration

Another problem: check your return codes. 另一个问题:检查您的返回码。 All of them! 他们全部!

eg 例如

UInt32 tmp = kAudioConverterQuality_Medium;
AudioConverterSetProperty( converter, kAudioConverterCodecQuality,
                      sizeof( tmp ), &tmp );

I'm pretty sure there's no codec to speak of in LPCM->LPCM conversions, and that kAudioConverterQuality_Medium is not the right value to use with kAudioConverterCodecQuality in any case. 我敢肯定,在LPCM-> LPCM转换中没有任何编解码器可言,而且kAudioConverterQuality_MediumkAudioConverterQuality_Medium是与kAudioConverterCodecQuality一起使用的正确值。 I don't see how this call can succeed. 我看不出此通话如何成功。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM