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

audiosessionQueue play - 코어오디오 재생하기

by 인생여희 2018. 12. 19.
반응형

audiosessionQueue play




.h


#import <UIKit/UIKit.h>

#import <AudioToolbox/AudioToolbox.h>

#import <CoreAudioKit/CoreAudioKit.h>

#import <MediaPlayer/MediaPlayer.h>

#import <AVFoundation/AVFoundation.h>


@interface CoreAudioPlayViewController : UIViewController <MPMediaPickerControllerDelegate>


enum {

    kNumberBuffers = 3,                     //오디오 버퍼는 3개

    kNumberPackages = 10* 1000,     //한번에 읽는 패키지 개수

};



struct PlayerStat

{

    AudioStreamBasicDescription   mDataFormat;                                      //오디오 스트림의 광범위한 특성정의

    AudioQueueRef                           mQueue;                                               // 재생오디오큐

    AudioQueueBufferRef                 mBuffers[kNumberBuffers];               //오디오 큐 버퍼 포인트의 리스트

    AudioFileID                                 mAudioFile;                                            //오디오 파일 객체

    UInt32                                          bufferByteSize;                                     //오디오 큐 버퍼 하나의 사이즈

    SInt64                                          mCurrentPacket;                                  //현재 읽은 패킷

    UInt32                        mNumPacketsToRead;                                           //playback callback 이 불렸을 때 한번에 읽을 packet 의 갯수

    AudioStreamPacketDescription *mPacketDescs;                               // vbr 오디오 인경우 필요한 pecket 정보

    bool                                                mIsRunning;                                      // 현재 재생 오디오 큐가 재생 중인지

};







/*

 

         struct AudioStreamBasicDescription

         {

             Float64             mSampleRate;

             AudioFormatID       mFormatID;

             AudioFormatFlags    mFormatFlags;

             UInt32              mBytesPerPacket;

             UInt32              mFramesPerPacket;

             UInt32              mBytesPerFrame;

             UInt32              mChannelsPerFrame;

             UInt32              mBitsPerChannel;

             UInt32              mReserved;

         };

 

 

         LPCM 포맷이나 모든 패킷이 정보가 동일한 constant bit rate (CBR) 포맷은 ASDB만으로도 충분하지만

         패킷마다 bitrate가 다를 수 있는 variable bit rate (VBR) 포맷은

         각 패킷의 정보를 담고 있는 AudioStreamPacketDescription 가 추가적으로 필요하다!

         VBR포맷의 ASBD를 프린트 해 보면 패킷마다 값이 다르기 때문에 mBytesPerPacket와 mFramesPerPacket 의 값은 0이다.

 


 

         struct  AudioStreamPacketDescription

         {

             SInt64  mStartOffset;

             UInt32  mVariableFramesInPacket;                       // 한 패킷의 프레임 수

             UInt32  mDataByteSize;                                         // 패킷의 실제 크기

         };

 

 

 */



@end


.m


#import "CoreAudioPlayViewController.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)


//int AQPlayer::GetCurrentTime() {

//    int timeInterval = 0;

//    AudioQueueTimelineRef timeLine;

//    OSStatus status = AudioQueueCreateTimeline(mQueue, &timeLine);

//    if(status == noErr) {

//        AudioTimeStamp timeStamp;

//        AudioQueueGetCurrentTime(mQueue, timeLine, &timeStamp, NULL);

//        timeInterval = timeStamp.mSampleTime / mDataFormat.mSampleRate; // modified

//    }

//    return timeInterval;

//}

//



//큐의 버퍼 크기를 계산하고, 각 버퍼에 몇 개의 패킷을 읽을 수 있는지 알게하는 함수

void CalculateBytesForTime (

                            AudioFileID inAudioFile,

                            AudioStreamBasicDescription inDesc,

                            Float64 inSeconds,

                            UInt32 *outBufferSize,

                            UInt32 *outNumPackets)

{

    NSLog(@"CalculateBytesForTime  진입");


    /*

     코어오디오에서 오디오 데이터를 오디오 큐에 전달하기 위해서는 AudioQueueBuffer 라는 객체를 사용한다.

     이 버퍼를 생성하기 전에 재생할 오디오 데이터를 분석해서 어떤 사이즈의 버퍼를 사용할 지,

     한 번에 몇개의 packet을 읽을지 결정해야 한다.

     다음 함수는 사용할 버퍼의 사이즈를 outBufferSize에 저장하고, 읽을 패킷의 갯수를 outNumPackets에 저장된다.

     */

    

    //AudioFileGetProperty함수를 이용해서 "maximum packet size"를 구한다.

    UInt32 maxPacketSize;

    UInt32 propertySize = sizeof(maxPacketSize);

    OSStatus  stts = AudioFileGetProperty(

                                inAudioFile,

                                kAudioFilePropertyPacketSizeUpperBound,

                                &propertySize,

                                &maxPacketSize);

    VStatus(stts, @"AudioFileGetProperty-kAudioFilePropertyDataFormat - 3 오류 발생 ");

    

    NSLog(@"CalculateBytesForTime  - maxPacketSize : %d" ,maxPacketSize);

    

    static const int maxBufferSize = 0x10000; // limit size to 320KB

    static const int minBufferSize = 0x4000; // limit size to 16K

    

    //ASDB에 패킷당 프레임 갯수가 전체 오디오 데이터를 통틀어 일정하다는 뜻이다

    if (inDesc.mFramesPerPacket != 0) {

        //주어진 시간 (=seconds)에 처리할 수 있는 패킷의 갯수 (=numPacketsForTime) 를 구할 수 있고

        //이를 통해 주어진 시간에 필요한 버퍼의 크기(=outBufferSize)를 계산할 수 있다.

        Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;

        *outBufferSize = numPacketsForTime * maxPacketSize;

         NSLog(@"CalculateBytesForTime  - 패킷당 프레임 갯수 동일");

        NSLog(@"CalculateBytesForTime  - numPacketsForTime : %f" ,numPacketsForTime);

        NSLog(@"CalculateBytesForTime  - outBufferSize : %d" ,(int)outBufferSize);

    } else {

        

        // 패킷 당 프레임 수가 0이면 코덱에 예측 가능한 패킷이 없다.(얼마나 많은 패킷이 시간주기를 나타내는 지 알지 못한다.)

         //패킷당 프레임 갯수가 다른 경우라면 maxBufferSize 와

        // maxPacketSize (AudioFileGetProperty 함수로 구할 수 있음) 를 비교해서 더 큰 값으로 설정해 준다.

        

        *outBufferSize = maxBufferSize > maxPacketSize ? maxBufferSize : maxPacketSize;

        NSLog(@"CalculateBytesForTime  - outBufferSize : %d" ,(int)outBufferSize);

    }

    

   //얻은 값이 너무 크다면 maxBufferSize 에 맞춰준다

    if (*outBufferSize > maxBufferSize && *outBufferSize > maxPacketSize)

        *outBufferSize = maxBufferSize;

    else {

        

        // 얻은 값이 너무 작다면 minBufferSize 에 맞춰준다

        if (*outBufferSize < minBufferSize)

            *outBufferSize = minBufferSize;

    }

    

    //버퍼의 크기와 최대 패킷 크기를 안다면 콜백 한번에 읽을 수 있는 패킷 갯수를 계산한다.

    *outNumPackets = *outBufferSize / maxPacketSize;

}




#pragma ************************      STEP 7 AUDIOQUEUEOUTPUTCALLBACK 함수 구현     ************************


//AudioOutPutCallBack 함수 - 파일에서 읽은 데이터를 전달된 버퍼에 할당하는 작업

/*

 이 함수는 AudioQueue가 버퍼에 담긴 오디오 데이터를 재생하는데 모두 소진하고 이 버퍼가 다시 사용될 수 있을 때 불린다.

 즉, 이 콜백 함수가 불렸을 때 같이 전달된 버퍼에 오디오 데이터를 밀어 넣는 작업을 수행하여야한다.

 */

void impAudioQueueOutputCallback(

                                                             void *inUserData,                                   //AudioQueueNewOutput 에서 전달한 사용자 데이터

                                                             AudioQueueRef inAQ,                            //콜백을 부른 audioQueue

                                                             AudioQueueBufferRef inBuffer) {         // 채워야 할 buffer

    

     NSLog(@"impAudioQueueOutputCallback  진입 - AudioFileReadPacketData ->  AudioQueueEnqueueBuffer 실행");

    

    //Callback에서 넘어온 aqData를 직접 구현한 사용자 구조체로 캐스팅

    struct PlayerStat *playerStat = (struct PlayerStat *)inUserData;

    


    //STEP - 1:  오디오 버퍼에 오디오 데이터 넣기

    UInt32 bufLen =  playerStat->bufferByteSize;                                                    //실제로 읽은 데이터의 길이를 저장하기 위한 변수

    UInt32 numPkgs =  playerStat->mNumPacketsToRead;                                    //몇개의 패킷을 읽어야 하는지 (Step 4에서 구한 값)

    //UInt32 numPkgs =  kNumberPackages;  수정됨

    

    OSStatus stts = AudioFileReadPacketData(

                                                                            playerStat->mAudioFile,                     // 데이터를 읽어들일 AudioFile

                                                                            NO,                                                        // cache를 할지말지

                                                                            &bufLen,                                                // 실제로 읽은 데이터의 길이 (output)

                                                                            playerStat->mPacketDescs,               // ASBD array의 포인터, CBR인 경우 NULL

                                                                            playerStat->mCurrentPacket,             // 읽을 패킷의 위치

                                                                            &numPkgs,                                           // 실제 읽은 packet 갯수

                                                                            inBuffer->mAudioData                        // 채울 오디오 버퍼

                                                                            );

    //위 스텝이 끝나고 나면 numPackets 에는 실제로 읽은 packet의 갯수가 저장되어 있다.

    

    VStatus(stts, @"AudioFileReadPacketData 오류가 발생했습니다. - !");

    inBuffer->mAudioDataByteSize = bufLen;

    

    NSLog(@"bufLen 실제 읽은 데이터 길이 : %d , numPkgs 실제로 읽은 패킷 개수: %d" , bufLen, numPkgs); // 40000, 10000

    

    // STEP -2 : 데이터 버퍼를 AudioQueueBufferRef enqueue 하기

    //버퍼가 준비가 되었다면 AudioQueueEnqueueBuffer함수를 이용해 해당 버퍼를 오디오 큐에 밀어넣어 준다.

    stts = AudioQueueEnqueueBuffer(

                                                               playerStat->mQueue,                                      //버퍼를 넣을 오디오 큐

                                                               inBuffer,                                                            //넣을 버퍼

                                                               numPkgs,                                                         // asbd의 개수, cbr = 0

                                                               playerStat->mPacketDescs                             //asbd array 포인터

                                                            );

    VStatus(stts, @"AudioQueueEnqueueBuffer 오류가 발생했습니다. -!!");

    playerStat->mCurrentPacket += numPkgs;                                                          //현재 읽고 있는 packet의 위치 !!

    

    

    // STEP - 3: 더이상 읽어들일 오디오 데이터가 없을 때 오디오 큐를 정지시킨다.

    if (numPkgs == 0) {

         NSLog(@"impAudioQueueOutputCallback  -  AudioQueueStop 실행");

        AudioQueueStop(

                                       playerStat->mQueue,                                                      //멈출 큐

                                       false                                                                 // immediately 멈출지 enqeue된 버퍼까지는 모두 재생하고 멈출 지

                                    );

        

        

        //mPlayer->mIsRunning = false; // playback이 끝났다는 표시

    }


};




@interface CoreAudioPlayViewController (){

    struct PlayerStat playerStat_;

}

@property (strong, nonatomic) IBOutlet UIButton *prepareBtn;

@property (strong, nonatomic) IBOutlet UIButton *playBtn;

@property (strong, nonatomic) MPMediaPickerController *mpPickerVC;

@property (strong,nonatomic) NSURL *musicURL;


@end


@implementation CoreAudioPlayViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    NSLog(@"viewDidLoad  진입 - MPMediaPickerController 초기화");

    //MPMediaPickerController 초기화

    _mpPickerVC = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio];

    _mpPickerVC.prompt = @"재생할 곡을 선택해 주세요.";

    _mpPickerVC.allowsPickingMultipleItems = NO;

    _mpPickerVC.showsCloudItems = NO;

    _mpPickerVC.delegate = self;


}



//플레이 시작

-(void)play{

    NSLog(@"play  진입 - AudioQueueStart 실행");

    //playerStat_ = true;

    OSStatus stts = AudioQueueStart(

                                                                playerStat_.mQueue,

                                                                NULL

                                                                );

    

     VStatus(stts, @"AudioQueueStart 오류 발생");

}


//멈춤


-(void)stop{

     NSLog(@"stop  진입 - AudioQueuePause 실행");

    

    OSStatus stts = AudioQueuePause(playerStat_.mQueue);

    

     VStatus(stts, @"AudioQueuePause 오류 발생");

}


//오디오 세션 셋팅

-(void)setAudioSession{

    NSLog(@"setAudioSession 진입");

    NSError *error;

    AVAudioSession *session = [AVAudioSession sharedInstance];

    [session setActive:YES error:&error];

    if (error != nil) {

        NSLog(@"AudioSession setActive error:%@", error.localizedDescription);

        return;

    }

    

    error = nil;

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:&error];

    if (error != nil) {

        NSLog(@"AudioSession setCategory(AVAudioSessionCategoryPlayAndRecord) error:%@", error.localizedDescription);

        return;

    }

    

    NSLog(@"AVAudioSession - setActive:YES -AVAudioSessionCategorySoloAmbient 셋팅 완료! ");

}


//준비

-(BOOL)prepareAudioFile{

     NSLog(@"prepareAudioFile 진입!");

    

    [self setAudioSession];

    

    /*

     재생에 관계없이 채널 수, 샘플링 속도 등과 같이 먼저 재생할 데이터의 기본 형식을 가져와야합니다.

     여기서는 예제로 AudioFileService를 사용하여 파일을 읽습니다.

     */

#pragma ************************      STEP 1  - AUDIOFILE 열기      ************************

    CFURLRef url  = (__bridge CFURLRef)_musicURL;

    if (url == nil) {

        url = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"test" ofType:@"wav"]];

    

    }

    NSLog(@"prepareAudioFile 실행될 URL :  %@" , url);

    

    /*

     코어 오디오에서의 Audio File Service는 디스크나 메모리로 부터 audio file을 열고,

     그것이 포함한 오디오 데이터 형식을 얻거나 설정하고,

     파일에 대해 읽기나 쓰기를 할 수 있게 한다.

     AudioFileOpenURL() 함수는 이미존재하는 audio file을 여는 함수이다.

     */

    OSStatus stts = AudioFileOpenURL(

                                                                 url,                                               //open 할 파일 경로

                                                                 kAudioFileReadPermission,

                                                                 0,

                                                                &playerStat_.mAudioFile             // output pointer  할당된다.

                                                                );

    VStatusBOOL(stts, @"AudioFileOpenURL 에러 발생!");

    NSLog(@"open file %@ 성공!!", url);

    

    

#pragma ************************      STEP 2 파일의 프로퍼티(정보) 읽기  (AUDIODATAFORMAT 불러오기)     ************************

 

    /*

     "AudioStreamBasicDescription" 정보를 가져오기 위해서 "kAudioFilePropertyDataFormat" 상수를 사용했다.

     오디오의 AlbumArtwork, PacketCount, Duration 등의 정보도 가져올 수 있다.

    */

  UInt32 descSize = sizeof(playerStat_.mDataFormat);

  stts = AudioFileGetProperty(

                                                    playerStat_.mAudioFile,                         //정보를 가져올 AudioFile

                                                     kAudioFilePropertyDataFormat,            //가져올 데이터 종류

                                                     &descSize,

                                                     &playerStat_.mDataFormat                   //output pointer 할당!

                                                );

    

    VStatusBOOL(stts, @"AudioFileGetProperty-kAudioFilePropertyDataFormat - 1오류 발생!!");

/*

 여기서는 가져올 데이터가AudioStreamBasicDescription 타입인 것을 알기 때문에 사이즈가 고정되어 있다.

타입에 따라서 사이즈를 알지 못할 때도 있다. 이런 경우에는 "AudioFileGetPropertyInfo()"함수를 이용해서 사이즈를 가져온다.

 */


    //파일 전체 재생시간 구하기

    Float64 outDataSize = 0;

    UInt32 thePropSize = sizeof(Float64);

    stts = AudioFileGetProperty(

                                                        playerStat_.mAudioFile,

                                                        kAudioFilePropertyEstimatedDuration,

                                                        &thePropSize,

                                                        &outDataSize

                                                    );

    VStatusBOOL(stts, @"AudioFileGetProperty-kAudioFilePropertyEstimatedDuration - 오류 발생!!");

    NSLog(@"파일 재생 시간  outDataSize : %f   thePropSize : %d " , outDataSize, thePropSize);


    

    

    

    

    

    

    

    

    

    

#pragma ************************      STEP 3 AUDIOQUEUE생성하기     ************************

    

  /*

   코어오디오 에서의 AudioQueue는 오디오 하드웨어의 일부와 통하는 간단한 인터페이스이다.

   AudioQueue는 버퍼 큐와 연결되어 있고 버퍼는 실제 오디오 데이터를 가지고 있는 메모리 블럭이다.

   재생 큐는 데이터로 채워진 버퍼를 받아서 스피커 하드웨어에 전달 한 뒤 빈 버퍼를 콜백 함수에 전달한다.

   콜백 함수에서 파일로부터 오디오 데이터를 읽어 버퍼에 담아준 뒤 오디오 큐에 전달한다.

   */

    stts = AudioQueueNewOutput(

                                                        &playerStat_.mDataFormat,

                                                        impAudioQueueOutputCallback,            //콜백함수

                                                        &playerStat_,                                           //사용자 정의 구조체

                                                        CFRunLoopGetCurrent(),

                                                        kCFRunLoopCommonModes,

                                                        0,

                                                        &playerStat_.mQueue                             //output audioQueue

                                                        );

    

     VStatusBOOL(stts, @"AudioFileGetProperty-kAudioFilePropertyDataFormat - 2 오류 발생");

    

    


#pragma ************************      STEP 4 버퍼 사이즈 계산하기     ************************

    

    //재생 버퍼 크기와 패킷의 수를 계산하기 위한 편의 함수 호출

    UInt32 bufferByteSize;

    //읽을 파일, asbd, 초단위 버퍼기간을 취하고, 적절한 버퍼 크기와 각 콜백에서 파일로 부터 읽을 오디오 패킷수를 나타내는 변수를 설정한다.

    CalculateBytesForTime(

                                              playerStat_.mAudioFile,

                                              playerStat_.mDataFormat,

                                              0.5,

                                              &bufferByteSize,

                                              &playerStat_.mNumPacketsToRead

                                        );

    

    

    playerStat_.bufferByteSize = bufferByteSize;

    

    

#pragma ************************      STEP 5 PACKETDESCRIPTION 메모리 설정     ************************

    

    /*

    패킷 정보 배열에 메모리 할당

    pocketDescs는 파일에서 읽은 각 오디오 버퍼의 내용을 나타내는 패킷 정보의 배열이다.

    패킷정보가 필요한지 여부는 오디오 형식이 가변(패킷정보가 필요함) 비트율인지 고정(패킷정보가 필요치 않음)인지에 달렸다

    Packet 사이즈가 동일한 CBR은 ASBD만으로도 충분한 정보가 되지만 VBR같은 경우는 각 패킷 마다 추가적인 정보를 들고 있어야 한다.

    이를 위해 오디오 데이터가 CBR 인지 VBR인지 구분해서 사용자 구조체에 메모리를 할당해 주자.

     */

    bool isFormatVBR = (playerStat_.mDataFormat.mBytesPerPacket == 0 ||

                                        playerStat_.mDataFormat.mFramesPerPacket == 0);

    if (isFormatVBR){

        

        //만약 VBR format 이라면 AudioStreamPacketDescription 의 사이즈 x 한번에 읽을 패킷 갯수 (= mNumPacketsToRead) 만큼 메모리를 할당해 준다. 이 정보는 오디오 큐 콜백 함수에서 오디오 데이터를 읽어들일 때 각각의 패킷을 분석할 때 쓰인다.

        playerStat_.mPacketDescs = (AudioStreamPacketDescription*)malloc

        (sizeof(AudioStreamPacketDescription) * playerStat_.mNumPacketsToRead);

    }else{

        playerStat_.mPacketDescs = NULL;

    }

    


    

    

    

    #pragma ************************      STEP 6 MAGIC COOKIE 설정     ************************

     /*

    오디오에서 magic cookie 란 MPEG4나 AAC 와 같은 압축 오디오 데이터 형식에서 사용하는 오디오 메타 데이터이다.

    오디오 데이터에서 magic cookie를 꺼내서 오디오 큐에 세팅해 줘야 한다.

    Magic cookie 데이터의 크기를 잘 모르기 때문에 AudioFileGetPropertyInfo 함수를 이용해서

    오디오 데이터에 실제로 magic cookie가 존재하는지, 존재 한다면 사이즈를 구해서 cookieSize 에 할당해 준다.*/

    UInt32 cookieSize = sizeof(UInt32);

    bool couldNotGetProperty = AudioFileGetPropertyInfo(

                                                                                                playerStat_.mAudioFile,

                                                                                                kAudioFilePropertyMagicCookieData,

                                                                                                &cookieSize,

                                                                                                NULL);

    

    //AudioFileGetPropertyInfo 함수에 대한 결과가 error가 아니고 (즉 magic cookie가 존재하고) 사이즈를 구했다면

    //AudioFileGetProperty 함수를 이용해서 실제로 magic cookie 정보를 불러온다.

    if (!couldNotGetProperty && cookieSize) {

       

        char *magicCookie= (char *) malloc(cookieSize);

        AudioFileGetProperty(

                                                 playerStat_.mAudioFile,

                                                 kAudioFilePropertyMagicCookieData,

                                                 &cookieSize,

                                                 magicCookie

                                                 );

        

        

        //오디오 데이터에서 불러온 magic cookie 정보를 AudioQueue에게 전달한다.

        AudioQueueSetProperty(

                                                  playerStat_.mQueue,

                                                  kAudioQueueProperty_MagicCookie,

                                                  magicCookie,

                                                  cookieSize);

        

        free(magicCookie);

    }

    

    

#pragma *************     STEP 8 ALLOCATE AUDIOQUEUEBUFFERS 함수 구현 (setup buffer queus)     *******

    // step 6:

    /*

     Step 4 에서 오디오 버퍼의 사이즈만 계산하고 실제로 오디오 큐 버퍼를 생성한 적은 없다.

     오디오 AudioQueueAllocateBuffer 함수를 이용해 큐 버퍼를 생성하고 세팅.

     kNumberBuffers은 Step 0에서 3개로 세팅하였다.

     사실 버퍼 갯수는 더 많아도 상관없지만 보편적으로 3개 정도로 맞춘다.

     (스트리밍의 경우 네크워크의 상태에 따라서 데이터를 많이 받아 놓아야 할 수도 있기 때문에

     더 많은 것이 일반적이지만 로컬 파일 재생의 경우는 3개로도 충분하다.)

     */

    playerStat_.mCurrentPacket = 0;

    for (int i = 0 ; i < kNumberBuffers; ++i) {

        

        AudioQueueAllocateBuffer(

                                                     playerStat_.mQueue,                                    //버퍼를 할당할 오디오 큐

                                                     playerStat_.bufferByteSize,                          //버퍼의 사이즈(크기)

                                                     &playerStat_.mBuffers[i]                               //새로생긴 버퍼의 포인터

                                                    );

        

        //AudioQueueAllocateBuffer 함수를 이용해서 Buffer를 생성한 뒤 impAudioQueueOutputCallback 함수를 불러주었다.

        //이렇게 하는 이유는 AudioQueue 가 실제로 start 하기 전에 버퍼에 데이터를 미리 채워서 enqueue 해 놓으려는 의도이다.

        impAudioQueueOutputCallback(&playerStat_, playerStat_.mQueue, playerStat_.mBuffers[i]);

    }

    

    //불륨

    Float32 gain = 1.0;

   AudioQueueSetParameter(

                                                  playerStat_.mQueue,

                                                  kAudioQueueParam_Volume,

                                                  gain

                                                );

    

    

    return  YES;

}






//해제

//파일 재생을 마쳤을 때는 꼭 AudioFileID와 AudioQueue를 정리해 줘야 한다.

//AudioQueueDispose 함수의 두번째 인자는 dispose를 바로 진행할 지, enqueue 된 버퍼를 전부 소진 한 후 dispose 할지 결정한다.

-(BOOL)dipose{

     NSLog(@"dipose 진입!");

    if (playerStat_.mQueue) {

        NSLog(@"AudioQueueDispose  실행!");

        AudioQueueDispose(playerStat_.mQueue, YES);

    }

    if (playerStat_.mAudioFile) {

        NSLog(@"AudioFileClose  실행!");

        AudioFileClose(playerStat_.mAudioFile);

    }

    

    if (playerStat_.mPacketDescs) {

         NSLog(@" playerStat_.mPacketDescs = NULL  실행!");

        free(playerStat_.mPacketDescs);

        playerStat_.mPacketDescs = NULL;

    }

    

    return YES;

}



#pragma **************************     STEP 9,10  START PLAYING ,   FINISH PLAYING    ********************


//준비 버튼 클릭

- (IBAction)onPrepare:(id)sender {

     NSLog(@"onPrepare 버튼 클릭!");

    static BOOL once = NO;

    if (!once) {

        

        if (![self prepareAudioFile]) {

            NSLog(@"prepareAudioFile 오류발생!");

            return;

        }

        

        [_prepareBtn setTitle:@"Dispose" forState:UIControlStateNormal];

        once = YES;

    }else{

        

        if (![self dipose]) {

            NSLog(@"dipose 오류발생!");

            return;

        }

        [_prepareBtn setTitle:@"Prepare" forState:UIControlStateNormal];

        once = NO;

        

    }

    

    

}


//플레이 시작

- (IBAction)onPlay:(id)sender {

 NSLog(@"onPlay  버튼 클릭!");

    static BOOL once = NO;

    

    if (!once) {

        [self play];

        [_playBtn setTitle:@"STOP" forState:UIControlStateNormal];

        once = YES;

    }else{

        [self stop];

        [_playBtn setTitle:@"PLAY" forState:UIControlStateNormal];

        once = NO;

    }

}



- (IBAction)onPickMusic:(id)sender {

    [self presentViewController:_mpPickerVC animated:YES completion:^{

       NSLog(@"onPickMusic 오픈!");

    }];

}


- (IBAction)onClose:(id)sender {

    [self dismissViewControllerAnimated:YES completion:nil];

}



#pragma mark  MPMediaPickerControllerDelegate


- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {

    if (NULL == mediaItemCollection) {

        NSLog(@"mediaItemCollection 이 존재하지 않습니다. NULL");

        return ;

    }

    for (MPMediaItem *item in [mediaItemCollection items]) {

        if (NULL == item) {

            NSLog(@"아이템이 존재하지 않습니다. NULL");

            continue;

        }

        NSString *title = [item valueForKey:MPMediaItemPropertyTitle];

        _musicURL = [item valueForKey:MPMediaItemPropertyAssetURL];

        NSLog(@"선택된 곡은 : %@ with url %@ 가수는  %@", title, _musicURL, [item valueForKey:MPMediaItemPropertyArtist]);

        break ;

    }

    [_mpPickerVC dismissViewControllerAnimated:YES completion:^{

        //

    }];

}


- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {

    NSLog(@"Cancel");

    [_mpPickerVC dismissViewControllerAnimated:YES completion:^{

        //

    }];

}



- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}



@end



파일

CoreAudioRecordStream.zip



반응형

댓글