Moja pierwsza odpowiedź nie była wystarczająco dobra, więc skompilowałem minimalny przykład, który będzie odtwarzał 2-kanałowy, 16-bitowy plik wave.
Główna różnica w stosunku do kodu polega na tym, że wykonałem odsłuchiwanie obiektu dla rozpoczęcia i zakończenia odtwarzania.
Co do kodu, na pierwszy rzut oka wydaje się być uzasadniony. Chciałbym jednak zwrócić uwagę na dwie rzeczy: 1. Wygląda na to, że alokujesz bufory o zbyt małym rozmiarze bufora. Zauważyłem, że AudioQueues nie będą odtwarzane, jeśli bufory są zbyt małe, co wydaje się pasować do twojego problemu. 2. Czy zweryfikowaliście zwrócone właściwości?
Powrót na moim przykładzie kodu:
Wszystko jest trudne kodowane, więc to nie jest dokładnie to dobra praktyka, kodowanie, ale to pokazuje, w jaki sposób można to zrobić.
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
Wreszcie, chciałbym to rozeznanie zrobiłem dzisiaj przetestować odporność AudioQueues.
Zauważyłem, że jeśli zrobisz zbyt małe bufory AudioQueue, to nie będzie ono w ogóle grać. To sprawiło, że trochę się pobawiłem, żeby zobaczyć, dlaczego nie gra.
Jeśli spróbuję rozmiaru bufora, który może pomieścić tylko 150 próbek, nie otrzymam żadnego dźwięku.
Jeśli spróbuję rozmiaru bufora, który może pomieścić 175 próbek, odtwarza całą piosenkę, ale z Dużym zniekształceniem. 175 oznacza odrobinę mniej niż 4 ms dźwięku.
AudioQueue ciągle prosi o nowe bufory, o ile nadal dostarczasz bufory. Niezależnie od tego, czy AudioQueue rzeczywiście odtwarza bufory, czy nie.
Jeśli podasz bufor o rozmiarze 0, bufor zostanie utracony i zostanie zwrócony błąd kAudioQueueErr_BufferEmpty dla tego żądania kolejkowania kolejek. Nigdy nie zobaczysz, że AudioQueue poprosi Cię o ponowne wypełnienie tego bufora. Jeśli tak się stało w przypadku ostatniej opublikowanej kolejki, AudioQueue przestanie prosić o wypełnienie kolejnych buforów. W takim przypadku nie będzie słychać więcej dźwięku dla tej sesji.
Aby zobaczyć, dlaczego AudioQueues nie odtwarza niczego z mniejszymi rozmiarami bufora, wykonałem test, aby sprawdzić, czy mój oddzwaniany jest w ogóle wywoływany, nawet gdy nie ma dźwięku. Odpowiedź brzmi, że bufory są wywoływane przez cały czas, dopóki AudioQueues gra i potrzebuje danych.
Więc jeśli nadal ładujesz bufory do kolejki, żaden bufor nie zostanie utracony. To się nie zdarza. O ile oczywiście nie ma błędu.
Dlaczego więc nie słychać dźwięku?
Przetestowałem, czy "AudioQueueEnqueueBuffer()" zwrócił żadnych błędów. To nie mialo miejsca. Żadnych innych błędów w mojej rutynie gry. Dane zwrócone z odczytu z pliku również są dobre.
Wszystko jest w normie, bufory są dobre, dane ponownie zgromadzone są dobre, po prostu nie ma dźwięku.
Tak więc moim ostatnim testem było powolne zwiększanie rozmiaru bufora, dopóki nie usłyszałem niczego. W końcu usłyszałem słabe i sporadyczne zniekształcenie.
Potem przyszedł do mnie ...
Wydaje się, że problem leży że system stara się utrzymać strumień zsynchronizowane z czasem więc jeśli enqueue dźwięku, a czas dla audio chciałeś gra minęła, po prostu pominie tę część bufora. Jeśli rozmiar bufora staje się zbyt mały, coraz więcej danych jest pomijanych lub pomijanych, aż system audio zsynchronizuje się ponownie. Które nigdy nie jest, jeśli rozmiar bufora jest zbyt mały. (Możesz usłyszeć to jako zniekształcenie, jeśli wybierzesz rozmiar bufora, który jest wystarczająco duży, aby obsługiwać ciągłe odtwarzanie.)
Jeśli o tym pomyślisz, jest to jedyny sposób, w jaki kolejka audio może działać, ale jest to dobre uświadomienie sobie, kiedy jesteś nieświadomy jak ja i "odkryć", jak to naprawdę działa.
Nie mam pojęcia, dlaczego to nie działa. Ale dlaczego nie używasz AudioUnit? –
@AleksandrAndrashuk Czy AudioUnits to nie tylko efekty? Czy rozsądnie jest używać ich do przesyłania strumieniowego z pliku? – Radiodef
tak. Możesz ustawić AUFilePlayer -> RemoteIO (w AUGraph), aby odtworzyć plik. –