我只是尝试开发一个VOIP应用程序,
从RecordingCallBack获取的音频缓冲区将被包装 到NSData,然后通过GCDAsyncSocket
并且远程端将获得NSData,解包为音频
缓冲区,然后PlayingCallBack将获取音频缓冲区。
我的计划到目前为止,在本地运行正常(套接字将数据发送到本地,并在本地播放缓冲区)
但是当它在两个设备上运行时(一个真正的iphone-4s,一个模拟器) 声音变得陌生,听起来像机器人声音
无论如何都要避免机器人音效?
这是我的AudioUnit设置:
#pragma mark - Init Methods
- (void)initAudioUint
{
OSStatus status;
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Get component
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
status = AudioComponentInstanceNew(inputComponent, &audioUnit);
checkStatus(status);
// Enable IO for recording
UInt32 flag = 1;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Enable IO for playback
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Describe format
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100.0f; // FS
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
audioFormat.mChannelsPerFrame = 1; // stereo output
audioFormat.mFramesPerPacket = 1;
audioFormat.mBitsPerChannel = sizeof(short) * 8; // 16-bit
audioFormat.mBytesPerFrame = audioFormat.mBitsPerChannel / 8 * audioFormat.mChannelsPerFrame;
audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
// Apply format
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
// Set input callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = recordingCallback;
callbackStruct.inputProcRefCon = (__bridge void*)self;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
kInputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
// Set output callback
callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = (__bridge void*)self;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
/*
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
flag = 0;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kInputBus,
&flag,
sizeof(flag));
// Allocate our own buffers (1 channel, 16 bits per sample, thus 16 bits per frame, thus 2 bytes per frame).
// Practice learns the buffers used contain 512 frames, if this changes it will be fixed in processAudio.
tempBuffer.mNumberChannels = 1;
tempBuffer.mDataByteSize = 512 * 2;
tempBuffer.mData = malloc( 512 * 2 );
checkStatus(status);
// Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
flag = 0;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kInputBus,
&flag,
sizeof(flag));
// TODO: Allocate our own buffers if we want
*/
// Initialise
status = AudioUnitInitialize(audioUnit);
checkStatus(status);
conversionBuffer = (SInt16 *) malloc(1024 * sizeof(SInt16));
}
BTW,有没有办法设置audioFormat.mFramesPerPacket> 1?
在我的情况下,如果参数>它会打印错误。 1。我在考虑发送一个包含多帧的缓冲区(for 获取更多时间在远程端播放),它应该比 为VOIP发送一帧一个数据包?
答案 0 :(得分:0)
由于两个设备的音频采样速率时钟不能完美同步,因此必须处理由于轻微的采样率不匹配导致的缓冲器下溢和溢出,以及网络延迟抖动。
另请注意,发送到RemoteIO回调的缓冲区大小可能不会保持不变,因此两个回调必须能够处理缓冲区大小不匹配。
答案 1 :(得分:0)
我刚刚解决了这个问题!
需要设置音频会话的属性,确保两个设备具有相同的BufferDuration
// set preferred buffer size
Float32 audioBufferSize = (set up the duration);
UInt32 size = sizeof(audioBufferSize);
result = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
size, &audioBufferSize);