我的第一个答案不够好,所以我编了一个最小的例子,播放一个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()'是否返回了任何错误。它没。我的播放例程中也没有其他错误。从文件读取返回的数据也很好。
一切都很正常,缓冲区很好,数据重新排队很好,只是没有声音。
所以我最后的测试是缓慢增加缓冲区大小,直到我能听到任何东西。我终于听到了微弱而零星的扭曲。
然后向我走来......
看来,问题在于,该系统试图保持流中同步与时间,所以如果你排队的音频,并时刻为您想的声音游戏已经过去了,它只会跳过缓冲区的那一部分。如果缓冲区大小变得太小,则越来越多的数据被丢弃或跳过,直到音频系统再次同步。如果缓冲区大小太小,那永远不会。 (如果您选择的缓冲区大小足以支持连续播放,您可以听到这种失真。)
如果您仔细考虑它,它是音频队列工作的唯一途径,但它是一种很好的当你像我一样无知,并“发现”它是如何工作的时候就会意识到这一点。
我不知道为什么它不起作用。但是,为什么你不使用AudioUnit? –
@AliaksandrAndrashuk AudioUnit不仅仅是为了效果吗?将它们用于从文件流式传输是否合理? – Radiodef
是的。您可以设置AUFilePlayer - > RemoteIO(在AUGraph中)播放文件。 –