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

C#控制台程序基于NAudio实现音频转字节数组及无头字节数组转音频

在控制台应用中用NAudio实现麦克风录制→字节数组→播放

我直接给你梳理完整的实现方案,结合你已有的代码补充关键逻辑,都是控制台环境能直接跑的:

一、先完善录制逻辑,捕获音频到字节数组

你现有的启动录制代码只是开启了麦克风,但没捕获音频数据。我们需要加个MemoryStream来存录制的原始数据,再通过WaveInEvent的DataAvailable事件把每次的音频缓冲区数据写入流,最后转成字节数组。

修改后的代码片段:

// 用来临时存储录制的音频数据
private MemoryStream _recordedAudioStream = new MemoryStream();
private WaveInEvent _audioSource = null;

// 你的设备获取逻辑保持不变
private List<WaveInCapabilities> _sources = new List<WaveInCapabilities>();
public List<string> Devices = new List<string>();
public List<string> Channels = new List<string>();

public void GetMicDevices()
{
    for(int i = 0; i < WaveIn.DeviceCount; i++)
    {
        _sources.Add(WaveIn.GetCapabilities(i));
    }
    foreach(var device in _sources)
    {
        Devices.Add(device.ProductName);
        Channels.Add(device.Channels.ToString());
    }
}

public void StartMicRecording()
{
    // 重置存储流,避免残留旧数据
    if (_recordedAudioStream != null)
    {
        _recordedAudioStream.Dispose();
        _recordedAudioStream = new MemoryStream();
    }

    int targetDeviceNum = 0; // 可改成用户选择的设备编号
    _audioSource = new WaveInEvent();
    _audioSource.DeviceNumber = targetDeviceNum;
    
    // 明确指定WaveFormat(采样率44100、16位深度、对应设备的通道数)
    var deviceCaps = WaveIn.GetCapabilities(targetDeviceNum);
    _audioSource.WaveFormat = new WaveFormat(44100, 16, deviceCaps.Channels);

    // 订阅数据捕获事件
    _audioSource.DataAvailable += AudioSource_DataAvailable;
    // 订阅录制停止事件,处理后续逻辑
    _audioSource.RecordingStopped += AudioSource_RecordingStopped;

    _audioSource.StartRecording();
    Console.WriteLine("开始录制,按任意键停止...");
    Console.ReadKey();
    _audioSource.StopRecording();
}

private void AudioSource_DataAvailable(object sender, WaveInEventArgs e)
{
    // 把每次收到的音频数据写入内存流
    _recordedAudioStream.Write(e.Buffer, 0, e.BytesRecorded);
}

private void AudioSource_RecordingStopped(object sender, StoppedEventArgs e)
{
    // 录制结束,把内存流转成字节数组
    byte[] audioBytes = _recordedAudioStream.ToArray();
    Console.WriteLine($"录制完成!音频字节数组长度:{audioBytes.Length}");

    // 直接调用播放方法测试
    PlayAudioFromBytes(audioBytes, _audioSource.WaveFormat);

    // 清理资源
    _audioSource.Dispose();
    _recordedAudioStream.Dispose();
}

二、实现从字节数组播放音频

控制台环境用WaveOutEvent就能播放,不需要依赖Windows Forms。我们把字节数组包装成MemoryStream,再用RawSourceWaveStream读取原始PCM数据,最后交给WaveOutEvent播放:

public void PlayAudioFromBytes(byte[] audioBytes, WaveFormat waveFormat)
{
    using (var memoryStream = new MemoryStream(audioBytes))
    using (var rawWaveStream = new RawSourceWaveStream(memoryStream, waveFormat))
    using (var waveOut = new WaveOutEvent())
    {
        waveOut.Init(rawWaveStream);
        waveOut.Play();
        Console.WriteLine("开始播放录制的音频,等待播放完成...");
        
        // 循环等待播放结束
        while (waveOut.PlaybackState == PlaybackState.Playing)
        {
            System.Threading.Thread.Sleep(100);
        }
        Console.WriteLine("播放结束!");
    }
}

三、完整控制台示例

把所有逻辑整合到Main方法里,直接就能运行:

class Program
{
    static void Main(string[] args)
    {
        var audioManager = new AudioManager();
        
        // 获取并打印可用麦克风设备
        audioManager.GetMicDevices();
        Console.WriteLine("=== 可用麦克风设备 ===");
        for (int i = 0; i < audioManager.Devices.Count; i++)
        {
            Console.WriteLine($"{i+1}. {audioManager.Devices[i]} | 通道数:{audioManager.Channels[i]}");
        }

        // 启动录制流程
        audioManager.StartMicRecording();
    }
}

// 封装所有音频操作的类
class AudioManager
{
    private MemoryStream _recordedAudioStream = new MemoryStream();
    private WaveInEvent _audioSource = null;
    private List<WaveInCapabilities> _sources = new List<WaveInCapabilities>();
    public List<string> Devices = new List<string>();
    public List<string> Channels = new List<string>();

    public void GetMicDevices()
    {
        for(int i = 0; i < WaveIn.DeviceCount; i++)
        {
            _sources.Add(WaveIn.GetCapabilities(i));
        }
        foreach(var device in _sources)
        {
            Devices.Add(device.ProductName);
            Channels.Add(device.Channels.ToString());
        }
    }

    public void StartMicRecording()
    {
        if (_recordedAudioStream != null)
        {
            _recordedAudioStream.Dispose();
            _recordedAudioStream = new MemoryStream();
        }

        int targetDeviceNum = 0;
        _audioSource = new WaveInEvent();
        _audioSource.DeviceNumber = targetDeviceNum;
        var deviceCaps = WaveIn.GetCapabilities(targetDeviceNum);
        _audioSource.WaveFormat = new WaveFormat(44100, 16, deviceCaps.Channels);

        _audioSource.DataAvailable += AudioSource_DataAvailable;
        _audioSource.RecordingStopped += AudioSource_RecordingStopped;

        _audioSource.StartRecording();
        Console.WriteLine("开始录制,按任意键停止...");
        Console.ReadKey();
        _audioSource.StopRecording();
    }

    private void AudioSource_DataAvailable(object sender, WaveInEventArgs e)
    {
        _recordedAudioStream.Write(e.Buffer, 0, e.BytesRecorded);
    }

    private void AudioSource_RecordingStopped(object sender, StoppedEventArgs e)
    {
        byte[] audioBytes = _recordedAudioStream.ToArray();
        Console.WriteLine($"录制完成!音频字节数组长度:{audioBytes.Length}");

        PlayAudioFromBytes(audioBytes, _audioSource.WaveFormat);

        _audioSource.Dispose();
        _recordedAudioStream.Dispose();
    }

    public void PlayAudioFromBytes(byte[] audioBytes, WaveFormat waveFormat)
    {
        using (var memoryStream = new MemoryStream(audioBytes))
        using (var rawWaveStream = new RawSourceWaveStream(memoryStream, waveFormat))
        using (var waveOut = new WaveOutEvent())
        {
            waveOut.Init(rawWaveStream);
            waveOut.Play();
            Console.WriteLine("开始播放录制的音频,等待播放完成...");
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                System.Threading.Thread.Sleep(100);
            }
            Console.WriteLine("播放结束!");
        }
    }
}

小提示

  • 记得先通过NuGet安装NAudio包:Install-Package NAudio
  • 运行时Windows会弹窗请求麦克风权限,一定要允许
  • 如果想让用户选择不同设备,修改targetDeviceNum的赋值逻辑就行,比如让用户输入设备编号

内容的提问来源于stack exchange,提问作者Computeshorts

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:38:36