В приложении iOS
Воспроизведение аудиопотока из BLE с помощью TPCircularBuffer
и Audio Unit.
Звук воспроизводится хорошо, но когда буфер пуст и нет байтов для воспроизведения, это, например, вызывает потрескивание.
Вот моя конфигурация аудиопотока,
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 8000.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsPacked;
// || kAudioFormatFlagsNativeFloatPacked || kAudioFormatFlagIsSignedInteger
//kAudioFormatFlagIsSignedInteger
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 32;// Update when required
audioFormat.mBytesPerPacket = 4; // Update when required
audioFormat.mBytesPerFrame = 4; // Update when required
Ниже представлена функция воспроизведения аудиоустройства.
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
//1
for (int i=0; i < ioData->mNumberBuffers; i++) {
IosAudioController *THIS = (__bridge IosAudioController *)inRefCon;
int bytesAskingByPlayback = ioData->mBuffers[i].mDataByteSize;
SInt16 *targetBuffer = (SInt16*)ioData->mBuffers[i].mData;
// Pull audio from playthrough buffer
int32_t availableBytesFromBuffer;
SInt16 *sBuffer = TPCircularBufferTail(iosAudio.addressOfTPBuffer, &availableBytesFromBuffer);
int willRemainBytes = availableBytesFromBuffer - bytesAskingByPlayback;
if (willRemainBytes > 0) {
memcpy(targetBuffer, sBuffer, bytesAskingByPlayback);
TPCircularBufferConsume(iosAudio.addressOfTPBuffer,bytesAskingByPlayback);
} else {
//Note: Mostly need to update code here
memcpy(targetBuffer, sBuffer, availableBytesFromBuffer);
TPCircularBufferConsume(iosAudio.addressOfTPBuffer, availableBytesFromBuffer);
}
}
return noErr;
}
Размер буфера 16384.
В каком-то решении говорилось, что я заполню целевой буфер нулями, чтобы заставить замолчать, но это не работает.
В некоторых решениях говорится, что я мог бы заполнить целевой буфер предыдущими значениями, чтобы заполнить пробел.
но когда буфер пуст и нет байтов для воспроизведения.
Вам нужно начать здесь и определить, почему это происходит. В идеале этого никогда не должно произойти. Если это и случается, то должно быть редко и по четкой причине. Если восходящий поток приостановился, то вам нужно приостановить и нисходящий. Если есть задержка в сети, вам необходимо увеличить размер буфера.
Каждый раз, когда вы вводите постоянное значение, будь то ноль или последнее полученное значение, вы будете вводить высокочастотный шум. Это будет звучать как небольшой хлопок. Если делать это часто, то он будет хрустеть. Существуют методы сглаживания этой проблемы, но они довольно сложны, и вам не следует обращаться к ним, прежде чем устранять основную проблему недозапуска.
Если ваш восходящий поток имеет переменную задержку, вам нужно будет немного буферизовать (10 мс, 50 мс, 2000 мс, это зависит от того, насколько он переменный), прежде чем начать нисходящий поток. Если ваши буферы исчерпаны, вам придется приостановить работу нисходящего потока, пока вы не сможете снова создать буфер.
Иногда стоит время от времени «всплывать», чтобы не приостанавливать нисходящий поток, и именно тогда приходят советы типа «заполнить нулями» или «заполнить последним значением». Но если вы получаете всплывающие сообщения много раз в секунду (и это то, что обычно это «треск»), это, скорее всего, означает, что вы недостаточно буферизуете перед началом нисходящего потока.
В зависимости от характера вашего аудио вам также необходимо учитывать случаи, когда в восходящем потоке происходит длительная пауза, а затем вы внезапно получаете много прошлых данных. На этом этапе вы должны решить, оставить ли его и увеличить задержку или отказаться от него, чтобы приблизиться к «реальному времени». Стратегии для этого полностью зависят от вашего варианта использования и являются важной частью проектирования любой системы реального времени.
Обратите внимание, что TPCircularBuffer и Audio Unit являются инструментами довольно низкого уровня и возлагают на вас большую часть работы. Лично я предпочитаю создавать подобные системы с помощью инструмента более высокого уровня, такого как AVSampleBuffer. Это по-прежнему сложно, и вам необходимо разбираться в системах реального времени, но AVFoundation сделает за вас гораздо больше работы. (Лично мне часто приходится беспокоиться о таких вещах, как пауза, перемотка назад, пропуск и тому подобное, поэтому моя проблема может существенно отличаться от вашей, поэтому этот совет неприменим.)