-1

我只是尝试开发一个VOIP应用程序,

  • 从 RecordingCallBack 获取的音频缓冲区将被包装到 NSData,然后由 GCDAsyncSocket 发送到远程端

  • 并且远程端将获取 NSData,解包到音频
    缓冲区,然后 PlayingCallBack 将获取音频缓冲区。

我的计划到目前为止工作正常,在本地运行良好(套接字将数据发送到本地,并在本地播放缓冲区)

但是当它在两台设备(一台真正的 iphone-4s,一台模拟器)上运行时,声音会变得陌生,听起来像机器人的声音

有没有办法避免机器人音效?

这是我的音频单元设置:

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

顺便说一句,有没有办法设置 audioFormat.mFramesPerPacket > 1 ?

就我而言,如果参数 > 1,它将打印错误。

我正在考虑发送一个包含多帧的缓冲区(以获取更多时间在远程端播放),它应该比为 VOIP 发送一个帧一个数据包更好?

4

2 回答 2

0

由于两个设备的音频采样率时钟不会完全同步,因此您将不得不处理由于轻微的采样率不匹配以及网络延迟抖动而导致的缓冲区下溢和溢出。

另请注意,发送到 RemoteIO 回调的缓冲区大小可能不会保持不变,因此两个回调必须能够处理缓冲区大小不匹配的问题。

于 2012-11-23T07:49:32.230 回答
0

我现在刚刚解决了这个问题!

需要设置音频会话的属性,确保两个设备具有相同的 BufferDuration

    // set preferred buffer size
    Float32 audioBufferSize = (set up the duration);
    UInt32 size = sizeof(audioBufferSize);
    result = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
                           size, &audioBufferSize);
于 2012-11-26T16:24:37.847 回答