播放本地pcm數(shù)據(jù)音頻


#define QUEUE_BUFFER_SIZE 4            //隊列緩沖個數(shù)
#define EVERY_READ_LENGTH 1000         //每次從文件讀取的長度
#define MIN_SIZE_PER_FRAME 2000        //每偵最小數(shù)據(jù)長度

@interface OLMyTapesCell ()
{
    AudioStreamBasicDescription audioDescription;             //音頻參數(shù)
    AudioQueueRef audioQueue;                                 //音頻播放隊列
    AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE]; //音頻緩存
    NSLock *synlock ;                                         //同步控制
    Byte *pcmDataBuffer;                                      //pcm的讀文件數(shù)據(jù)區(qū)
    FILE *file;                                               //pcm源文件
    BOOL isStaring;
    
}


// 初始化播放器
- (void)player{
    NSFileManager *fileManger = [NSFileManager defaultManager];
    NSString *filePath = @“文件路徑”;
    
    [[fileManger attributesOfItemAtPath:filePath error:nil] fileSize];
    
    file = fopen([filePath UTF8String],"r");
    if (file) {
        fseek(file, 0, SEEK_SET);
        pcmDataBuffer = malloc(EVERY_READ_LENGTH);
    }else{
        NSLog(@"!!!!!!!!!!!!!!!!");
    }
    synlock = [[NSLock alloc] init];
    
    [self initAudio];
    
    AudioQueueStart(audioQueue, NULL);
    for(int i=0;i<QUEUE_BUFFER_SIZE;i++)
    {
        [self readPCMAndPlay:audioQueue buffer:audioQueueBuffers[i]];
    }
    /*
     audioQueue使用的是驅(qū)動回調(diào)方式,即通過AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);傳入一個buff去播放,播放完buffer區(qū)后通過回調(diào)通知用戶,
     用戶得到通知后再重新初始化buff去播放,周而復始,當然,可以使用多個buff提高效率(測試發(fā)現(xiàn)使用單個buff會小卡)
     */


}



// 開始
-(void)AudioQueueStart
{
    AudioQueueStart(audioQueue, NULL);
    NSLog(@"onbutton2clicked");
}

// 結(jié)束
-(void)AudioQueueStop{
   AudioQueueStop(audioQueue, YES);
}

// 暫停
-(void)AudioQueuePause
{
     AudioQueuePause(audioQueue);
    NSLog(@"onbutton2clicked");
}

#pragma mark player call back
/*
 試了下其實可以不用靜態(tài)函數(shù),但是c寫法的函數(shù)內(nèi)是無法調(diào)用[self ***]這種格式的寫法,所以還是用靜態(tài)函數(shù)通過void *input來獲取原類指針
 這個回調(diào)存在的意義是為了重用緩沖buffer區(qū),當通過AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);函數(shù)放入queue里面的音頻文件播放完以后,通過這個函數(shù)通知
 調(diào)用者,這樣可以重新再使用回調(diào)傳回的AudioQueueBufferRef
 */
static void AudioPlayerAQInputCallback(void *input, AudioQueueRef outQ, AudioQueueBufferRef outQB)
{
    NSLog(@"AudioPlayerAQInputCallback");
    OLMyTapesCell *mainviewcontroller = (__bridge OLMyTapesCell *)input;
    [mainviewcontroller checkUsedQueueBuffer:outQB];
    [mainviewcontroller readPCMAndPlay:outQ buffer:outQB];
}


#pragma mark - 錄音播放
-(void)initAudio
{
    ///設置音頻參數(shù)
    audioDescription.mSampleRate = 16000;//采樣率(每秒鐘采集多少個信號樣本)
    audioDescription.mFormatID = kAudioFormatLinearPCM;
    audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioDescription.mChannelsPerFrame = 1;///單聲道
    audioDescription.mFramesPerPacket = 1;//每一個packet一偵數(shù)據(jù)
    audioDescription.mBitsPerChannel = 16;//每個采樣點16bit量化
    audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame;
    
    audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
    ///創(chuàng)建一個新的從audioqueue到硬件層的通道
    
    //  AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &audioQueue);///使用當前線程播
    
    AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, (__bridge void * _Nullable)(self), nil, nil, 0, &audioQueue);//使用player的內(nèi)部線程播
    ////添加buffer區(qū)
    for(int i=0;i<QUEUE_BUFFER_SIZE;i++)
    {
        int result =  AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);///創(chuàng)建buffer區(qū),MIN_SIZE_PER_FRAME為每一偵所需要的最小的大小,該大小應該比每次往buffer里寫的最大的一次還大
        NSLog(@"AudioQueueAllocateBuffer i = %d,result = %d",i,result);
    }
}

-(void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB
{
    [synlock lock];
    int readLength = fread(pcmDataBuffer, 1, EVERY_READ_LENGTH, file);//讀取文件
    if (readLength == 0) {
        dispatch_async(dispatch_get_main_queue(), ^{
            //  這里可以進行一些UI控件的刷新操作
        });
    }
    NSLog(@"read raw data size = %d",readLength);
    outQB->mAudioDataByteSize = readLength;
    Byte *audiodata = (Byte *)outQB->mAudioData;
    for(int i=0;i<readLength;i++)
    {
        audiodata[i] = pcmDataBuffer[i];
    }
    /*
     將創(chuàng)建的buffer區(qū)添加到audioqueue里播放
     AudioQueueBufferRef用來緩存待播放的數(shù)據(jù)區(qū),AudioQueueBufferRef有兩個比較重要的參數(shù),AudioQueueBufferRef->mAudioDataByteSize用來指示數(shù)據(jù)區(qū)大小,AudioQueueBufferRef->mAudioData用來保存數(shù)據(jù)區(qū)
     */
    AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);
    [synlock unlock];
}

-(void)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf
{
    if(qbuf == audioQueueBuffers[0])
    {
        NSLog(@"AudioPlayerAQInputCallback,bufferindex = 0");
    }
    if(qbuf == audioQueueBuffers[1])
    {
        NSLog(@"AudioPlayerAQInputCallback,bufferindex = 1");
    }
    if(qbuf == audioQueueBuffers[2])
    {
        NSLog(@"AudioPlayerAQInputCallback,bufferindex = 2");
    }
    if(qbuf == audioQueueBuffers[3])
    {
        NSLog(@"AudioPlayerAQInputCallback,bufferindex = 3");
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容