2013-10-17 1 views
12

파일 기능에서 간단한 재생을 시도하고 콜백 함수가 호출되지 않는 것 같습니다. 모든 OSStatuses가 0으로 되돌아오고 다른 숫자가 모두 올바르게 표시되기 때문에 실제로 의미가 없습니다 (AudioFileReadPackets의 출력 패킷 읽기 포인터처럼). 여기 CoreAudio AudioQueue 콜백 함수가 호출되지 않고 오류가보고되지 않았습니다.

는 설정이다 :
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의 값을 확인하고 있습니다 경우 표시되지 않음은, 그냥 정상으로 돌아 온다.)

그리고 콜백 함수를 : aStreamData 내 사용자 데이터 (그래서 typedefed I 클래스 속성으로 사용할 수 있음) 구조체와 플레이어

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; 
} 

은 제어 목표 C-C의 정적 인스턴스 젊은 여자. 다른 코드가 필요하면 알려주십시오. 나는 지혜로 끝이다. 여기에 관련된 숫자를 출력하면 allocate 루프에서 직접 호출 할 때 bufferCallback의 함수를 포함하여 올바른 결과를 얻을 수 있습니다. 그 후에는 절대로 전화를받지 않습니다. 시작 메서드가 반환되고 아무 일도 일어나지 않습니다.

또한 일화에서 주변 장치 (MBox Pro 3)를 사용하여 CoreAudio가 출력 할 때만 부팅되는 사운드를 재생합니다. IE 나 아이튠즈 등을 시작하면 스피커가 희미하게 터지고 깜빡이는 곳에서부터 단단한 곳으로가는 LED가 있습니다. 장치가 부팅되어 CA가 확실히 작업을 수행합니다. (물론 나는 온보드 Macbook 사운드가 장치에 탑재 된 채로 시도해 보았습니다.)

나는 비슷한 것처럼 들리지만 작동하지 않는 다른 해결책을 읽었습니다. 여러 가지 버퍼를 사용하는 것과 같지만 지금하고있는 일과 어떤 차이가없는 것처럼 보입니다.

나는 기본적으로 어떻게 든 분명히 잘못하고 있다고 가정하지만 어떻게 될지 확신 할 수 없습니다. 관련 문서를 읽었고, 사용 가능한 코드 예제를 살펴보고 그물을 조금만 훑어 보았습니다. 이것이 내가해야 할 일이고 그냥 가야하는 것으로 보입니다.

적어도 내가 조사 할 수있는 것이 있습니까?

+0

왜 작동하지 않는지 나는 모른다. 하지만 AudioUnit을 왜 사용하지 않습니까? –

+0

@AliaksandrAndrashuk AudioUnits는 효과가 있습니까? 파일에서 스트리밍하는 데이 파일을 사용하는 것이 합리적입니까? – Radiodef

+0

예. AUFilePlayer -> RemoteIO (AUGraph)를 설정하여 파일을 재생할 수 있습니다. –

답변

2

첫 번째 대답이 좋지 않아서 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()'에서 오류가 반환되었는지 테스트했습니다. 그렇지 않았습니다. 내 놀이 루틴 중 다른 오류도 없습니다. 파일에서 읽기에서 반환 된 데이터도 좋습니다.

모든 것이 정상이며 버퍼가 좋으며 데이터를 다시 대기열에 포함하는 것이 좋으며 소리가 들리지 않습니다.

그래서 마지막 테스트는 내가 들릴 때까지 버퍼 크기를 천천히 늘리는 것이 었습니다. 나는 희미하고 산발적 인 왜곡을 마침내 들었다.

는 그럼 ... 나에게 온

이 문제가 시스템이 오디오를 대기열 그래서 만약 시간과 동기화 스트림을 유지하려고 그와 함께 거짓말을 것, 그리고 당신이 원하는 오디오의 시간 재생이 완료되면 버퍼의 해당 부분을 건너 뜁니다. 버퍼 크기가 너무 작아지면 오디오 시스템이 다시 동기화 될 때까지 점점 더 많은 데이터가 삭제되거나 건너 뜁니다. 버퍼 크기가 너무 작 으면 절대로 없습니다. (연속 재생을 지원할만큼 충분히 작은 버퍼 크기를 선택한 경우 왜곡 된 것으로 들릴 수 있습니다.)

생각해 보면 오디오 대기열이 작동하는 유일한 방법이지만 좋은 방법입니다 당신이 내게 우둔하고 실제로 어떻게 작동하는지 "발견"할 때 실현.

+0

* "버퍼 크기가 너무 작 으면 버퍼를 할당하는 것처럼 보입니다."* AudioFileGetProperty를 채우도록합니다. 예를 들어 Apple 자신의 SpeakHere를 보았습니다. 나는 그것이 왜 문제가되어야하는지 알지 못합니다. 기본적으로 당신은 내가 그것을 바꿔야한다고 생각하니? * "반환 된 속성을 확인 했습니까?"* ASBD는 해당 파일에 대한 올바른 정보를 제공합니다. 그것이 왜곡 된 오디오에 대해 저를 놀리는 것입니다. – Radiodef

+0

정확히 그게 당신 문제라고 말할 수는 없지만 다른 버퍼 크기를 테스트하고 있었고 전혀 소리가 나지 않는 것에서 아주 작은 버퍼로 다양한 정도의 왜곡 된 소리까지, 충분히 큰 버퍼로 오디오를 완벽하게 재생할 수있었습니다. . 다른 모든 것은 정상적으로 보입니다. 이것은 물론 하드웨어에 달려 있습니다. 내 하드웨어가 오래되었고 아마도 최신 Mac보다 더 빨리 작동합니다. – RoyGal

2

다시 한번 살펴보고 버퍼를 크게하여 해결할 수있었습니다. 나는 그들의 제안 이었기 때문에 @RoyGal에 의한 대답을 받아 들였지만 다른 사람들이 같은 문제를 겪고 있기 때문에 실제 코드를 제공하고 싶었다.내가 패킷 크기보다 큰 만들고 있었다 시도

한 가지 :

aData->aDescription.mFramesPerPacket = 512; // or some other number 
aData->aDescription.mBytesPerPacket = (
    aData->aDescription.mFramesPerPacket * aData->aDescription.mBytesPerFrame 
); 

이 작동하지 않습니다 : 그것은 AudioConverterNew returned -50 메시지와 함께 실패 할 AudioQueuePrime됩니다. 나는 PCM을 위해 mFramesPerPacket이 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; 
} 
관련 문제