Speex编码/解码导致嘶嘶声(Objective-c)

时间:2013-01-23 21:32:22

标签: objective-c encoding decoding audiotoolbox speex

当我绕过speex编码/解码步骤时,原始音频输出是正确的。我想要的是从我的录制回调中捕获的整个缓冲区被编码,解码并发送回回放循环。我不确定的几个项目是:

  1. 为enc_buffer和dec_buffer分配的大小
  2. 在speex_bits_read_from中指定的长度(SpeexBits * bits,char * bytes,int len)
  3. 在int speex_bits_write中指定的最大大小(SpeexBits * bits,char * bytes,int max_len)
  4. 这是我的speex编解码器初始化:

    #define SAMPLE_RATE 8000
    #define MAX_FRAMES 100
    #define FRAME_SIZE 160
    
    enc_state = speex_encoder_init(&speex_nb_mode);
    dec_state = speex_decoder_init(&speex_nb_mode);
    
    spx_int32_t tmp;
    tmp=5;
    speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &tmp);
    tmp=1;
    speex_encoder_ctl(enc_state, SPEEX_SET_COMPLEXITY, &tmp);
    
    speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size );
    speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size );
    
    tmp = SAMPLE_RATE;
    
    speex_encoder_ctl(enc_state, SPEEX_SET_SAMPLING_RATE, &tmp);
    speex_decoder_ctl(dec_state, SPEEX_SET_SAMPLING_RATE, &tmp);
    
    speex_bits_init(&enc_bits);
    speex_bits_init(&dec_bits);
    
    //Unsure of this allocation size
    enc_buffer = (char*)malloc(sizeof(char)*enc_frame_size*MAX_FRAMES);
    dec_buffer = (spx_int16_t*)malloc(sizeof(spx_int16_t)*dec_frame_size*MAX_FRAMES);
    

    我的编码/解码方法:

    -(char*)encodeAudioBuffer:(spx_int16_t*)audioBuffer withByteSize:(int)numberOfFrames andWriteSizeTo:(int*)inSize{
        speex_bits_reset(&enc_bits);
        speex_encode_int(enc_state, audioBuffer, &enc_bits);
    
        //Unsure of this third argument. 'numberOfFrames' is the stored number of input frames from my recording callback.
        *inSize = speex_bits_write(&enc_bits, enc_buffer, numberOfFrames*enc_frame_size);
    
        return enc_buffer;
    }
    -(spx_int16_t*)decodeSpeexBits:(char*)encodedAudio  withEncodedSize:(int)encodedSize andDecodedSize:(int)decodedSize{
    
        //Unsure of this third argument.  'encodedSize' is the number written to *inSize in the encode method
        speex_bits_read_from(&dec_bits, encodedAudio, encodedSize*dec_frame_size);
    
        speex_decode_int(dec_state, &dec_bits, dec_buffer);
        return dec_buffer;
    }
    

    他们被这样称呼:

    - (void)encodeBufferList:(AudioBufferList*)bufferList withNumberOfFrames:(int)numberOfFrames{
        AudioBuffer sourceBuffer = bufferList->mBuffers[0];
        int speexSize = 0;
        char* encodedAudio = [speexCodec encodeAudioBuffer:(spx_int16_t*)sourceBuffer.mData withByteSize:numberOfFrames andWriteSizeTo:&speexSize];
        spx_int16_t* decodedAudio = [speexCodec decodeSpeexBits:encodedAudio withEncodedSize:speexSize andDecodedSize:sourceBuffer.mDataByteSize];
        memcpy(audioBuffer.mData, sourceBuffer.mData, numberOfFrames * sizeof(SInt32));
    }
    

    其中“bufferList”是从我的录制/回放回调中返回的。有人可以验证我正在填充我的缓冲区吗?我看到一个类似的问题报告here,但无法看到我的代码中我可能做错了:

    static OSStatus recordingCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
    {
        AudioBuffer buffer;
        OSStatus status;
        AudioStreamer *input = (__bridge AudioStreamer*) inRefCon;
    
        buffer.mDataByteSize = inNumberFrames * sizeof(SInt16);
        buffer.mNumberChannels = 1;
        buffer.mData = malloc( inNumberFrames * sizeof(SInt16));
    
        AudioBufferList bufferList;
        bufferList.mNumberBuffers = 1;
        bufferList.mBuffers[0] = buffer;
    
        status = AudioUnitRender([input rioAUInstance], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
        [input encodeBufferList:&bufferList withNumberOfFrames:inNumberFrames];
        return noErr;
    }
    
    static OSStatus playbackCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
    {
        AudioStreamer* input = (__bridge AudioStreamer*)inRefCon;
        UInt32 size = MIN(ioData->mBuffers[0].mDataByteSize, [input audioBuffer].mDataByteSize);
        memcpy(ioData->mBuffers[0].mData, input.audioBuffer.mData, size);
        return noErr;
    }
    

    编码/解码所产生的噪音是一种颗粒状的静态嘶嘶声,但它不是完全随机的信息 - 当我吹进麦克风时,我可以在噪音后面听到它。

    任何将这个问题放到床上的帮助将不胜感激。一旦我解决了所有事情,我可能最终会写博客,似乎很多人都遇到了设置这个编解码器的各种琐碎问题。

2 个答案:

答案 0 :(得分:1)

所以这是编码/解码函数中的问题,我需要在多个帧中调用speex_encode_int,因为它似乎一次只能处理1帧,然后将它们写入编码缓冲区,如下所示:

-(char*)encodeAudioBuffer:(spx_int16_t*)audioBuffer withNumberOfFrames:(int)numberOfFrames andWriteSizeTo:(int*)inSize{
    speex_bits_reset(&enc_bits);
    for(int i = 0; i < numberOfFrames; ++i){
        speex_encode_int(enc_state, audioBuffer+i, &enc_bits);
    }
    *inSize = speex_bits_write(&enc_bits, enc_buffer, numberOfFrames);
    return enc_buffer;
}

类似地,对于解码,speex_bits_read_来自编码缓冲区,然后遍历每个帧的dec_bits,写入解码缓冲区

-(spx_int16_t*)decodeSpeexBits:(char*)encodedAudio  withEncodedSize:(int)encodedSize andNumberOfFrames:(int)numberOfFrames{
    speex_bits_read_from(&dec_bits, encodedAudio, encodedSize);
    for(int i = 0; i < numberOfFrames; ++i){
        speex_decode_int(dec_state, &dec_bits, dec_buffer+i);
    }
    return dec_buffer;
}

这对我来说仍然很慢。即使在配置speex库以使用定点计算而不是浮点计算之后,它仍然比我的音频循环运行得慢(导致一种新的波动)。有关如何让这种运行更快的任何线索?

答案 1 :(得分:0)

在你的两个循环中,你正在传递音频缓冲区,但没有考虑帧大小:

for(int i = 0; i < numberOfFrames; ++i){
    speex_encode_int(enc_state, audioBuffer+i, &enc_bits);
}

应该是:

for(int i = 0; i < numberOfFrames; ++i){
    speex_encode_int(enc_state, audioBuffer + (i * enc_frame_size), &enc_bits);
}
希望有所帮助。