Speex кодирование / декодирование, вызывающее шипение (Objective-c)
Когда я пропускаю шаги кодирования / декодирования speex, исходный аудио вывод правильный. Я хотел бы, чтобы весь буфер, захваченный из моего обратного вызова записи, был закодирован, декодирован и отправлен обратно в цикл воспроизведения. Несколько вещей, в которых я не уверен:
- Какой размер выделить для enc_buffer и dec_buffer
- Какую длину указывать в speex_bits_read_from(SpeexBits* bits,char* bytes,int len)
- Какой максимальный размер указывать в int speex_bits_write(SpeexBits* bits,char* bytes,int max_len)
Вот моя инициализация кодека 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" - это то, что возвращено из моих обратных вызовов записи / воспроизведения. Может ли кто-нибудь проверить, правильно ли я заполняю буфер? Я видел подобную проблему, о которой сообщалось здесь, но не мог видеть, где в моем коде я мог делать это неправильно:
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 ответа
Так что это была проблема в функциях кодирования / декодирования, мне нужно было вызвать 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_from закодированного буфера, а затем итерации по 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 для использования вычислений с фиксированной запятой вместо вычислений с плавающей запятой она все равно работает медленнее, чем мой аудио цикл (вызывая новый вид прерывистости). Есть ли какие-либо указания о том, как заставить это работать быстрее?
В обоих ваших циклах вы передаете аудио-буфер, но не принимаете во внимание размер кадра:
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);
}
надеюсь, это поможет.