我试图从文件功能中进行简单的回放,而我的回调函数似乎从未被调用过。这是没有意义的,因为所有的OSStatuses都返回0,其他数字也都是正确的(比如输出数据包从AudioFileReadPackets读取指针)。
下面是设置:
OSStatus stat;
stat = AudioFileOpenURL(
(CFURLRef)urlpath, kAudioFileReadPermission, 0, &aStreamData->aFile
);
UInt32 dsze = 0;
stat = AudioFileGetPropertyInfo(
aStreamData->aFile, kAudioFilePropertyDataFormat, &dsze, 0
);
stat = AudioFileGetProperty(
aStreamData->aFile, kAudioFilePropertyDataFormat, &dsze, &aStreamData->aDescription
);
stat = AudioQueueNewOutput(
&aStreamData->aDescription, bufferCallback, aStreamData, NULL, NULL, 0, &aStreamData->aQueue
);
aStreamData->pOffset = 0;
for(int i = 0; i < NUM_BUFFERS; i++) {
stat = AudioQueueAllocateBuffer(
aStreamData->aQueue, aStreamData->aDescription.mBytesPerPacket, &aStreamData->aBuffer[i]
);
bufferCallback(aStreamData, aStreamData->aQueue, aStreamData->aBuffer[i]);
}
stat = AudioQueuePrime(aStreamData->aQueue, 0, NULL);
stat = AudioQueueStart(aStreamData->aQueue, NULL);(未显示的是我在函数之间检查stat值的位置,它只是恢复正常。)
以及回调函数:
void bufferCallback(void *uData, AudioQueueRef queue, AudioQueueBufferRef buffer) {
UInt32 bread = 0;
UInt32 pread = buffer->mAudioDataBytesCapacity / player->aStreamData->aDescription.mBytesPerPacket;
OSStatus stat;
stat = AudioFileReadPackets(
player->aStreamData->aFile, false, &bread, NULL, player->aStreamData->pOffset, &pread, buffer->mAudioData
);
buffer->mAudioDataByteSize = bread;
stat = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
player->aStreamData->pOffset += pread;
}其中,aStreamData是我的用户数据结构(输入是为了将它作为类属性使用),player是控制对象-C类的静态实例。如果需要其他代码,请告诉我。我的智慧已到了极限。这里所涉及的任何数字都会产生正确的结果,包括在分配循环中自己调用bufferCallback中的函数。从那以后就再也没打过电话了。启动方法会返回,什么都不会发生。
同样有趣的是,我正在使用一个外围设备(一个MBox Pro 3)来播放CoreAudio只有在它即将输出时才启动的声音。如果我启动iTunes或其他什么的话,扬声器会微弱地弹出,有一个从闪烁到固态的发光二极管。设备像它一样启动,所以CA肯定在做一些事情。(当然,我也尝试过用车载Macbook听起来没有这种设备。)
我读过其他类似问题的解决方案,但它们不起作用。像使用多个缓冲区这样的事情,我现在正在做,似乎没有任何区别。
我基本上假设我在做一些明显的错误,但我不知道它会是什么。我已经阅读了相关的文档,查看了可用的代码示例,并在网络中搜索了一些答案,这似乎是我所需要做的,应该就这么做了。
至少,我还能做些什么来调查吗?
发布于 2013-12-01 10:17:09
我的第一个答案不够好,所以我编写了一个最小的例子,将播放一个2通道,16位的波形文件。
与您的代码主要不同的是,我制作了一个属性监听器,侦听播放、开始和停止事件。
至于您的代码,乍一看似乎是合法的。但是,我要指出的两件事是: 1. Is似乎是在分配缓冲区大小太小的缓冲区。我已经注意到,如果缓冲区太小,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;
@endAudioStreamTest.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()‘是否返回了任何错误。但事实并非如此。在我的演奏程序中也没有其他错误。从文件中读取返回的数据也是好的。
一切正常,缓冲区很好,数据重新排队很好,只是没有声音。
所以我的最后一个测试是慢慢增加缓冲区大小,直到我听到任何声音。我终于听到了微弱和零星的扭曲。
然后我想到了..。
问题似乎在于系统试图使流与时间保持同步,因此如果您登记了音频队列,并且您想播放的音频的时间已经过去,它将跳过缓冲区的这一部分。如果缓冲区大小变得太小,会有越来越多的数据被删除或跳过,直到音频系统再次同步。如果缓冲区大小太小,则永远不会。(如果您选择的缓冲区大小几乎不足以支持连续播放,则可以将其视为失真。)
如果你想一想,这是唯一的方式音频队列可以工作,但这是一个很好的认识,当你是无知的像我,并“发现”它是如何真正的工作。
发布于 2014-02-14 00:57:59
我决定再看一遍,然后通过增加缓冲区来解决这个问题。我已经接受了@RoyGal的回答,因为这是他们的建议,但我想提供实际的代码,因为我想其他人也有同样的问题(问题有几个目前不是我的最爱)。
我试过的一件事是把包变大:
aData->aDescription.mFramesPerPacket = 512; // or some other number
aData->aDescription.mBytesPerPacket = (
aData->aDescription.mFramesPerPacket * aData->aDescription.mBytesPerFrame
);这不起作用:它会导致AudioQueuePrime与AudioConverterNew returned -50消息一起失败。我想它想让mFramesPerPacket成为PCM的1。
(我还尝试设置kAudioQueueProperty_DecodeBufferSizeFrames属性,该属性似乎没有做任何事情。不知道是做什么用的。)
解决方案似乎是只分配具有指定大小的缓冲区:
AudioQueueAllocateBuffer(
aData->aQueue,
aData->aDescription.mBytesPerPacket * N_BUFFER_PACKETS / N_BUFFERS,
&aData->aBuffer[i]
);尺寸必须足够大。我发现神奇的数字是:
mBytesPerPacket * 1024 / N_BUFFERS(其中N_BUFFERS是缓冲区的数量,应该是> 1,或者回放是起伏不定的。)
这里有一个MCVE演示了这个问题和解决方案:
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#define N_BUFFERS 2
#define N_BUFFER_PACKETS 1024
typedef struct AStreamData {
AudioFileID aFile;
AudioQueueRef aQueue;
AudioQueueBufferRef aBuffer[N_BUFFERS];
AudioStreamBasicDescription aDescription;
SInt64 pOffset;
volatile BOOL isRunning;
} AStreamData;
void printASBD(AudioStreamBasicDescription* desc) {
printf("mSampleRate = %d\n", (int)desc->mSampleRate);
printf("mBytesPerPacket = %d\n", desc->mBytesPerPacket);
printf("mFramesPerPacket = %d\n", desc->mFramesPerPacket);
printf("mBytesPerFrame = %d\n", desc->mBytesPerFrame);
printf("mChannelsPerFrame = %d\n", desc->mChannelsPerFrame);
printf("mBitsPerChannel = %d\n", desc->mBitsPerChannel);
}
void bufferCallback(
void *vData, AudioQueueRef aQueue, AudioQueueBufferRef aBuffer
) {
AStreamData* aData = (AStreamData*)vData;
UInt32 bRead = 0;
UInt32 pRead = (
aBuffer->mAudioDataBytesCapacity / aData->aDescription.mBytesPerPacket
);
OSStatus stat;
stat = AudioFileReadPackets(
aData->aFile, false, &bRead, NULL, aData->pOffset, &pRead, aBuffer->mAudioData
);
if(stat != 0) {
printf("AudioFileReadPackets returned %d\n", stat);
}
if(pRead == 0) {
aData->isRunning = NO;
return;
}
aBuffer->mAudioDataByteSize = bRead;
stat = AudioQueueEnqueueBuffer(aQueue, aBuffer, 0, NULL);
if(stat != 0) {
printf("AudioQueueEnqueueBuffer returned %d\n", stat);
}
aData->pOffset += pRead;
}
AStreamData* beginPlayback(NSURL* path) {
static AStreamData* aData;
aData = malloc(sizeof(AStreamData));
OSStatus stat;
stat = AudioFileOpenURL(
(CFURLRef)path, kAudioFileReadPermission, 0, &aData->aFile
);
printf("AudioFileOpenURL returned %d\n", stat);
UInt32 dSize = 0;
stat = AudioFileGetPropertyInfo(
aData->aFile, kAudioFilePropertyDataFormat, &dSize, 0
);
printf("AudioFileGetPropertyInfo returned %d\n", stat);
stat = AudioFileGetProperty(
aData->aFile, kAudioFilePropertyDataFormat, &dSize, &aData->aDescription
);
printf("AudioFileGetProperty returned %d\n", stat);
printASBD(&aData->aDescription);
stat = AudioQueueNewOutput(
&aData->aDescription, bufferCallback, aData, NULL, NULL, 0, &aData->aQueue
);
printf("AudioQueueNewOutput returned %d\n", stat);
aData->pOffset = 0;
for(int i = 0; i < N_BUFFERS; i++) {
// change YES to NO for stale playback
if(YES) {
stat = AudioQueueAllocateBuffer(
aData->aQueue,
aData->aDescription.mBytesPerPacket * N_BUFFER_PACKETS / N_BUFFERS,
&aData->aBuffer[i]
);
} else {
stat = AudioQueueAllocateBuffer(
aData->aQueue,
aData->aDescription.mBytesPerPacket,
&aData->aBuffer[i]
);
}
printf(
"AudioQueueAllocateBuffer returned %d for aBuffer[%d] with capacity %d\n",
stat, i, aData->aBuffer[i]->mAudioDataBytesCapacity
);
bufferCallback(aData, aData->aQueue, aData->aBuffer[i]);
}
UInt32 numFramesPrepared = 0;
stat = AudioQueuePrime(aData->aQueue, 0, &numFramesPrepared);
printf("AudioQueuePrime returned %d with %d frames prepared\n", stat, numFramesPrepared);
stat = AudioQueueStart(aData->aQueue, NULL);
printf("AudioQueueStart returned %d\n", stat);
UInt32 pSize = sizeof(UInt32);
UInt32 isRunning;
stat = AudioQueueGetProperty(
aData->aQueue, kAudioQueueProperty_IsRunning, &isRunning, &pSize
);
printf("AudioQueueGetProperty returned %d\n", stat);
aData->isRunning = !!isRunning;
return aData;
}
void endPlayback(AStreamData* aData) {
OSStatus stat = AudioQueueStop(aData->aQueue, NO);
printf("AudioQueueStop returned %d\n", stat);
}
NSString* getPath() {
// change NO to YES and enter path to hard code
if(NO) {
return @"";
}
char input[512];
printf("Enter file path: ");
scanf("%[^\n]", input);
return [[NSString alloc] initWithCString:input encoding:NSASCIIStringEncoding];
}
int main(int argc, const char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSURL* path = [NSURL fileURLWithPath:getPath()];
AStreamData* aData = beginPlayback(path);
if(aData->isRunning) {
do {
printf("Queue is running...\n");
[NSThread sleepForTimeInterval:1.0];
} while(aData->isRunning);
endPlayback(aData);
} else {
printf("Playback did not start\n");
}
[pool drain];
return 0;
}https://stackoverflow.com/questions/19434413
复制相似问题