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

coreAudio recording

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

coreAudio recording



.h



#import <UIKit/UIKit.h>

#import <AudioToolbox/AudioToolbox.h>

#import <CoreAudioKit/CoreAudioKit.h>

#import <MediaPlayer/MediaPlayer.h>

#import <AVFoundation/AVFoundation.h>


//오디오 큐 서비스를 사용하는 클래스 정의




enum {

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

    kNumberPackages = 10* 1000,

};



struct RecorderStat

{

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

    AudioQueueRef                           mQueue;                                               // 재생오디오큐

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

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

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

    SInt64                                          mCurrentPacket;                                  //다음 재생할 패킷의 위치

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

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

};



@interface ViewController : UIViewController <AVAudioPlayerDelegate>


@end


.m

//


#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)



//콜백 함수

static void impAudioQueueInputCallback(

                                       void * inUserData,                                                                       //AudioQueueNewInput 으로부터 전달된 사용자 데이터

                                       AudioQueueRef inAQ,                                                                 //콜백을 부른 오디오 큐

                                       AudioQueueBufferRef inBuffer,                                                  //채워야 할 버퍼

                                       const AudioTimeStamp * inStartTime,

                                       UInt32 inNumberPacketDescription,                                         //실제 읽은 packet 개수 (output)

                                       const AudioStreamPacketDescription *inPacketDescs)          //aspd array의 포인터, cbr인 경우 null

{

    

    NSLog(@"impAudioQueueInputCallback 진입");

    

    struct RecorderStat *recorderStat = (struct RecorderStat *) inUserData;

    

    //레코딩이 재생 중이 아니라면

    NSLog(@"recoderStat.Ruing : %@" , recorderStat->mIsRunning ? @"true" : @"false");

    if (!recorderStat->mIsRunning) {

        OSStatus stts = AudioQueueStop(recorderStat->mQueue, true);

        VStatus(stts, @"AudioQueueStop error!");

        AudioFileClose(recorderStat -> mAudioFile);

        return;

    }

    

     //for CBR

    if (0 == inNumberPacketDescription && recorderStat-> mDataFormat.mBytesPerPacket != 0) {

        

        inNumberPacketDescription = recorderStat -> bufferByteSize/recorderStat -> mDataFormat.mBytesPerPacket;

    }

    

    //패킷을 파일에 기록

    OSStatus stt = AudioFileWritePackets(

                                         recorderStat->mAudioFile,              //데이터를 기록할 AudioFile

                                         FALSE,                                                 //cache를 할지 말지

                                         recorderStat->bufferByteSize,       // 데이터의 길이 (input)

                                         inPacketDescs,                                //aspd array의 포인터, cbr인 경우 null

                                         recorderStat->mCurrentPacket,     //기록할 패킷의 위치

                                         &inNumberPacketDescription,        //실제 읽은 packet 개수 (output)

                                         inBuffer->mAudioData);                 //채울 오디오 버퍼?

    VStatus(stt, @"AudioFileWritePackets Error!!");

    

    //패킷의 인덱스 증가

    recorderStat->mCurrentPacket += inNumberPacketDescription;  //현재 읽고 있는 packet의 위치

    

    

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

    stt = AudioQueueEnqueueBuffer(

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

                                  inBuffer,                                   //넣을 버퍼

                                  0,                                              //aspd 의 갯수, cbr == 0

                                  NULL);

    VStatus(stt, @"AudioQueueEnqueueBuffer Error!!");

}




@interface ViewController (){

    struct RecorderStat recorderStat_;

}


@property (strong, nonatomic) IBOutlet UIButton *prepareBtn;        //녹음을 위한 준비 버튼

@property (strong, nonatomic) IBOutlet UIButton *recordBtn;         //녹음하기 버튼


@property (strong,nonatomic) NSString *filePath;

@property(strong,nonatomic) AVAudioPlayer *player;


@end


@implementation ViewController


- (void)viewDidLoad {

    [super viewDidLoad];


}



//오디오 레코더 셋팅 (준비)

-(BOOL) prepareAudioRecorder{

    NSLog(@"prepareAudioRecorder 진입");

    

    

    //파일 경로 지정

    NSString *docDir  =    [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

    //데이트 포멧으로 파일명 지정

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];

    [dateFormatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss"];

    _filePath = [NSString stringWithFormat:@"%@/%@.wav" , docDir, [dateFormatter stringFromDate:[NSDate date]]];

    NSLog(@"startRecord _filePath : %@ ", _filePath);

    

    

    if (recorderStat_.mCurrentPacket) {

         NSLog(@"prepareAudioRecorder - mCurrentPacket 두번째 이상 녹음입니다. -  현재 패킷 초기화!!");

        recorderStat_.mCurrentPacket = 0;

    }

    

    

    OSStatus stts = noErr;

    

    // step 1: 레코딩 포멧 셋팅

    recorderStat_.mDataFormat.mFormatID = kAudioFormatLinearPCM;

    recorderStat_.mDataFormat.mSampleRate = 44100.0;

    recorderStat_.mDataFormat.mChannelsPerFrame = 2;

    recorderStat_.mDataFormat.mBitsPerChannel = 16;

    recorderStat_.mDataFormat.mFramesPerPacket = 1;

    

    recorderStat_.mDataFormat.mBytesPerFrame = recorderStat_.mDataFormat.mChannelsPerFrame *

    recorderStat_.mDataFormat.mBitsPerChannel / 8;

    


    recorderStat_.mDataFormat.mBytesPerPacket = recorderStat_.mDataFormat.mBytesPerFrame *

    recorderStat_.mDataFormat.mFramesPerPacket;


    recorderStat_.mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger |

    kAudioFormatFlagIsBigEndian;

    

    //mBytesPerFrame : 4  , mBytesPerPacket : 4

    NSLog(@"prepareAudioRecorder - mBytesPerFrame : %d  , mBytesPerPacket : %d" ,

          recorderStat_.mDataFormat.mBytesPerFrame, recorderStat_.mDataFormat.mBytesPerPacket);

    

    /*

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

     하드웨어의 일부라 함은 보통 스피커나 마이크를 말한다.

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

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

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

     */

 //step 2: audio input queue 생성

  stts = AudioQueueNewInput(

                        &recorderStat_.mDataFormat,

                       impAudioQueueInputCallback,          //콜백 함수

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

                       CFRunLoopGetCurrent(),                   //inCallbackRunLoop

                       kCFRunLoopCommonModes,            //inCallbackRunLoopMode

                       0,

                       &recorderStat_.mQueue);                  //input AudioQueue


    VStatusBOOL(stts, @"AudioQueueNewInput");

    

    

     // step 3: 상세한 포멧 정보

 UInt32 dataFormatSize = sizeof(recorderStat_.mDataFormat);

   stts =  AudioQueueGetProperty(

                          recorderStat_.mQueue,

                          kAudioQueueProperty_StreamDescription,

                          &recorderStat_.mDataFormat,

                          &dataFormatSize);

    

    //prepareAudioRecorder - dataFormatSize : 40

    NSLog(@"prepareAudioRecorder - dataFormatSize : %d" , dataFormatSize);

    VStatusBOOL(stts, @"AudioQueueGetProperty-AudioQueueGetProperty");

    

    


 // step 4: 오디오 파일 생성

  NSURL *tempURL = [NSURL URLWithString:_filePath];

    CFURLRef url = (__bridge CFURLRef)(tempURL);

    

    stts = AudioFileCreateWithURL(

                           url,

                           kAudioFileAIFFType,

                           &recorderStat_.mDataFormat,

                           kAudioFileFlags_EraseFile,

                           &recorderStat_.mAudioFile);

    

    VStatusBOOL(stts, @"AudioFileOpenURL");

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

    

    // step 5: 버퍼와 버퍼 큐 준비

    /*

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

     이 버퍼를 생성하기 전에 재생할 오디오 데이터를 분석해서 어떤 사이즈의 버퍼를 사용할 지, 한 번에 몇개의 packet을 읽을지 결정해야 한다.

     다음은 사용할 버퍼의 사이즈를 bufferByteSize에 저장한다.

     */

    

    //prepareAudioRecorder - kNumberPackages : 10000  ,  mBytesPerPacket : 4

    NSLog(@"prepareAudioRecorder - kNumberPackages : %d  ,  mBytesPerPacket : %d" , kNumberPackages , recorderStat_.mDataFormat.mBytesPerPacket);

    

    

    recorderStat_.bufferByteSize = kNumberPackages * recorderStat_.mDataFormat.mBytesPerPacket;

    

    //prepareAudioRecorder - bufferByteSize : 40000

    NSLog(@"prepareAudioRecorder - bufferByteSize : %d " , recorderStat_.bufferByteSize);

    

    /*

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

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

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

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

     로컬 파일 재생의 경우는 3개로도 충분하다.)

     */

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

        

         NSLog(@"prepareAudioRecorder - for문 안 (%d) ", i);

        

        AudioQueueAllocateBuffer(

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

                                 recorderStat_.bufferByteSize,              //버퍼의 사이즈

                                 &recorderStat_.mBuffers[0]);               //새로생긴 버퍼의 포인터

        

        /*

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

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

       */

        

        /*

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

         */

         AudioQueueEnqueueBuffer(

                                 recorderStat_.mQueue,                      //버퍼를 넣을 오디오 큐

                                 recorderStat_.mBuffers[i],                 //넣을 버퍼

                                 0,                                                         //ASPD 의 갯수 , cbr == 0

                                 NULL);                                                 //ASPD array의 포인터

    }

    

    

    return YES;

}



// 오디오 Queue와 오디오 File 해제!

-(BOOL) disposeAudioRecorder {

    NSLog(@"disposeAudioRecorder 진입");

    

    

    if (recorderStat_.mQueue) {

        NSLog(@"disposeAudioRecorder -  mQueue 해제");

        //AudioQueueStop(recorderStat_.mQueue, true);

        AudioQueueDispose(recorderStat_.mQueue, false);

      

        recorderStat_.mQueue = NULL;

    }

    

    

    if (recorderStat_.mAudioFile) {

        NSLog(@"disposeAudioRecorder -  mAudioFile 해제");

        AudioFileClose(recorderStat_.mAudioFile);

        recorderStat_.mAudioFile = NULL;

    }

    

    return YES;

}


- (IBAction)onPrepare:(id)sender {

    NSLog(@"onPrepare 진입 -  오디오 레코더 셋팅 or 오디오 Queue와 오디오 File 해제 할것임.");

    static BOOL once = NO;

    

    //once 가 No 가 아닐경우(처음에는 NO)

    if (!once) {

        //오디오 레코더 셋팅 (준비)

        if (![self prepareAudioRecorder]) {

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

            return;

        }

        

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

        once = YES;

        

    }else{

        

        // 오디오 Queue와 오디오 File 해제!

        if (![self disposeAudioRecorder]) {

            NSLog(@"disposeAudioRecorder 오류 발생");

            return;

        }

     

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

        once = NO;

    }

}






- (IBAction)onPlay:(id)sender {

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


    [self setAudioSession:2];

    [self preparePlayer:[NSURL URLWithString:_filePath]];

    

  BOOL rst = [_player play];

    NSLog(@"play with %d" , rst);

}



-(BOOL) preparePlayer :(NSURL *)filePath{

    NSLog(@"preparePlayer 메소드 진입=");

    NSError *error = nil;

    _player = [[AVAudioPlayer alloc]initWithContentsOfURL:filePath error:&error];

    if (error != nil) {

       NSLog(@"create player[%@] error:%@", filePath, error.localizedDescription);

        return NO;

    }

    

    _player.delegate  = self;

    _player.numberOfLoops = 100;

    BOOL rst = [_player prepareToPlay];

    NSLog(@"preparePlayer - 플레이어  %d. 가 준비되었습니다.", rst);

    NSLog(@"preparePlayer -  파일 길이는  %g   입니다.", _player.duration);

    NSLog(@"preparePlayer -  채널의 수는  %d.   입니다.", _player.numberOfChannels);

    

    return YES;

}



-(void) stopRecord{

     NSLog(@"stopRecord 메소드 진입");

    // category = AVAudioSessionCategorySoloAmbient;

    [self setAudioSession:2];

    recorderStat_.mIsRunning = false;

}



-(void) startRecord{

    NSLog(@"startRecord 메소드 진입 - filePath 경로 지정");

    


    //category = AVAudioSessionCategoryRecord;

    [self setAudioSession:1];

   OSStatus stts = AudioQueueStart(

                    recorderStat_.mQueue,

                    NULL);

    VStatus(stts, @"AudioQueueStart error");

    recorderStat_.mIsRunning = true;

}



//녹음

- (IBAction)onRecord:(id)sender {

    NSLog(@"onRecord 버튼 클릭- ");

    

    static BOOL once = NO;

    if (!once) {

        [self startRecord];

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

        once = YES;

    } else {

        [self stopRecord];

        [_recordBtn setTitle:@"Recording" forState:UIControlStateNormal];

        once = NO;

    }

    

}




//세션 셋팅

-(void)setAudioSession: (int) mode{

    NSLog(@"setAudioSession 메소드 진입 - mode : %d " , mode);

    

    NSError *error;

    AVAudioSession *session = [AVAudioSession sharedInstance];

    

    [session setActive:YES error:&error];

    if (error != nil) {

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

        return;

    }

    

    error = nil;

    NSString *category;

    

    if (mode == 1) {

        category = AVAudioSessionCategoryRecord;

    } else {

        category = AVAudioSessionCategorySoloAmbient;

    }

    

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

    

    if (error != nil) {

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

        return;

    } else {

        NSLog(@"set category : %@ " , category);

    }

}




#pragma mark AVAudioPlayerDelegate

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {

    NSLog(@"audioPlayerDidFinishPlaying 진입");

    NSLog(@"audioPlayerDidFinishPlaying rst: %d", flag);

    [_player stop];

    

}



- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error {

    NSLog(@"audioPlayerDecodeErrorDidOccur 진입");

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

}


- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end



로그


로직순서

준비버튼 -> 레코드 버튼 ->  정지버튼 -> 디스포즈 버튼




<열었을때>

2018-12-18 16:14:27.726930+0900 CoreAudioPlay[36392:2262705] viewDidLoad 진입



<준비버튼 클릭!>

2018-12-18 16:28:11.635205+0900 CoreAudioPlay[36450:2265047] viewDidLoad 진입 - filePath 경로 지정

2018-12-18 16:28:15.347805+0900 CoreAudioPlay[36450:2265047] onPrepare 진입 -  오디오 레코더 셋팅 or 오디오 Queue와 오디오 File 해제

2018-12-18 16:28:15.347981+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder 진입

2018-12-18 16:28:15.348042+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - mBytesPerFrame : 4  , mBytesPerPacket : 4

2018-12-18 16:28:15.365912+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - dataFormatSize : 40

2018-12-18 16:28:15.368517+0900 CoreAudioPlay[36450:2265047] open file /var/mobile/Containers/Data/Application/B94E926A-0256-410F-B60C-C33C68ADF602/Documents/void3.wav success!

2018-12-18 16:28:15.368568+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - kNumberPackages : 10000  ,  mBytesPerPacket : 4

2018-12-18 16:28:15.368595+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - bufferByteSize : 40000

2018-12-18 16:28:15.368653+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - for문 안 (0)

2018-12-18 16:28:15.369033+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - for문 안 (1)

2018-12-18 16:28:15.369194+0900 CoreAudioPlay[36450:2265047] prepareAudioRecorder - for문 안 (2)



<레코드 버튼 클릭!>

2018-12-18 16:14:36.135516+0900 CoreAudioPlay[36392:2262705] onRecord 버튼 클릭-

2018-12-18 16:14:36.135618+0900 CoreAudioPlay[36392:2262705] startRecord 메소드 진입

2018-12-18 16:14:36.135654+0900 CoreAudioPlay[36392:2262705] setAudioSession 메소드 진입 - mode : 1

2018-12-18 16:14:36.321801+0900 CoreAudioPlay[36392:2262705] set category : AVAudioSessionCategoryRecord

2018-12-18 16:14:36.344158+0900 CoreAudioPlay[36392:2262705] [avas] AVAudioSessionPortImpl.mm:56:ValidateRequiredFields: Unknown selected data source for Port 스피커 (type: Speaker)

2018-12-18 16:14:36.574860+0900 CoreAudioPlay[36392:2262705] impAudioQueueInputCallback 진입

2018-12-18 16:14:36.574929+0900 CoreAudioPlay[36392:2262705] recoderStat.Ruing : true

2018-12-18 16:14:41.684033+0900 CoreAudioPlay[36392:2262705] recoderStat.Ruing : true

2018-12-18 16:14:41.916060+0900 CoreAudioPlay[36392:2262705] impAudioQueueInputCallback 진입

2018-12-18 16:14:41.916213+0900 CoreAudioPlay[36392:2262705] recoderStat.Ruing : true

2018-12-18 16:14:46.188503+0900 CoreAudioPlay[36392:2262705] impAudioQueueInputCallback 진입

2018-12-18 16:14:46.188669+0900 CoreAudioPlay[36392:2262705] recoderStat.Ruing : true

2018-12-18 16:14:46.397709+0900 CoreAudioPlay[36392:2262705] impAudioQueueInputCallback 진입

2018-12-18 16:14:46.397899+0900 CoreAudioPlay[36392:2262705] recoderStat.Ruing : true





<onRecord 버튼 한번더 클릭 (stop)>

2018-12-18 16:14:46.603850+0900 CoreAudioPlay[36392:2262705] onRecord 버튼 클릭-

2018-12-18 16:14:46.604029+0900 CoreAudioPlay[36392:2262705] stopRecord 메소드 진입

2018-12-18 16:14:46.604109+0900 CoreAudioPlay[36392:2262705] setAudioSession 메소드 진입 - mode : 2

2018-12-18 16:14:47.142569+0900 CoreAudioPlay[36392:2262705] set category : AVAudioSessionCategorySoloAmbient

2018-12-18 16:14:47.150925+0900 CoreAudioPlay[36392:2262705] impAudioQueueInputCallback 진입

2018-12-18 16:14:47.151186+0900 CoreAudioPlay[36392:2262705] recoderStat.Ruing : false

2018-12-18 16:14:47.196723+0900 CoreAudioPlay[36392:2262705] [avas] AVAudioSessionPortImpl.mm:56:ValidateRequiredFields: Unknown selected data source for Port 스피커 (type: Speaker)



<play button 클릭>

2018-12-18 16:14:49.653140+0900 CoreAudioPlay[36392:2262705] onPlay 버튼 클릭

2018-12-18 16:14:49.653260+0900 CoreAudioPlay[36392:2262705] setAudioSession 메소드 진입 - mode : 2

2018-12-18 16:14:49.653852+0900 CoreAudioPlay[36392:2262705] set category : AVAudioSessionCategorySoloAmbient

2018-12-18 16:14:49.654001+0900 CoreAudioPlay[36392:2262705] preparePlayer 메소드 진입=

2018-12-18 16:14:49.664863+0900 CoreAudioPlay[36392:2262705] preparePlayer - 플레이어  1. 가 준비되었습니다.

2018-12-18 16:14:49.664973+0900 CoreAudioPlay[36392:2262705] preparePlayer -  파일 길이는  10.0404   입니다.

2018-12-18 16:14:49.665012+0900 CoreAudioPlay[36392:2262705] preparePlayer -  채널의 수는  2.   입니다.

2018-12-18 16:14:49.673648+0900 CoreAudioPlay[36392:2262705] play with 1





녹음하기 버튼 클릭    -> 준비 버튼 자동으로 눌림   ..... 녹음 .....



-> 정지 버튼 클릭 -> 디스 포즈 버튼 자동으로 눌림



용어


- mBytesPerFrame : 프레임 당 바이트

*설명
오디오 버퍼에서 한 프레임의 시작에서 다음 프레임의 시작까지의 바이트 수.
압축된 형식의 경우 0으로 설정한다.

*예)
n개의 채널에 대해 인터리브 된 데이터가 들어있는 오디오 버퍼의 경우, 각 유형의 샘플과 함께 다음과 같이 이 필드값을 계산한다.
mBytesPerFrame = n * sizeof (AudioSampleType);


비 인터리브(모노포닉)데이터를 포함하고 샘플을 사용하는 오디오 버퍼의 경우 이 필드값을 아래와 같이 계산한다.
mBytesPerFrame = sizeof (AudioSampleType);


- mBytesPerPacket
*설명
패킷당 바이트 수. 오디오 데이터 패킷의 바이트 수. 가변 패킷 크기를 표시하려면 필드를 0으로 설정.
가변 패킷 크기를 사용하는 형식의 경우 struct 를 사용하여 각패킷의 크기를 결정한다.
AudioStreamPacketDescription


- mFormatID
*설명
스트림의 일반적인 오디오 데이터 형식을 지정하는 식별자.


- mFramesPerPacket
*설명
패킷당 프레임 수. 오디오 데이터 패킷의 프래임수.
비트압축 오디오의 경우 1.
가변비트전송률 형식의 경우 이 값은 aac에 대해 1024 처럼 더 큰 값이다.
OggVorbis처럼 패킷 당 프레임 수가 가변적인 경우 0으로 설정한다.



- mSampleRate
*설명
데이터 스트림의 초당 프레임수. 압축된 형식의 경우 이 필드는 동등한 압축해제된 데이터의 초당 프레임 수를 나타낸다.

- mChannelsPerFrame
*설명
프레임당 채널 수






참조

https://cz-it.gitbooks.io/play-and-record-with-coreaudio/content/audiotoolbox/audioqueue/recorder.html


https://github.com/cz-it/play_and_record_with_coreaudio


http://mm.sookmyung.ac.kr/~bigrain/class/2010/


http://mm.sookmyung.ac.kr/~bigrain/class/2010/sound2/AudioQueue_Recording02.key.pdf


https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/CoreAudioOverview/CoreAudioEssentials/CoreAudioEssentials.html#//apple_ref/doc/uid/TP40003577-CH10-SW1


반응형

댓글