美文网首页
iOS 通过 Audio Unit 播放音频数据

iOS 通过 Audio Unit 播放音频数据

作者: 飛天江郎 | 来源:发表于2019-05-10 14:51 被阅读0次

    关于AudioUnit

    Audio Unit 是iOS系统音频架构的最底层了,这一层架构是最接近硬件层的,也是开发者目前能操作最的层的API架构了。


    Audio Unit

    这里先解释一下DSP(digital signal processing)数字信号处理,音频信号是需要通过设备采样之后变成的数字信号,以方便数据的传输和记录。目前最常用的是PCM格式的音频数据信号,因为这种高保真的信号方便后续的处理,还有就是它保留了数据的完整性。

    关于音频流参数

    1、采样率
    每秒钟采得声音样本的次数,声音是一种能量波,有振幅和频率,人的耳朵可以听到的频率在20-Hz~20kHz之间的声波,所以采样率越高,获取的到的频率信息就更为丰富,由于人耳的分辨率很有限,太高的频率并不能分辨出来。22050 的采样频率是常用的,44100已是CD音质,超过48000或96000的采样对人耳已经没有意义。

    常用的采样率:

    8000 Hz - 电话所用采样率
    22050 Hz - 无线电广播所用采样率
    32000 Hz - miniDV 数码视频 camcorder、DAT (LP mode)所用采样率
    44100 Hz - 音频 CD, 也常用于 MPEG-1 音频(VCD,SVCD,MP3)所用采样率
    47250 Hz - 商用 PCM 录音机所用采样率
    48000 Hz - miniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音所用采样率
    50000 Hz - 商用数字录音机所用采样率
    96000 Hz或者 192000 Hz - DVD-Audio、一些 LPCM DVD 音轨、BD-ROM(蓝光盘)音轨、和 HD-DVD (高清晰度 DVD)音轨所用所用采样率
    

    2、采样位数
    采样位数,他是衡量声音播到变化的一个参数,它的数值越大,分辨率就越高,录制和回放的声音就越接近真实。常见的声卡主要有8位和16位两种,如今市面上所有的主流产品都是16位及以上的声卡。

     每个采样数据记录的是振幅, 采样精度取决于采样位数的大小:
    1 字节(也就是8bit) 只能记录 256 个数, 也就是只能将振幅划分成 256 个等级;
    2 字节(也就是16bit) 可以细到 65536 个数, 这已是 CD 标准了;
    4 字节(也就是32bit) 能把振幅细分到 4294967296 个等级, 实在是没必要了.
    

    3、通道数
    即声音的通道的数目,目前使用较多的是单声道和立体声,相当于从多位置采集声音。
    4、比特率
    每秒的传输速率(位速, 也叫比特率)。如705.6kbps 或 705600bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒705600bit的容量。不同的音频格式编码,对PCM都有一个压缩比,所以比特率一般等于原始比特率/音频压缩比。
    5、帧长
    帧长记录了一个声音单元字节为单位,其长度为:样本长度 * 通道数 = 帧长
    6、帧数
    每秒数据分为都少帧: 帧长 * 帧数 * 8 = 比特率

    Audio Unit 工作时的脑图和流程图

    AudioUnit 涉及到的知识图.png

    整体的一个流程图如下:


    Audio Unit Flow.png

    使用流程

    1、iOS 涉及音频使用和会话都需要使用到 AudioSessionInitialize去初始化音频会话对象。

     // set audio session
        NSError *error = nil;
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
        [audioSession setActive:YES error:&error];
    

    2、配置音频组件Audio Unit 并描述输出的单元

     //set audio component information
        AudioComponentDescription audioDesc;
        audioDesc.componentType = kAudioUnitType_Output;
        audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
        audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
        audioDesc.componentFlags = 0;
        audioDesc.componentFlagsMask = 0;
    

    3、查找、创建对应的音频输出单元组件

     AudioUnit audioUnit;
    //set audio component information
        AudioComponentDescription audioDesc;
        audioDesc.componentType = kAudioUnitType_Output;
        audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
        audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
        audioDesc.componentFlags = 0;
        audioDesc.componentFlagsMask = 0;
    
        //Finds the next component that matches a specified AudioComponentDescription structure after a specified audio component.
        AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc);
        //create a new instance of an audio component
        AudioComponentInstanceNew(inputComponent, &audioUnit);
     //audio property
        UInt32 flag = 1;
        if (flag) {
            status = AudioUnitSetProperty(audioUnit,
                                          kAudioOutputUnitProperty_EnableIO,
                                          kAudioUnitScope_Output,
                                          OUTPUT_BUS,
                                          &flag,
                                          sizeof(flag));
        }
    

    4、配置对应需要播放的音频数据格式内容

     // format
        AudioStreamBasicDescription outputFormat;
        memset(&outputFormat, 0, sizeof(outputFormat));
        outputFormat.mSampleRate       = 44100; // 采样率
        outputFormat.mFormatID         = kAudioFormatLinearPCM; // PCM格式
        outputFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger; // 整形
        outputFormat.mFramesPerPacket  = 1; // 每帧只有1个packet
        outputFormat.mChannelsPerFrame = 1; // 声道数
        outputFormat.mBytesPerFrame    = 2; // 每帧只有2个byte 声道*位深*Packet数
        outputFormat.mBytesPerPacket   = 2; // 每个Packet只有2个byte
        outputFormat.mBitsPerChannel   = 16; // 位深
        [self printAudioStreamBasicDescription:outputFormat];
    
        status = AudioUnitSetProperty(audioUnit,
                                      kAudioUnitProperty_StreamFormat,
                                      kAudioUnitScope_Input,
                                      OUTPUT_BUS,
                                      &outputFormat,
                                      sizeof(outputFormat));
        if (status) {
            NSLog(@"AudioUnitSetProperty eror with status:%d", status);
        }
    

    5、指定播放源的相关信息

        // callback
        AURenderCallbackStruct playCallback;
        playCallback.inputProc = PlayCallback;
        playCallback.inputProcRefCon = (__bridge void *)self;
        AudioUnitSetProperty(audioUnit,
                             kAudioUnitProperty_SetRenderCallback,
                             kAudioUnitScope_Input,
                             OUTPUT_BUS,
                             &playCallback,
                             sizeof(playCallback));
        
        
        OSStatus result = AudioUnitInitialize(audioUnit);
    

    6、给目标播放组件输入播放内容

    
    static OSStatus PlayCallback(void *inRefCon,
                                 AudioUnitRenderActionFlags *ioActionFlags,
                                 const AudioTimeStamp *inTimeStamp,
                                 UInt32 inBusNumber,
                                 UInt32 inNumberFrames,
                                 AudioBufferList *ioData) {
        yourPlayerClass *player = (__bridge yourPlayerClass *)inRefCon;//这里获取之前初始化时配置给播放组件的类对象
       // 这里是静音数据,需要播放更对内容可以往ioData->mBuffers输入数据
      for (int iBuffer = 0; iBuffer < ioData->mNumberBuffers; ++iBuffer) {
            memset(ioData->mBuffers[iBuffer].mData, 0, ioData->mBuffers[iBuffer].mDataByteSize);
        }
       return noErr;
      }
    }
    

    7、这里想要补充说明一下
    上面的配置和初始中,多次用到了AudioUnitSetProperty,这是一个设置音频单元属性的函数,它的几个参数如下:


    参数补充.png

    上面用到了两个宏定义 作用于设置AudioUnit的I/O口设置

    #define INPUT_BUS 1
    #define OUTPUT_BUS 0
    

    相关文章

      网友评论

          本文标题:iOS 通过 Audio Unit 播放音频数据

          本文链接:https://www.haomeiwen.com/subject/zsjooqtx.html