golang知识集

用 Go 给 Windows 装个“顺风耳”:两分钟写个录音小工

2025-11-06  本文已影响0人  Mgx_无心

大家好!今天我要带大家用 Go 语言写一个能在 Windows 上录音的小程序。别紧张,这不是什么黑客工具(虽然听起来有点像),而是一个正经的、能生成 output.wav 文件的"声音捕捉器"——比如录下你早上对着电脑喊"为什么又报错了!"的珍贵瞬间 😅。

使用的Go库

这个项目用到了两个非常酷的 Go 库:

话不多说,咱们直接上"操作"!

第一步:看看你的电脑有哪些"耳朵"和"嘴巴"

程序启动后,它会先列出你电脑上所有的输入设备(麦克风)和输出设备(扬声器):

输入设备: 0 麦克风 (Realtek Audio)
输入设备: 1 立体声混音 (Realtek Audio)
输出设备: 0 扬声器 (Realtek Audio)
输出设备: 1 耳机 (Bluetooth Device)

然后它会坏笑着等你按回车——仿佛在说:"准备好了吗?我要开始偷听你了哦~"

第二步:开始录音!

程序会以 8kHz 采样率、16位深度、立体声 的格式录音。虽然音质比不上 Hi-Res,但录个语音备忘、猫叫、或者你模仿 Siri 的声音绰绰有余。

录音过程中,你随时按回车就能停止。程序会把录到的所有声音,乖乖写进一个叫 output.wav 的文件里。

🎧 小知识:8kHz 是电话音质,但对语音识别或简单录音完全够用,而且文件小!

第三步:播放你的"高光时刻"

录完后,双击 output.wav,Windows 自带的播放器就会打开。

为什么这个代码有点"复古"?

因为底层用的是 Windows 的 WaveIn API,这可是上世纪 90 年代的产物!但它稳定、简单、无需管理员权限,特别适合做小型录音工具。Go 语言通过 cgo 或 syscall 封装后,用起来居然还挺顺手。

不过要注意:这段代码只能在 Windows 上跑。Mac 和 Linux 用户请绕道,或者考虑用 PortAudio 之类的跨平台库。

完整源码

下面就是我们精心整理、注释全中文、风格清爽的录音小工具源码:

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "io"
    "log"
    "os"
    "time"

    "github.com/Onelio/winmm"
    "github.com/go-audio/audio"
    "github.com/go-audio/wav"
)

func main() {
    // 列出所有输入设备
    inputDevices, _ := winmm.EnumInDevices()
    for _, dev := range inputDevices {
        fmt.Println("输入设备:", dev.Id(), dev.Name())
    }

    // 列出所有输出设备
    outputDevices, _ := winmm.EnumOutDevices()
    for _, dev := range outputDevices {
        fmt.Println("输出设备:", dev.Id(), dev.Name())
    }

    // 等待用户按回车继续
    fmt.Println("按回车键开始录音...")
    _, _ = fmt.Scanln()

    // 1. 创建录音所需的音频格式:立体声(2通道)、8kHz 采样率、16位深度
    waveFormat := winmm.NewWaveFormat(winmm.ChStereo, winmm.SpS08kHz, winmm.BpS16)

    // 创建用于录音的缓冲区,时长为 2 秒
    recordBuffer := winmm.NewWaveHeader(waveFormat, 2)

    // 创建录音对象并打开默认录音设备(WaveMapper)
    recorder := winmm.NewWaveIn()
    _ = recorder.Open(winmm.WaveMapper, waveFormat)

    // 准备缓冲区,并获取其唯一标识 key(用于后续操作)
    bufferKey, _ := recorder.PrepareBuffer(recordBuffer)

    fmt.Println("正在录音...(按回车键停止)")

    // 将缓冲区添加到录音设备,并开始录音
    _ = recorder.AddBuffer(bufferKey)
    _ = recorder.Start()

    // 稍微延迟,确保录音已开始
    time.Sleep(200 * time.Millisecond)

    // 启动一个 goroutine,监听用户是否按下回车键以停止录音
    shouldStop := false
    go func() {
        _, _ = fmt.Scanln()
        shouldStop = true
    }()

    // 创建输出 WAV 文件
    outFile, err := os.Create("output.wav")
    if err != nil {
        log.Fatal("无法创建输出文件:", err)
    }
    defer outFile.Close()

    // 创建 WAV 编码器:8kHz、16位、2通道(立体声)、1 块(通常为 1)
    encoder := wav.NewEncoder(outFile, 8000, 16, 2, 1)

    // 初始化用于写入的音频缓冲区
    writeBuffer := audio.IntBuffer{
        Format: &audio.Format{
            NumChannels: 2,   // 立体声
            SampleRate:  8000, // 8kHz 采样率
        },
    }

    // 持续监听录音数据,直到用户停止
    for !shouldStop {
        select {
        case data := <-recorder.Channel:
            // 将接收到的原始字节数据转换为 int16 样本,并追加到 writeBuffer
            copyDataToBuffer(data.GetBufferSlice(), &writeBuffer)
            // 重新将缓冲区加入录音队列(循环使用)
            _ = recorder.AddBuffer(bufferKey)
            _ = recorder.Start()
        default:
            // 短暂休眠,避免忙等待
            time.Sleep(10 * time.Millisecond)
        }
    }

    // 停止录音后,释放缓冲区
    _ = recorder.UnPrepareBuffer(bufferKey)
    // 关闭录音设备
    _ = recorder.Close()

    // 将录制的音频数据写入 WAV 文件(包含 RIFF 头和 PCM 数据)
    if err := encoder.Write(&writeBuffer); err != nil {
        log.Fatal("写入 WAV 文件失败:", err)
    }
    if err := encoder.Close(); err != nil {
        log.Fatal("关闭 WAV 编码器失败:", err)
    }

    fmt.Println("录音已保存为 output.wav")
}

// copyDataToBuffer 将原始字节数据(小端序 int16)转换为 audio.IntBuffer 所需的 int 切片
func copyDataToBuffer(data []byte, buffer *audio.IntBuffer) {
    reader := bytes.NewReader(data)
    for {
        var sample int16
        err := binary.Read(reader, binary.LittleEndian, &sample)
        switch {
        case err == io.EOF:
            return // 读取完毕
        case err != nil:
            log.Println("读取音频样本时出错:", err)
            return
        }
        // 将 int16 转为 int 并追加到缓冲区
        buffer.Data = append(buffer.Data, int(sample))
    }
}

小提醒(认真脸)

结语

用 Go 在 Windows 上录音,听起来是不是有点"跨界"?但正是这种混搭,才让编程充满乐趣。下次你可以在这个基础上加个 GUI、自动上传到云盘,甚至做个"语音日记"小工具。

好了,我去试试录下我家母老虎打呼的声音了。你呢?打算录点啥?

🎙️ 你的声音,值得被保存——哪怕是吐槽代码的那一刻。

上一篇 下一篇

猜你喜欢

热点阅读