使用AAudio获取&输出音频流

AAudio的原理可以参考Android音频API介绍「转载」-CSDN博客
本文主要的目标是从指定的音频输入设备读取音频流,写入指定的音频输出设备。

1,主要流程

  • 配置并创建音频输入流
  • 打开音频输入流
  • 启动音频输入流(设备)
  • 配置并创建音频输出流
  • 打开音频输出流
  • 启动音频输出流(设备)
  • 从音频输入流读取数据,写入音频输出流

2,AAudio的具体NDK代码

下面代码实现了从麦克风收音,输出到场声器或耳机。尽量使用耳机测试,使用外放扬声器和麦克会出现刺耳的声音回授audio feedback。

  • 创建Builder,并配置Builder
    AAudioStreamBuilder *builder;
    aaudio_result_t result = AAudio_createStreamBuilder(&builder);

    AAudioStreamBuilder_setDeviceId(builder, input_device_id);
    AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
    AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
    AAudioStreamBuilder_setSampleRate(builder, 44100);
    AAudioStreamBuilder_setChannelCount(builder, 2);
    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);

  • 用Builder打开音频输入流
    AAudioStream *stream;
    result = AAudioStreamBuilder_openStream(builder, &stream);
  • 启动音频输入流,并等待音频流状态从Starting到Started。
    result = AAudioStream_requestStart(stream);

    int MY_TIMEOUT_NANOS = 100000000;
    result = AAUDIO_ERROR_TIMEOUT;
    aaudio_stream_state_t currentState = AAudioStream_getState(stream);
    aaudio_stream_state_t inputState = currentState;
    while (result == AAUDIO_ERROR_TIMEOUT && currentState != AAUDIO_STREAM_STATE_STARTED) {
        result = AAudioStream_waitForStateChange(
                stream, inputState, &currentState, MY_TIMEOUT_NANOS);
    }
  • 获取流信息
    int32_t deviceId = AAudioStream_getDeviceId(stream);
    input_device_stat += "input device id:" + std::to_string(deviceId) + '\n';
    aaudio_direction_t Direct = AAudioStream_getDirection(stream);
    input_device_stat += "Direct:" + std::to_string(Direct) + '\n';
    aaudio_sharing_mode_t SharingMode = AAudioStream_getSharingMode(stream);
    input_device_stat += "SharingMode:" + std::to_string(SharingMode) + '\n';
    int32_t SampleRate = AAudioStream_getSampleRate(stream);
    input_device_stat += "SampleRate:" + std::to_string(SampleRate) + '\n';
    int32_t ChannelCount = AAudioStream_getChannelCount(stream);
    input_device_stat += "ChannelCount:" + std::to_string(ChannelCount) + '\n';
    aaudio_format_t format = AAudioStream_getFormat(stream);
    input_device_stat += "format:" + std::to_string(format) + '\n';
    int32_t BufferCapacityInFrames = AAudioStream_getBufferCapacityInFrames(stream);
    input_device_stat += "BufferCapacityInFrames:" +  std::to_string(BufferCapacityInFrames) + '\n';
  • 音频输出流代码与输入流类似,这里不重复给出
  • 从音频输入流读取数据,写入音频输出流
    while(Sampling){

        aaudio_result_t readSize = AAudioStream_read(stream, audioBuffer, bufferSize, MY_TIMEOUT_NANOS);

        if (readSize < 0) {
            // Error!
            break;
        }

        int ret = AAudioStream_write(output_stream, audioBuffer, readSize, MY_TIMEOUT_NANOS);
        if (ret < 0) {
            // Error!
            break;
        }

    }
  • 关闭流
    AAudioStream_requestStop(output_stream);
    AAudioStream_close(output_stream);
    AAudioStream_requestStop(stream);
    AAudioStream_close(stream);
    AAudioStreamBuilder_delete(output_builder);
    AAudioStreamBuilder_delete(builder);

这里代码存在的问题是不能重复打开流,可能是设备关闭代码有问题。

3,指定输入/输出音频设备的坑

这里希望使用函数AAudioStreamBuilder_setDeviceId来指定音频的输入和输出设备,例如

    AAudioStreamBuilder_setDeviceId(builder, input_device_id);

官方手册说明了ID是从AudioManager.getDevices()处获取的,也提示了,你指定的设备,有可能设置不成功,并且不会返回错误。要求你自己获取并校验ID的设置。

这里我测试了两款手机:

3.1 Samsung S7 edge

硬件简要说明:手机机身底部麦克风、手机机身顶部麦克风,如果插耳机还有耳机麦克风;输出音频有手机外放、手机听筒、如果插耳机还有耳机;

AudioManager.getDevices()获取的输入设备列表:

AudioManager.getDevices()获取的输出设备列表:

输出设备无论指定什么,都是耳机输出(未插耳机是扬声器)

当指定输入设备为6 “内置麦克风”时,只有底部麦克风收音;

当指定输入设备为8 “通话麦克风”时,无法打开设备;

3.2 红米K40Pro

硬件简要说明:输入音频有手机机身底部麦克、手机机身顶部麦克、手机机身背部麦克风,如果插耳机还有耳机麦克风;输出音频有手机外放、手机听筒、如果插耳机还有耳机;

AudioManager.getDevices()获取的输入设备列表:

AudioManager.getDevices()获取的输出设备列表:

可以看出,硬件设备,AudioManager.getDevices()获取的输入设备并不等同于实际硬件。

测试时输出设备无论指定什么,都是耳机输出(未插耳机是扬声器)

当指定输入设备为17 “内置麦克风bottom”时,4个麦克风都收音;

当指定输入设备为19 “内置麦克风back”时,实测只有底部麦克风收音;

当指定输入设备为18 、20时,无声音;

当指定输入设备为32 “混音设备”时,无法打开设备;

通过向硬件麦克风吹气,可以从耳机中听到声音,能听出这款手机的收音,除了顶部和底部麦克风双声道对应双声道收音,其它都是单声道。

结论

Andoird音频架构中,HAL之上均无法完全控制硬件。AAudio只能在一定程度上指定硬件,并且打开流后,不一定成功打开的是你指定的硬件。

AAudioStreamBuilder_setDeviceId无法指定输出音频设备。

AAudioStreamBuilder_setDeviceId部分类型可以指定输入音频设备,也可能是权限有关系不可用。有单声道(所有麦克输入都是一样输出)和双声道(底部麦克左声道,顶部麦克是右声道)输出,具体不同的体现,应该取决于HAL层的实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值