첫 번째 대답이 좋지 않아서 2 채널, 16 비트 웨이브 파일을 재생하는 최소한의 예를 작성했습니다.
코드와의 가장 큰 차이점은 재생 시작 및 중지 이벤트를 수신하는 속성 수신기를 만들었습니다.
코드에 대해서는 처음에는 합법적 인 것으로 보입니다. 두 가지, 내가 지적 할 것입니다 : 1. 버퍼 크기가 TOO SMALL 인 버퍼를 할당하는 것 같습니다. 버퍼가 너무 작 으면 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 ms 미만의 오디오에 해당합니다.
AudioQueue는 버퍼를 계속 제공하는 한 새로운 버퍼를 요구합니다. 그건 실제로 당신의 버퍼를 재생하거나하지 AudioQueue에 관계없이입니다.
크기가 0 인 버퍼를 제공하면 버퍼가 손실되고 해당 대기열 대기열 요청에 대해 kAudioQueueErr_BufferEmpty 오류가 반환됩니다. AudioQueue가 버퍼를 다시 채우라고 요청하는 것을 결코 보지 못할 것입니다. 마지막으로 게시 한 대기열에 문제가 발생하면 AudioQueue에서 더 이상 버퍼를 채우지 못하게됩니다. 이 경우 해당 세션에 대한 오디오가 더 이상 들리지 않습니다.
AudioQueues가 더 작은 버퍼 크기로 어떤 것을 재생하지 않는지 확인하려면 소리가 들리지 않아도 콜백이 호출되는지 테스트 해 보았습니다. 대답은 AudioQueues가 재생되고 데이터가 필요한 한 버퍼가 항상 호출된다는 것입니다.
버퍼에 큐를 계속 공급하면 버퍼가 손실되지 않습니다. 그것은 일어나지 않습니다. 물론 오류가없는 한.
왜 사운드가 재생되지 않습니까?
'AudioQueueEnqueueBuffer()'에서 오류가 반환되었는지 테스트했습니다. 그렇지 않았습니다. 내 놀이 루틴 중 다른 오류도 없습니다. 파일에서 읽기에서 반환 된 데이터도 좋습니다.
모든 것이 정상이며 버퍼가 좋으며 데이터를 다시 대기열에 포함하는 것이 좋으며 소리가 들리지 않습니다.
그래서 마지막 테스트는 내가 들릴 때까지 버퍼 크기를 천천히 늘리는 것이 었습니다. 나는 희미하고 산발적 인 왜곡을 마침내 들었다.
는 그럼 ... 나에게 온
이 문제가 시스템이 오디오를 대기열 그래서 만약 시간과 동기화 스트림을 유지하려고 그와 함께 거짓말을 것, 그리고 당신이 원하는 오디오의 시간 재생이 완료되면 버퍼의 해당 부분을 건너 뜁니다. 버퍼 크기가 너무 작아지면 오디오 시스템이 다시 동기화 될 때까지 점점 더 많은 데이터가 삭제되거나 건너 뜁니다. 버퍼 크기가 너무 작 으면 절대로 없습니다. (연속 재생을 지원할만큼 충분히 작은 버퍼 크기를 선택한 경우 왜곡 된 것으로 들릴 수 있습니다.)
생각해 보면 오디오 대기열이 작동하는 유일한 방법이지만 좋은 방법입니다 당신이 내게 우둔하고 실제로 어떻게 작동하는지 "발견"할 때 실현.
왜 작동하지 않는지 나는 모른다. 하지만 AudioUnit을 왜 사용하지 않습니까? –
@AliaksandrAndrashuk AudioUnits는 효과가 있습니까? 파일에서 스트리밍하는 데이 파일을 사용하는 것이 합리적입니까? – Radiodef
예. AUFilePlayer -> RemoteIO (AUGraph)를 설정하여 파일을 재생할 수 있습니다. –