You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin C#安卓开发:如何实现高质量音频录制与实时放大播放?

Xamarin安卓实时音频录播:音质优化与音频放大解决方案

我是一名使用Xamarin和C#进行安卓开发的新手,尝试录制高质量音频并实时放大播放。目前已通过AudioRecord和AudioTrack实现录播功能,但在音质优化和音频放大方面遇到问题,附上相关代码,恳请指导解决方案。

原代码:

private void RecordAndPlayback()
{
    int recordingSampleRate = AudioTrack.GetNativeOutputSampleRate(Stream.Music);
    recordingSampleRate = System.Math.Max(recordingSampleRate, instance.SelectedSamplingRate);
    bufferSize = AudioRecord.GetMinBufferSize(recordingSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit);

    buffer = new byte[bufferSize];

    audioRecord = new AudioRecord(AudioSource.Mic, recordingSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize);

    audioTrack = new AudioTrack(Stream.Music, recordingSampleRate, ChannelOut.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize, AudioTrackMode.Stream);

    audioTrack.SetPlaybackRate(recordingSampleRate);

    audioRecord.StartRecording();
    audioTrack.Play();

    while (isListening)
    {
        audioRecord.Read(buffer, 0, buffer.Length);
        audioTrack.Write(buffer, 0, buffer.Length);
    }
}

一、音质优化方案

  • 换用优化音频源:将AudioSource.Mic替换为AudioSource.VoiceCommunication,该源自带降噪、回声消除等适配实时语音场景的优化,能直接提升基础音质。
  • 调整缓冲大小:GetMinBufferSize返回的是系统要求的最低缓冲值,实际使用2-3倍的大小(比如bufferSize = AudioRecord.GetMinBufferSize(...) * 2),可以减少卡顿、爆音等问题,同时保证录制与播放的缓冲尺寸一致。
  • 校验采样率兼容性:不要直接取最大采样率,优先用设备支持的标准采样率(44100Hz或48000Hz),可以先检查自定义采样率是否被支持,不支持则 fallback 到系统默认值。
  • 后台线程执行:直接在主线程运行录播逻辑会阻塞UI甚至触发ANR,把整个录播逻辑放到后台线程,比如用Task.Run包裹执行。
  • 处理有效数据:读取音频时判断audioRecord.Read的返回值,只有返回正数(实际读取到的字节数)才写入AudioTrack,避免无效数据导致杂音。

二、音频放大实现

因为使用的是16位PCM编码,每个音频样本是short类型(范围-32768到32767),需要将字节数组转换为短整型数组,乘以增益系数后再转回字节数组,同时做限幅处理防止失真:

  1. 定义增益系数(比如float gain = 2.0f,可根据实际需求调整)
  2. 将字节数组转换为短整型数组(注意安卓为小端字节序)
  3. 对每个样本乘以增益,超出范围的截断到最大值/最小值
  4. 转换回字节数组后写入AudioTrack

优化后的完整代码示例

private AudioRecord audioRecord;
private AudioTrack audioTrack;
private byte[] buffer;
private int bufferSize;
private bool isListening;
// 可调整的增益系数
private float gain = 2.5f;

public void StartRecordAndPlayback()
{
    // 后台线程执行,避免阻塞UI
    Task.Run(() => RecordAndPlaybackInternal());
}

private void RecordAndPlaybackInternal()
{
    int targetSampleRate = instance.SelectedSamplingRate;
    // 校验采样率是否支持,不支持则用系统默认值
    if (!IsSampleRateSupported(targetSampleRate))
    {
        targetSampleRate = AudioTrack.GetNativeOutputSampleRate(Stream.Music);
    }

    // 用2倍最小缓冲提升稳定性
    bufferSize = AudioRecord.GetMinBufferSize(targetSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit) * 2;
    buffer = new byte[bufferSize];

    // 使用带优化的语音源
    audioRecord = new AudioRecord(AudioSource.VoiceCommunication, targetSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize);
    audioTrack = new AudioTrack(Stream.Music, targetSampleRate, ChannelOut.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize, AudioTrackMode.Stream);

    audioRecord.StartRecording();
    audioTrack.Play();

    isListening = true;
    while (isListening)
    {
        int bytesRead = audioRecord.Read(buffer, 0, buffer.Length);
        if (bytesRead > 0)
        {
            // 应用音频增益
            ApplyGain(buffer, bytesRead);
            audioTrack.Write(buffer, 0, bytesRead);
        }
        // 短暂休眠降低CPU占用
        Thread.Sleep(10);
    }

    // 停止并释放资源,避免内存泄漏
    audioRecord.Stop();
    audioTrack.Stop();
    audioRecord.Release();
    audioTrack.Release();
}

private void ApplyGain(byte[] audioBytes, int length)
{
    // 16位PCM每2字节对应一个short样本
    int sampleCount = length / 2;
    short[] samples = new short[sampleCount];
    Buffer.BlockCopy(audioBytes, 0, samples, 0, length);

    for (int i = 0; i < sampleCount; i++)
    {
        // 应用增益
        float amplified = samples[i] * gain;
        // 限幅处理,防止超出short范围导致失真
        if (amplified > short.MaxValue)
            samples[i] = short.MaxValue;
        else if (amplified < short.MinValue)
            samples[i] = short.MinValue;
        else
            samples[i] = (short)amplified;
    }

    // 转换回字节数组
    Buffer.BlockCopy(samples, 0, audioBytes, 0, length);
}

// 检查采样率是否被当前设备支持
private bool IsSampleRateSupported(int sampleRate)
{
    try
    {
        int minBufferSize = AudioRecord.GetMinBufferSize(sampleRate, ChannelIn.Mono, Android.Media.Encoding.Pcm16bit);
        return minBufferSize != AudioRecord.ErrorBadValue;
    }
    catch
    {
        return false;
    }
}

额外注意事项

  • 权限申请:确保在AndroidManifest.xml中添加RECORD_AUDIO和MODIFY_AUDIO_SETTINGS权限,并且在运行时动态申请RECORD_AUDIO权限。
  • 增益测试:增益过高会导致音频失真,建议从1.5f开始逐步测试,找到适合的数值。
  • 资源释放:停止录播时必须调用Release()释放AudioRecord和AudioTrack资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Shrishail V H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 00:01:16