본문 바로가기
ios 뽀개기/objective-c

core audioqueue player 에 대하여

by 인생여희 2019. 3. 28.
반응형

core audioqueue player 에 대하여

코어오디오를 이용해서 음원을 플레이 하는 어플을 간단하게 만들어보았다. 애플 오디오 프로그래밍 가이드 문서를 기반으로

구현을 하였다. 작동 순서는 prepare  버튼을 눌러서 먼저 재생할 오디오 파일 형식, 파일 위치와 관련된 설정을 해준다.

start 버튼을 눌러서 음원을 재생한다. 재생을 하면 재생이 완료 될때까지 끊임없이 콜백함수가 호출되면서 오디오 파일에서 읽은 

데이터를 버퍼에 넣고 출력한다. 그리고 음악이 종료되며면 dispose 버튼을 눌러서 메모리에 할당된 큐를 해제해준다.

처음에 프로세서를 알기까지 많은 시간이 걸렸다. 단순하게 오디오를 재생하려면 이미 

만들어져있는 MediaPlayer 오디오 프레임워크를 가져다 쓰면된다. 하지만  구간반복, 재생속도 조절 등과 같이 오디오를 

자유자재로 컨트롤하려면 코어오디오에 대한 개념이 반드시 필요하다. 

 

 

소스

/*
 - 순서
 1. 상태, 형식 및 경로 정보를 관리하는 사용자 지정구조를 정의한다.
 2. 실제 재생을 수행하는 오디오 큐 콜백 함수를 작성한다.
 3.오디오 큐 버퍼에 적합한 크기를 결정하는 코드를 작성한다.
 4.재생할 오디오 파일을 열고 오디오 데이터 형식을 결정한다.
 5. 재생오디오 큐를 생성하고 재생하기 위해서 셋팅한다.
 6. 오디오 큐 버퍼를 할당하고, 오디오 큐에 넣는다. 오디오 큐에 재생이 시작될 것을 알린다.
 재생이 완료되면 재생 콜백은 오디오 큐에 중지를 알린다.
 7. 오디오 큐 삭제, 리소스 해제하기
 
 */



#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudioKit/CoreAudioKit.h>

//step 1. 시작 전 오디오 형식 및 오디오 큐 상태 정보를 관리하는 데 사용할 사용자 지정 구조를 정의 해야 한다.

static const int kNumberBuffers = 3;                                                 //1. 사용할 오디오 큐 버퍼 수 정하기

struct AQPlayerState {
    AudioStreamBasicDescription mDataFormat;                            // 2.파일의 오디오 데이터 형식을 나타냄, mQueue 필드에 지정된 오디오 큐에 사용된다.
    AudioQueueRef mQueue;                                                            // 3. app에서 생성하는 재생 오디오 큐
    AudioQueueBufferRef mBuffers [kNumberBuffers];                  // 4.오디오 큐가 관리하는 오디오 큐 버퍼의 포인터를 보관하는 배열
    AudioFileID mAudioFile;                                                               // 5. 프로그램에서 재생하는 오디오 파일을 나타내는 오디오 파일 객체.
    UInt32 bufferByteSize;                                                                 // 6.각 오디오 큐 버퍼의 크기 (바이트)
    SInt64 mCurrentPacket;                                                               // 7.오디오 파일에서 재생할 다음 패킷의 패킷 인덱스
    UInt32 mNumPacketsToRead;                                                      // 8.오디오 큐의 재생 콜백이 호출될때마다 읽을 패킷의 수
    AudioStreamPacketDescription * mPacketDescs;                      // 9. vbr일 경우 재생중인 파일에 대한 패킷 설명. cbr일 경우 null
    bool mIsRunning;                                                                          // 10. 오디오 큐가 실행중인지 여부를 나타내는 부울 값.
};

@interface ViewController : UIViewController


@end

 

#import "ViewController.h"


#define VErr(err, msg)  do {\
if(nil != err) {\
NSLog(@"[ERR]:%@--%@", (msg), [err localizedDescription]);\
return ;\
}\
} while(0)


#define VStatus(err, msg) do {\
if(noErr != err) {\
NSLog(@"[ERR-%d]:%@", err, (msg));\
return ;\
}\
} while(0)


#define VStatusBOOL(err, msg) do {\
if(noErr != err) {\
NSLog(@"[ERR-%d]:%@", err, (msg));\
return NO;\
}\
} while(0)




//step2. 재생 오디오 큐 콜백 함수 작성
//. 기능
//* 오디오 파일에서 지정된 양의 데이터를 읽어서 오디오 버퍼에 삽입.
//* 오디오 버퍼를 큐에 넣는다.
//* 오디오 파일에서 읽을 데이터가 없을 때 오디오 대기열에 중지 할 것을 알린다.
static void HandleOutputBuffer (
                                                        void * aqData,                                   // 1. 오디오 큐에 대한 상태 정보
                                                        AudioQueueRef inAQ,                      // 2. 이 콜백을 소유한 오디오 큐
                                                        AudioQueueBufferRef inBuffer       // 3. 콜백이 오디오 파일에서 읽은 데이터를 넣을 버퍼.
){
    
    NSLog(@"HandleOutputBuffer 콜백함수 진입");
    
    //step 2-1. 오디오 큐에 대한 상태 정보
      struct AQPlayerState *pAqData = (struct AQPlayerState *) aqData;
    
    
    NSLog(@"pAqData->mIsRunning : %@" , pAqData->mIsRunning ? @"yes" : @"no"); //no 면 정지 됨
    
    
   // if (pAqData->mIsRunning == 0) return;                                               //. 오디오 큐가 중지되면 리턴.
    
    UInt32 numBytesReadFromFile  = pAqData->bufferByteSize;          //. 재생중인 파일에서 읽은 오디오 데이터의 바이트 값을 가지는 변수
    UInt32 numPackets = 10*1000;
    //UInt32 numPackets = pAqData->mNumPacketsToRead;                 //. 재생중인 파일에서 읽을 패킷수.  -  오류남! 확인!
    
    
    //step 2-2. 오디오 파일의 데이터를 판독해서 버퍼에 넣는 역할 하는 함수 작성 (오디오 데이터 -> 버퍼)
    OSStatus stts =  AudioFileReadPacketData (
                                              pAqData->mAudioFile,                       // 2. 읽을 오디오 파일
                                              NO,                                                     // 3. 함수가 읽을 때 데이터를 캐시 (x)
                                              &numBytesReadFromFile,                    // 4. 출력시 오디오 파일에서 읽은 오디오 데이터의 바이트 수.
                                              pAqData->mPacketDescs,                    // 5.출력시 오디오 파일에서 읽은 데이터에 대한 패킷의 설명. cbr = null
                                              pAqData->mCurrentPacket,                  // 6. 오디오 파일에서 읽을 첫번째 패킷의 인덱스.
                                              &numPackets,                                          // 7. 입력시 오디오 파일에서 읽을 패킷수. 출력시 실재로 읽힌 패킷수.
                                              inBuffer->mAudioData                              // 8. 출력시 오디오 파일에서 읽은 데이터가 들어 있는 버퍼.
                          );
    
    VStatus(stts, @"AudioFileReadPacketData");
    
    if (numPackets> 0) {                                                                            //.파일로 부터 오디오 데이터를 읽었는지 여부. 읽었다면 버퍼를 큐에 할당. 못읽었다면 중지.
        
        inBuffer-> mAudioDataByteSize = numBytesReadFromFile;       //. 오디오 큐 버퍼에 읽은 데이터의 바이트 수를 할당.
        
        
        //step 2-3. 오디오 파일 데이터가 읽혀서 오디오 큐 버퍼에 저정되어 있는 상태이다. (버퍼 -> 큐)
        //아래 함수 처럼, 콜백이 버퍼를 큐에 넣는다. 큐에 들어가면 버퍼의 오디오 데이터를 큐에서 출력장치로 보낼 수 있다.
        //버퍼를 큐에 추가하는 함수
        AudioQueueEnqueueBuffer (
                                 pAqData-> mQueue,                                                // 2. 버퍼가 담긴 큐를 소유하고 있는 오디오 큐
                                 inBuffer,                                                                    // 3. 큐에 넣을 오디오 버퍼.
                                 (pAqData-> mPacketDescs? numPackets : 0),      // 4. 오디오 큐 버퍼의 데이터  패킷의 수. cbr = 0
                                 pAqData-> mPacketDescs                                       // 5
                                 );
        
        
        pAqData-> mCurrentPacket += numPackets;                                 //.읽은 패킷 수에 따라 패킷 인덱스 증가.
        
        
    } else {
        
        //콜백이 하는 마지막 작업은 재생중인 오디오가 더 이상 읽을 데이터가 없는지 확인하는 것이다.
        //파일의 끝을 발견하면 콜백은 재생 오디오 큐에 멈추라고 이야기 한다.
        //AudioFileReadPackets함수에 의해 읽힌 패킷의 수가 콜백에 의해 이전에 호출 되었는지를 검사한다  패킷이 0 이면 읽을 파일 x.
        AudioQueueStop (                                                    // 2.오디오 큐를 정지하는 함수
                                        pAqData-> mQueue,               // 3.중지할 오디오 큐
                                        false                                        //  4.큐에 저장된 모든 버퍼가 재생되면 오디오 큐를 비동기 적으로 중지한다.
                                    );
        
        pAqData-> mIsRunning = false;                           // 5. 재생이 완료되었음을 나타낸다.
    }
    
    
    
}



@interface ViewController (){
    struct AQPlayerState pAqData;
}

@property (weak, nonatomic) IBOutlet UIButton *play;
@property (weak, nonatomic) IBOutlet UIButton *stop;
@property (weak, nonatomic) IBOutlet UIButton *prepare;

@property (strong, nonatomic) NSURL *musicURL;
@end

@implementation ViewController

//step3. AVAudioSession 설정 -AVAudioSessionCategorySoloAmbient 로 설정.
- (void) setAudioSession {
    NSError *error;
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setActive:YES error:&error];
   
    if (nil != error) {
        NSLog(@"AudioSession setActive 에러 발생:%@", error.localizedDescription);
        return;
    }
    
    error = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:&error];
    if (nil != error) {
        NSLog(@"AudioSession setCategory(AVAudioSessionCategoryPlayAndRecord) 에러 발생:%@", error.localizedDescription);
        return;
    }
}






- (void)viewDidLoad {
    [super viewDidLoad];
    
}

//오디오 파일 준비
- (BOOL) prepareAudioFile {
    //step 3-1 읽을 오디오 파일 셋팅 및 초기화
    [self setAudioSession];
    
    //step4.오디오 파일에 대한 CFURL 객체 가져 오기
    CFURLRef audioFileURL = (__bridge CFURLRef) _musicURL;
    //nill 이면 로컬에 있는 파일 재생하기
    if (nil == audioFileURL) {
        audioFileURL = (__bridge CFURLRef) [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"03" ofType:@"m4a"]];
    }
    
    
    
    //step5. 재생을 위해 오디오 파일열기
    OSStatus result =
    AudioFileOpenURL (                                                                                  // 2. 재생할 파일을 여는 함수
                      audioFileURL,                                                                          // 3.  재생할 파일 참조
                      kAudioFileReadPermission,                                                     //4.  재생 중인 파일에 사용할 파일 권한. File Access Permission Constants열거 형에 정의되어있음.
                      0,                                                                                               //5. 파일 타입 관련 옵션. 0은 이 기능을 사용하지 않겠다.
                      &pAqData.mAudioFile                                                              //6. 출력시 오디오 파일에 대한 참조가 사용자 정의 구조 mAudioFile 필드에 배치 된다.
                      );
    
    //CFRelease (audioFileURL);                                                                        // 7. 위에서 만든 CFURL 개체 해제.
    VStatusBOOL(result, @"AudioFileOpenURL");
    NSLog(@"open file %@ success!", audioFileURL);
    
    //step6. 파일의 오디오 데이터 형식 얻기
    UInt32 dataFormatSize = sizeof (pAqData.mDataFormat);                       // 1. 오디오 파일의 오디오 데이터 형식을 쿼리 할 때 사용할 예상 속성 값 크기를 가져온다.
    
    result = AudioFileGetProperty (                                                                                  //2. 오디오 파일의 지정된 속성의 값을 획득
                                   pAqData.mAudioFile,                                                               //3. AudioFileID 오디오 데이터 형식을 가져올 파일을 나타내는 오디오 파일 객체
                                   kAudioFilePropertyDataFormat,                                             //4. 오디오 파일의 데이터 형식 값을 얻기 위한 속성 ID.
                                   &dataFormatSize,                                                                    //5. 입력시 AudioStreamBasicDescription오디오 파일의 데이터 형식을 설명하는 구조체의 예상 크기. 출력시 사용 x
                                   &pAqData.mDataFormat                                                          //6. 출력시 AudioStreamBasicDescription 오디오 파일에서 가져온 전체 오디오 데이터 형식을 구조체 형식으로 출력. 파일의 오디오 데이터 형식을 오디오 큐의 사용자 지정 구조에 저장하여 오디오 대기열에 적용
                                   );
    VStatusBOOL(result, @"AudioFileGetProperty");
    
    
    //step7. 재생오디오 큐 만들기: 이전 단계에서 구성한 사용자 지정 구조 및 콜백과 재생할 파일의 오디오 데이터 형식을 사용한다.
    result = AudioQueueNewOutput (                                                                                  // 1
                                  &pAqData.mDataFormat,                                         // 2. 오디오 큐가 재생되도록 설정된 파일의 오디오 데이터 형식이다.
                                  HandleOutputBuffer,                                                // 3. 재생 오디오 큐와 함께 사용할 콜백 함수.
                                  &pAqData,                                                                // 4. 재생 오디오 큐의 사용자 지정 데이터 구조.
                                  CFRunLoopGetCurrent (),                                      // 5. 현재 실행중인 루프와 오디오 큐 재생 콜백이 호출되는 루프.
                                  kCFRunLoopCommonModes,                                // 6. 콜백을 호출 할 수 있는 실행 루프 모드.
                                  0,                                                                             // 7.
                                  &pAqData.mQueue                                                // 8. 출력시 새로 할당 된 재생 오디오 큐.
                                  );
    VStatusBOOL(result, @"AudioQueueNewOutput");
    
    //step 8. 재생 오디오 큐의 크기 설정 : 오디오 큐에 버퍼를 할당할때와 오디오 파일을 읽기 시작하기 전에 사용
    //설정방법: 오디오 큐의 버퍼 크기, 재생 오디오 큐 콜백 호출 마다 읽을 패킷 수, 버퍼 하나의 오디오 데이터 패킷 설명을 보관 유지 하기 위한 배열 사이즈
    //읽을 버퍼 크기 및 패킷 수 설정 : 여기에서 각 오디오 큐 버퍼의 크기를 설정하고, 재생 오디오 큐 콜백을 호출할때 마다 읽을 패킷 수를 결정한다.
    UInt32 maxPacketSize;
    UInt32 propertySize = sizeof (maxPacketSize);
    result = AudioFileGetProperty (                                                                              // 1.  재생하려는 파일의 오디오 데이터 패킷 크기에 대한 max를 바이트 단위로 구하기
                                   pAqData.mAudioFile,                                                             //2. 재생할 파일을 나타내는 오디오 파일 객체.
                                   kAudioFilePropertyPacketSizeUpperBound,                     // 3.오디오 파일 패킷 사이즈를 얻기위한 프로퍼티 id
                                   &propertySize,                                                                    // 4. 출력시 kAudioFilePropertyPacketSizeUpperBound 속성의 크기
                                   &maxPacketSize                                                                 // 5. 출력시 재생할 파일의 패킷 크기에 대한 max 값
                                   );
    VStatusBOOL(result, @"AudioFileGetProperty");
    
    
    // kNumberPackages = 10*1000,
    pAqData.bufferByteSize = (10*1000) * maxPacketSize;
    //pAqData.mPacketDescs =(AudioStreamPacketDescription *) malloc((10*1000) * sizeof(AudioStreamPacketDescription));
    
    
        //step 9. 패킷 설명 배열에 메모리 할당
        //하나의 버퍼에 들어있는 오디오 데이터에 대한 패킷 설명을 보관할 배열의 메모리를 할당한다.
    
        //오디오 파일의 데이터 형식이 vbr인지 cbr인지 결정.  vbr 데이터에서 패킷당 바이트 수 or 패킷당 프레임 수 중 하나 또는 모두가 가변적이므로 0 이다.
        bool isFormatVBR = (pAqData.mDataFormat.mBytesPerPacket == 0 || pAqData.mDataFormat.mFramesPerPacket == 0);
    
        //vbr데이터가 포함된 오디오 파일의 경우 패킷 설명 배열에 메모리를 할당한다.
        if (isFormatVBR) {
            //재생 콜백을 호출 할 때마다 읽을 오디오 데이터 패킷 수에 따라 필요한 메모리를 계산한다.
            pAqData.mPacketDescs =(AudioStreamPacketDescription *) malloc((10*1000) * sizeof(AudioStreamPacketDescription));
            //pAqData.mPacketDescs = (AudioStreamPacketDescription *) malloc (pAqData.mNumPacketsToRead * sizeof (AudioStreamPacketDescription)); //apple 문서
        } else {
            pAqData.mPacketDescs = NULL;  //선형 pcm과 같은 cbr 데이터가 포함된 오디오 파일의 경우 오디오 대기열은 패킷 설명 배열을 사용하지 않는다.
        }
    
    
    
    //step 10. 매직 쿠키 설정
    //mpeg 4 aac 와 같은 일부 압축 오디오 포맷은 구조를 사용해서 오디오 메타 데이터를 포함한다. (이 구조를 매직 쿠키라고 함)
    //오디오 큐 서비스를 사용해서 이러한 형식의 파일을 재생할 때 재생을 시작하기 전에 오디오 파일에서 매직쿠키를 가져와서 오디오 큐에 추가.
    //아래는 매직 쿠키를 얻어 오디오 큐에 적용하는 방법. 재생을 시작하기 전에 이 함수를 호출 한다.
    
    UInt32 cookieSize = sizeof (UInt32);                                                                  //1. 매직 쿠키 데이터의 예상 크기 설정
    
    bool couldNotGetProperty =                                                                                //2. AudioFileGetPropertyInfo 함수의 결과를 캡쳐한다. 성공하면 false 반환.
    AudioFileGetPropertyInfo (                                                                                   //3.
                              pAqData.mAudioFile,                                                   //4.재생할 오디오 파일을 나타내는 오디오 파일 객체.
                              kAudioFilePropertyMagicCookieData,                        //5.오디오 파일의 매직 쿠키 데이터를 나타내는 속성 ID
                              &cookieSize,                                                                 //6.입력시 마법 쿠키 데이터의 예상 크기, 출력시 실제 크기.
                              NULL                                                                             //7.
                              );
    
    if (!couldNotGetProperty && cookieSize) {                                                             //8. 오디오 파일에 매직쿠키가 포함되어 있으면 이를 보관할 메모리 할당.
        
        char* magicCookie = (char *) malloc (cookieSize);
        
        AudioFileGetProperty (                                                                                          // 9. 파일의 매직 쿠키를 가져온다.
                              pAqData.mAudioFile,                                                       // 10.재생할 오디오 파일을 나타내는 오디오 파일 객체.
                              kAudioFilePropertyMagicCookieData,                             //11.오디오 파일의 마법 쿠키 데이터를 나타내는 속성 id
                              &cookieSize,                                                                      //12.입력시 함수 매직 쿠키를 사용하여 얻은 변수의 크기, 출력시 매직 쿠키 변수에 기록된 바이트 크기.
                              magicCookie                                                                       // 13. 출력시 오디오 파일의 매직쿠키.
                              );
        
        AudioQueueSetProperty (                                                                                // 14. 오디오 큐 속성 설정. 오디오 큐의 매직 쿠키를 설정하여 재생할 오디오 파일의 마법 쿠키와 일치 시킨다.
                               pAqData.mQueue,                                                                // 15. 매직 쿠키를 설정하려는 오디오 큐.
                               kAudioQueueProperty_MagicCookie,                                 // 16.오디오 큐의 매직 쿠키를 나타내는 속성 id
                               magicCookie,                                                                       // 17. 재생할 오디오 파일의 매직 쿠키
                               cookieSize                                                                            // 18. 매직 쿠키의 크기(바이트)
                               );
        
        free (magicCookie);                                                                                                         // 19.매직 쿠키에 할당 된 메모리를 해제.
    }
    
    pAqData.mCurrentPacket = 0;                                                                   // 1. 오디오 큐 콜백이 버퍼를 채우기 시작할때, 오디오 파일의 시작 부분에서 시작되도록 패킷 인덱스를 0으로 설정.
    
    for (int i = 0; i < kNumberBuffers; ++i) {                                                   // 2. 오디오 큐 버퍼 세트를 할당 및 준비
        
        AudioQueueAllocateBuffer (                                                                  // 3. 이 함수는 메모리를 할당하여 오디오 큐 버퍼를 만듭니다.
                                  pAqData.mQueue,                                    // 4. 오디오 큐 버퍼를 할당하고 있는 오디오 큐.
                                  pAqData.bufferByteSize,                         // 5. 새로운 오디오 큐 버퍼의 사이즈.(바이트)
                                  &pAqData.mBuffers[i]                             // 6. 출력시 mBuffers 사용자 지정 구조의 배열에 새 오디오 큐 버퍼를 추가한다.
                                  );
        
        HandleOutputBuffer (                                                                                            // 7. 재생 오디오 큐 콜백.
                            &pAqData,                                                                          // 8. 오디오 큐의 사용자 지정 데이터 구조
                            pAqData.mQueue,                                                             // 9. 콜백하고 있는 오디오 큐
                            pAqData.mBuffers[i]                                                         // 10. 오디오 큐 콜백에 전달할 오디오 큐 버퍼.
                            );
    }
    
    Float32 gain = 1.0;                                                                             // 1. , 0 ~ 1.0 (무음~크게)
    
    // 선택적으로 사용자가 설정을 재정의 할 수 있음
    AudioQueueSetParameter (                                                                //2. 오디오 큐에 대한 매개 변수 값을 설정.
                            pAqData.mQueue,                                                      //3.  매개 변수를 설정하려는 오디오 큐.
                            kAudioQueueParam_Volume,                                    // 4. 설정된 매개 변수의 ID
                            gain                                                                             // 5. 오디오 대기열에 적용 할 gain 설정
                            );
    
    return YES;
}


- (IBAction)playBtn:(id)sender {
    NSLog(@"플레이 버튼 클릭");
    pAqData.mIsRunning = true;                                      //1. 큐가 실행중
    
    OSStatus stts = AudioQueueStart (                          // 2. 자체 스레드에서 오디오 큐 시작.
                     pAqData.mQueue,                                      // 3. 시작할 오디오 대기열.
                     NULL                                                           // 4.큐가 즉시 시작해야 함.
                     );
    
    do {                                                                                 // 5. mIsRunning을 정기적으로 체크해서 오디오 큐가 중지되었는지 확인.
        CFRunLoopRunInMode (                                            // 6. 오디오 큐의 스레드가 포함 된 실행 루프를 실행.
                            kCFRunLoopDefaultMode,                     // 7.실행 루프의 기본 모드 사용.
                            0.25,                                                       // 8. 실행 루프의 시간을 0.25초 단위로 설정
                            false                                                       // 9.실행루프가 지정된 전체 시간 동안 계속되어야 함.
                            );
    } while (pAqData.mIsRunning);
    
    CFRunLoopRunInMode (                                             // 10.오디오 큐가 중지된 후 실행 루프를 조금 더 오래 실행해서 현재 재생중인 오디오 대기열 버퍼에 완료 시간이 있는 지 확인.
                        kCFRunLoopDefaultMode,
                        1,
                        false
                        );
     VStatus(stts, @"AudioQueueStart");
}

//큐 정지
- (IBAction)stopBtn:(id)sender {
    OSStatus stts = AudioQueuePause(pAqData.mQueue);
    VStatus(stts, @"AudioQueuePause");
}

//준비 or 해제
- (IBAction)preparebtn:(id)sender {
    
    static BOOL once = NO;
    if (! once ) {
        if (! [self prepareAudioFile]) {
            NSLog(@"dipose error!");
            return ;
        }
        [_prepare setTitle:@"Dispose" forState:UIControlStateNormal];
        once = YES;
    } else {
        if (! [self dipose]) {
            NSLog(@"dipose error!");
            return ;
        }
        [_prepare setTitle:@"Prepare" forState:UIControlStateNormal];
        once = NO;
    }
    
}


- (BOOL) dipose {
    if (pAqData.mQueue) {
        AudioQueueDispose (                                          // 1. 오디오 큐와 해당 버퍼를 포함하여 모든 리소스 삭제.
                           pAqData.mQueue,                             // 2. 삭제할 큐
                           true                                                     // 3. 오디오 큐를 동기적으로 처리
                           );
    }
    
    if (pAqData.mAudioFile) {
        AudioFileClose (pAqData.mAudioFile);                 // 4. 재생 된 오디오 파일을 닫는다.
    }
    
    if (pAqData.mPacketDescs) {
        free (pAqData.mPacketDescs);                            // 5. 패킷 설명을 유지하는데 사용된 메모리 해제.
        pAqData.mPacketDescs = NULL;
    }
    
    return YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

 

파일

예제파일1

예제파일2

 

반응형

댓글