服务注册中心 MongoDB 微信直播 stl tags electron安装 内存计算 idea批量替换快捷键 cad正在执行命令 wps文件修复工具下载 python功能 python算法 python文件操作 windows搭建python开发环境 python怎么使用 python写入txt文件 java字符串 java删除数组中的元素 java在线课程 java语言简介 java遍历集合 java时间格式 java时间格式化 java删除数组中的某个元素 php实例代码 win7loader microkms 亚索刀光特效包 phpqrcode pdf拆分工具 数据挖掘原理与算法 mssql 微信临时链接多久失效 正当防卫4存档 方正兰亭粗黑字体下载 vue定时器 设备管理器在哪 vs2008中文版下载 摇骰子表情包 鼠标速度怎么调
当前位置: 首页 > 学习教程  > 

PCM音频播放器模组(iOS)

2020/10/16 17:47:49 文章标签: pcm接口

PCM音频播放器在网上已经有较多的教程及代码,各有千秋,在此不再做过多的描述和讲解。 此文章及代码是基于iOS原生系统的接口进行扩展和封装的,支持各种PCM采样率,支持音频数据缓存,支持PCM纯数据流及CMSampleBufferRef…

PCM音频播放器在网上已经有较多的教程及代码,各有千秋,在此不再做过多的描述和讲解。
此文章及代码是基于iOS原生系统的接口进行扩展和封装的,支持各种PCM采样率,支持音频数据缓存,支持PCM纯数据流及CMSampleBufferRef结构接口。此模块仅支持单声道,稍有遗憾。
PCM播放器确实是比较基础的东西,代码已经过长期验证及测试,可直接拿来使用及参考。若有优化及漏洞,尽情留言告知,非常感谢!

//
//  AudioPCMPlayer.h
//
//  Created by lizhijian on 2017/2/24.
//  Copyright © 2017年 ZJ. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <CoreMedia/CoreMedia.h>

@interface AudioPCMPlayer : NSObject

/**
 初始化PCM音频播放器,默认已启动播放

 @param sampleRate 采样率
 @param channels 通道数
 @return obj
 */
- (instancetype)initWithSampleRate:(Float64)sampleRate channels:(UInt32)channels;

/**
 开始播放

 @return YES:已启动
 */
- (BOOL)start;

/**
 停止播放并释放资源
 */
- (void)stop;

/**
 填入需要播放的PCM音频数据

 @param data PCM数据
 @param length PCM音频数据长度
 @return 实际读取PCM数据的长度
 */
- (NSInteger)play:(const char *)data length:(NSInteger)length;

/**
 填入需要播放的PCM采样数据

 @param sampleBuffer 采样Buffer
 @return 实际读取PCM数据的长度
 */
- (NSInteger)play:(CMSampleBufferRef)sampleBuffer;

- (BOOL)isValid;

/**
 获取最大Buf缓存大小

 @return 最大缓存大小
 */
- (NSInteger)getBufMaxLength;

@end

//
//  AudioPCMPlayer.m
//
//  Created by lizhijian on 2017/2/24.
//  Copyright © 2017年 ZJ. All rights reserved.
//

#import "AudioPCMPlayer.h"
#import <Foundation/Foundation.h>

#define PCM_QUEUE_BUFFER_SIZE 10   //队列缓冲个数(队列过多将影响播放时效)

@interface AudioPCMPlayer ()
{
    AudioQueueRef mAudioQueue;
    AudioQueueBufferRef mAudioBufferRef[PCM_QUEUE_BUFFER_SIZE];
    u_char *pPCMData;
    int mDataLen;

    int mPCMBufferSize;     //单次加载的PCM数据量
    int mPCMMaxBufferSize;  //总缓存区可存放的PCM数据量
}

@property (nonatomic,assign) Float64 sampleRate;
@property (nonatomic,assign) UInt32 channels;

@property (nonatomic,strong) NSCondition *audioLock;

@end

@implementation AudioPCMPlayer

- (instancetype)initWithSampleRate:(Float64)sampleRate channels:(UInt32)channels
{
    if (self = [super init]) {
        mAudioQueue = nil;
        _channels = 1;  //不支持双声道,强制单声道
        _sampleRate = sampleRate;
        _audioLock = [[NSCondition alloc] init];

        mPCMBufferSize = (int)sampleRate / 8000 * 320;
        mPCMMaxBufferSize = mPCMBufferSize * PCM_QUEUE_BUFFER_SIZE * 5;

        pPCMData = (u_char *)malloc(mPCMMaxBufferSize);

        [self start];
    }
    
    return self;
}

- (void)dealloc
{
    [self stop];

    if (pPCMData) {
        free(pPCMData);
        pPCMData = NULL;
    }
    mDataLen = 0;   //重置缓冲区长度
}

- (BOOL)start
{
    @synchronized (self) {
        if (mAudioQueue) {
            return YES;
        }
        
        ///设置音频参数
        AudioStreamBasicDescription audioDescription = {0};
        audioDescription.mSampleRate = self.sampleRate; //采样率
        audioDescription.mFormatID = kAudioFormatLinearPCM;
        audioDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsNonInterleaved;
        audioDescription.mChannelsPerFrame = self.channels; ///单声道
        audioDescription.mFramesPerPacket = 1; //每一个Packet包含一侦数据
        audioDescription.mBitsPerChannel = 16; //每个采样点16bit量化
        audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel / 8) * audioDescription.mChannelsPerFrame;
        audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame * audioDescription.mFramesPerPacket;
        
        OSStatus status = AudioQueueNewOutput(&audioDescription, didAudioQueueOutputCallback, (__bridge void *)(self), nil, nil, 0, &mAudioQueue);
        if (status != noErr) {
            NSLog(@"Init Audio PCM Player failed:%@ SampleRate:%f Channels:%u", NSError(status), self.sampleRate, (unsigned int)self.channels);
            return NO;
        }
        
        for (int i=0; i<PCM_QUEUE_BUFFER_SIZE; i++) {
            AudioQueueAllocateBuffer(mAudioQueue, mPCMBufferSize, &mAudioBufferRef[i]);
            memset(mAudioBufferRef[i]->mAudioData, 0, mPCMBufferSize);
            mAudioBufferRef[i]->mAudioDataByteSize = mPCMBufferSize;     //指定每包数据长度
            AudioQueueEnqueueBuffer(mAudioQueue, mAudioBufferRef[i], 0, NULL);  //队列的数据清零
        }
        AudioQueueStart(mAudioQueue, NULL);
        
        NSLog(@"Init Audio PCM Player success.");
        return YES;
    }
}

- (void)stop
{
    @synchronized (self) {
        if (mAudioQueue) {
            AudioQueueStop(mAudioQueue, YES);
            for (int i = 0; i < PCM_QUEUE_BUFFER_SIZE; i++) {
                AudioQueueFreeBuffer(mAudioQueue, mAudioBufferRef[i]);
            }
            AudioQueueDispose(mAudioQueue, YES);
            mAudioQueue = nil;
            NSLog(@"Stop Audio PCM Player.");
        }
        
        if (pPCMData) {
            memset(pPCMData, 0, mPCMMaxBufferSize);
        }
        mDataLen = 0;   //重置缓冲区长度
    }
}

- (NSInteger)play:(const char *)pcmData length:(NSInteger)length
{
    if (mAudioQueue == nil || pcmData == NULL || length <= 0) {
        return 0;
    }
    [self.audioLock lock];
    
    NSInteger ret = 0;
    NSInteger remainSpace = mPCMMaxBufferSize - mDataLen; //计算剩余空间

    if (remainSpace > 0) {
        if (length <= remainSpace) {
            memcpy(pPCMData + mDataLen, pcmData, length);
            mDataLen += length;
            ret = length;
        } else {
            memcpy(pPCMData + mDataLen, pcmData, remainSpace);
            mDataLen += remainSpace;
            ret = length - remainSpace;
            NSLog(@"PCM player is not enough space:%ld", (long)ret);
        }
    } else {
        NSLog(@"PCM player is not enough space");
    }
    
    [self.audioLock unlock];
    
    return ret;
}

- (NSInteger)play:(CMSampleBufferRef)sampleBuffer
{
    CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
    size_t pcmBufferSize = 0;
    char *pcmBuffer = NULL;
    OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &pcmBufferSize, &pcmBuffer);
    if (status == kCMBlockBufferNoErr) {
        return [self play:pcmBuffer length:pcmBufferSize];
    }
    
    return 0;
}

- (BOOL)isValid
{
    return mAudioQueue?YES:NO;
}

static void didAudioQueueOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
    AudioPCMPlayer *player = (__bridge AudioPCMPlayer *)(inUserData);
    [player handlerOutputAudioQueue:inAQ inBuffer:inBuffer];
}

- (void)handlerOutputAudioQueue:(AudioQueueRef)inAQ inBuffer:(AudioQueueBufferRef)inBuffer
{
    BOOL isFull = NO;
    if (mDataLen >= mPCMBufferSize) {
        [self.audioLock lock];
        memcpy(inBuffer->mAudioData, pPCMData, mPCMBufferSize);  //将需要播放的数据入队
        mDataLen -= mPCMBufferSize;
        memmove(pPCMData, pPCMData + mPCMBufferSize, mDataLen);  //将后面未播放的缓存数据前移
        [self.audioLock unlock];
        isFull = YES;
    }
    
    if (!isFull) {
        memset(inBuffer->mAudioData, 0, mPCMBufferSize);
    }
    
    inBuffer->mAudioDataByteSize = mPCMBufferSize;
    AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}

- (NSInteger)getBufMaxLength
{
    return mPCMMaxBufferSize;
}

@end

本文链接: http://www.dtmao.cc/news_show_300137.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?