Мой первый ответ был недостаточно хорош, поэтому я собрал минимальный пример, который будет воспроизводить 2-канальный 16-битный волновой файл.
Основное отличие от вашего кода в том, что я сделал прослушиватель свойств, прослушивающий события запуска и остановки воспроизведения.
Что касается вашего кода, то на первый взгляд он кажется законным. Однако я отмечу две вещи: 1. Кажется, вы выделяете буферы со СЛИШКОМ МАЛЕНЬКИМ размером буфера. Я заметил, что AudioQueues не будет воспроизводиться, если буферы слишком малы, что, кажется, соответствует вашей проблеме. 2. Вы проверили возвращенные свойства?
Вернемся к моему примеру кода:
Все жестко запрограммировано, так что это не совсем хорошая практика кодирования, но она показывает, как вы можете это сделать.
AudioStreamTest.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
uint32_t bufferSizeInSamples;
AudioFileID file;
UInt32 currentPacket;
AudioQueueRef audioQueue;
AudioQueueBufferRef buffer[3];
AudioStreamBasicDescription audioStreamBasicDescription;
@interface AudioStreamTest : NSObject
- (void)start;
- (void)stop;
@end
AudioStreamTest.m
#import "AudioStreamTest.h"
@implementation AudioStreamTest
- (id)init
{
self = [super init];
if (self) {
bufferSizeInSamples = 441;
file = NULL;
currentPacket = 0;
audioStreamBasicDescription.mBitsPerChannel = 16;
audioStreamBasicDescription.mBytesPerFrame = 4;
audioStreamBasicDescription.mBytesPerPacket = 4;
audioStreamBasicDescription.mChannelsPerFrame = 2;
audioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
audioStreamBasicDescription.mFramesPerPacket = 1;
audioStreamBasicDescription.mReserved = 0;
audioStreamBasicDescription.mSampleRate = 44100;
}
return self;
}
- (void)start {
AudioQueueNewOutput(&audioStreamBasicDescription, AudioEngineOutputBufferCallback, (__bridge void *)(self), NULL, NULL, 0, &audioQueue);
AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioEnginePropertyListenerProc, NULL);
AudioQueueStart(audioQueue, NULL);
}
- (void)stop {
AudioQueueStop(audioQueue, YES);
AudioQueueRemovePropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioEnginePropertyListenerProc, NULL);
}
void AudioEngineOutputBufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
if (file == NULL) return;
UInt32 bytesRead = bufferSizeInSamples * 4;
UInt32 packetsRead = bufferSizeInSamples;
AudioFileReadPacketData(file, false, &bytesRead, NULL, currentPacket, &packetsRead, inBuffer->mAudioData);
inBuffer->mAudioDataByteSize = bytesRead;
currentPacket += packetsRead;
if (bytesRead == 0) {
AudioQueueStop(inAQ, false);
}
else {
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}
}
void AudioEnginePropertyListenerProc (void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID) {
//We are only interested in the property kAudioQueueProperty_IsRunning
if (inID != kAudioQueueProperty_IsRunning) return;
//Get the status of the property
UInt32 isRunning = false;
UInt32 size = sizeof(isRunning);
AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &isRunning, &size);
if (isRunning) {
currentPacket = 0;
NSString *fileName = @"/Users/roy/Documents/XCodeProjectsData/FUZZ/03.wav";
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: fileName];
AudioFileOpenURL((__bridge CFURLRef) fileURL, kAudioFileReadPermission, 0, &file);
for (int i = 0; i < 3; i++){
AudioQueueAllocateBuffer(audioQueue, bufferSizeInSamples * 4, &buffer[i]);
UInt32 bytesRead = bufferSizeInSamples * 4;
UInt32 packetsRead = bufferSizeInSamples;
AudioFileReadPacketData(file, false, &bytesRead, NULL, currentPacket, &packetsRead, buffer[i]->mAudioData);
buffer[i]->mAudioDataByteSize = bytesRead;
currentPacket += packetsRead;
AudioQueueEnqueueBuffer(audioQueue, buffer[i], 0, NULL);
}
}
else {
if (file != NULL) {
AudioFileClose(file);
file = NULL;
for (int i = 0; i < 3; i++) {
AudioQueueFreeBuffer(audioQueue, buffer[i]);
buffer[i] = NULL;
}
}
}
}
-(void)dealloc {
[super dealloc];
AudioQueueDispose(audioQueue, true);
audioQueue = NULL;
}
@end
Наконец, я хочу включить некоторые исследования, которые я провел сегодня, чтобы проверить надежность AudioQueues.
Я заметил, что если вы сделаете слишком маленькие буферы AudioQueue, он вообще не будет воспроизводиться. Это заставило меня немного поиграть, чтобы понять, почему он не играет.
Если я попробую размер буфера, который может содержать только 150 сэмплов, я вообще не получу звука.
Если я попробую размер буфера, который может содержать 175 семплов, он проиграет всю песню, но с большим количеством искажений. 175 - это чуть меньше 4 мс звука.
AudioQueue продолжает запрашивать новые буферы, пока вы продолжаете предоставлять буферы. Это не зависит от того, воспроизводит ли AudioQueue ваши буферы или нет.
Если вы предоставите буфер с размером 0, буфер будет потерян, и для этого запроса постановки в очередь будет возвращена ошибка kAudioQueueErr_BufferEmpty. Вы никогда не увидите, чтобы AudioQueue снова попросил вас заполнить этот буфер. Если это произошло для последней отправленной вами очереди, AudioQueue перестанет просить вас заполнить дополнительные буферы. В этом случае вы больше не услышите звук для этого сеанса.
Чтобы понять, почему AudioQueues ничего не воспроизводит с меньшими размерами буфера, я провел тест, чтобы увидеть, вызывается ли мой обратный вызов вообще, даже когда нет звука. Ответ заключается в том, что буферы вызываются все время, пока AudioQueues воспроизводится и ему нужны данные.
Так что, если вы продолжаете подавать буферы в очередь, буфер никогда не теряется. Не бывает. Если, конечно, не будет ошибки.
Так почему не воспроизводится звук?
Я проверил, вернул ли AudioQueueEnqueueBuffer () какие-либо ошибки. Это не так. Никаких других ошибок в моей игровой программе тоже нет. Данные, полученные при чтении из файла, тоже хороши.
Все нормально, буферы хорошие, re-enqueue данных хороший, звука просто нет.
Итак, мой последний тест заключался в том, чтобы медленно увеличивать размер буфера, пока я ничего не услышу. Наконец я услышал слабые и спорадические искажения.
Потом до меня дошло ...
Похоже, что проблема заключается в том, что система пытается синхронизировать поток со временем, поэтому, если вы ставите звук в очередь, и время для звука, которое вы хотели воспроизвести, прошло, она просто пропустит эту часть буфера. Если размер буфера становится слишком маленьким, все больше и больше данных теряется или пропускается, пока аудиосистема снова не синхронизируется. Этого никогда не бывает, если размер буфера слишком мал. (Вы можете услышать это как искажение, если вы выбрали размер буфера, едва достаточный для поддержки непрерывного воспроизведения.)
Если вы думаете об этом, это единственный способ работы аудио-очереди, но это хорошая реализация, когда вы невежественны, как я, и «открываете», как это работает на самом деле.
person
RoyGal
schedule
01.12.2013