基于Go語言編寫一個能在Windows上錄音的小程序
大家好!今天我要帶大家用 Go 語言寫一個能在 Windows 上錄音的小程序。別緊張,這不是什么黑客工具(雖然聽起來有點(diǎn)像),而是一個正經(jīng)的、能生成 output.wav 文件的"聲音捕捉器"——比如錄下你早上對著電腦喊"為什么又報錯了!"的珍貴瞬間 。
使用的Go庫
這個項目用到了兩個非??岬?Go 庫:
- github.com/Onelio/winmm:讓你在 Windows 上調(diào)用經(jīng)典的 winmm.dll 多媒體 API(沒錯,就是那個 Win95 時代就存在的老古董,但它依然好用?。?/li>
- github.com/go-audio/wav:幫你把原始音頻數(shù)據(jù)打包成標(biāo)準(zhǔn)的 WAV 文件。
話不多說,咱們直接上"操作"!
第一步:看看你的電腦有哪些"耳朵"和"嘴巴"
程序啟動后,它會先列出你電腦上所有的輸入設(shè)備(麥克風(fēng))和輸出設(shè)備(揚(yáng)聲器):
輸入設(shè)備: 0 麥克風(fēng) (Realtek Audio)
輸入設(shè)備: 1 立體聲混音 (Realtek Audio)
輸出設(shè)備: 0 揚(yáng)聲器 (Realtek Audio)
輸出設(shè)備: 1 耳機(jī) (Bluetooth Device)
然后它會壞笑著等你按回車——仿佛在說:"準(zhǔn)備好了嗎?我要開始偷聽你了哦~"
第二步:開始錄音
程序會以 8kHz 采樣率、16位深度、立體聲 的格式錄音。雖然音質(zhì)比不上 Hi-Res,但錄個語音備忘、貓叫、或者你模仿 Siri 的聲音綽綽有余。
錄音過程中,你隨時按回車就能停止。程序會把錄到的所有聲音,乖乖寫進(jìn)一個叫 output.wav 的文件里。
小知識:8kHz 是電話音質(zhì),但對語音識別或簡單錄音完全夠用,而且文件?。?/p>
第三步:播放你的"高光時刻"
錄完后,雙擊 output.wav,Windows 自帶的播放器就會打開。
- 如果你錄的是"啊——測試測試",那恭喜你,成功了!
- 如果你錄的是"老板根本不懂技術(shù)",建議立刻刪除文件并裝作無事發(fā)生 。
為什么這個代碼有點(diǎn)"復(fù)古"?
因為底層用的是 Windows 的 WaveIn API,這可是上世紀(jì) 90 年代的產(chǎn)物!但它穩(wěn)定、簡單、無需管理員權(quán)限,特別適合做小型錄音工具。Go 語言通過 cgo 或 syscall 封裝后,用起來居然還挺順手。
不過要注意:這段代碼只能在 Windows 上跑。Mac 和 Linux 用戶請繞道,或者考慮用 PortAudio 之類的跨平臺庫。
完整源碼
下面就是我們精心整理、注釋全中文、風(fēng)格清爽的錄音小工具源碼:
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() {
// 列出所有輸入設(shè)備
inputDevices, _ := winmm.EnumInDevices()
for _, dev := range inputDevices {
fmt.Println("輸入設(shè)備:", dev.Id(), dev.Name())
}
// 列出所有輸出設(shè)備
outputDevices, _ := winmm.EnumOutDevices()
for _, dev := range outputDevices {
fmt.Println("輸出設(shè)備:", dev.Id(), dev.Name())
}
// 等待用戶按回車?yán)^續(xù)
fmt.Println("按回車鍵開始錄音...")
_, _ = fmt.Scanln()
// 1. 創(chuàng)建錄音所需的音頻格式:立體聲(2通道)、8kHz 采樣率、16位深度
waveFormat := winmm.NewWaveFormat(winmm.ChStereo, winmm.SpS08kHz, winmm.BpS16)
// 創(chuàng)建用于錄音的緩沖區(qū),時長為 2 秒
recordBuffer := winmm.NewWaveHeader(waveFormat, 2)
// 創(chuàng)建錄音對象并打開默認(rèn)錄音設(shè)備(WaveMapper)
recorder := winmm.NewWaveIn()
_ = recorder.Open(winmm.WaveMapper, waveFormat)
// 準(zhǔn)備緩沖區(qū),并獲取其唯一標(biāo)識 key(用于后續(xù)操作)
bufferKey, _ := recorder.PrepareBuffer(recordBuffer)
fmt.Println("正在錄音...(按回車鍵停止)")
// 將緩沖區(qū)添加到錄音設(shè)備,并開始錄音
_ = recorder.AddBuffer(bufferKey)
_ = recorder.Start()
// 稍微延遲,確保錄音已開始
time.Sleep(200 * time.Millisecond)
// 啟動一個 goroutine,監(jiān)聽用戶是否按下回車鍵以停止錄音
shouldStop := false
go func() {
_, _ = fmt.Scanln()
shouldStop = true
}()
// 創(chuàng)建輸出 WAV 文件
outFile, err := os.Create("output.wav")
if err != nil {
log.Fatal("無法創(chuàng)建輸出文件:", err)
}
defer outFile.Close()
// 創(chuàng)建 WAV 編碼器:8kHz、16位、2通道(立體聲)、1 塊(通常為 1)
encoder := wav.NewEncoder(outFile, 8000, 16, 2, 1)
// 初始化用于寫入的音頻緩沖區(qū)
writeBuffer := audio.IntBuffer{
Format: &audio.Format{
NumChannels: 2, // 立體聲
SampleRate: 8000, // 8kHz 采樣率
},
}
// 持續(xù)監(jiān)聽錄音數(shù)據(jù),直到用戶停止
for !shouldStop {
select {
case data := <-recorder.Channel:
// 將接收到的原始字節(jié)數(shù)據(jù)轉(zhuǎn)換為 int16 樣本,并追加到 writeBuffer
copyDataToBuffer(data.GetBufferSlice(), &writeBuffer)
// 重新將緩沖區(qū)加入錄音隊列(循環(huán)使用)
_ = recorder.AddBuffer(bufferKey)
_ = recorder.Start()
default:
// 短暫休眠,避免忙等待
time.Sleep(10 * time.Millisecond)
}
}
// 停止錄音后,釋放緩沖區(qū)
_ = recorder.UnPrepareBuffer(bufferKey)
// 關(guān)閉錄音設(shè)備
_ = recorder.Close()
// 將錄制的音頻數(shù)據(jù)寫入 WAV 文件(包含 RIFF 頭和 PCM 數(shù)據(jù))
if err := encoder.Write(&writeBuffer); err != nil {
log.Fatal("寫入 WAV 文件失敗:", err)
}
if err := encoder.Close(); err != nil {
log.Fatal("關(guān)閉 WAV 編碼器失敗:", err)
}
fmt.Println("錄音已保存為 output.wav")
}
// copyDataToBuffer 將原始字節(jié)數(shù)據(jù)(小端序 int16)轉(zhuǎn)換為 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 轉(zhuǎn)為 int 并追加到緩沖區(qū)
buffer.Data = append(buffer.Data, int(sample))
}
}
小提醒(認(rèn)真臉)
- 代碼中為了簡潔,忽略了大部分錯誤處理。實際項目中請務(wù)必加上!
- 錄音需要麥克風(fēng)權(quán)限,Windows 可能會彈出隱私提示。
- 如果你錄下了什么不可描述的內(nèi)容……后果自負(fù),本人概不負(fù)責(zé)
結(jié)語
用 Go 在 Windows 上錄音,聽起來是不是有點(diǎn)"跨界"?但正是這種混搭,才讓編程充滿樂趣。下次你可以在這個基礎(chǔ)上加個 GUI、自動上傳到云盤,甚至做個"語音日記"小工具。
好了,我去試試錄下我家母老虎打呼的聲音了。你呢?打算錄點(diǎn)啥?
你的聲音,值得被保存——哪怕是吐槽代碼的那一刻。
到此這篇關(guān)于基于Go語言編寫一個能在Windows上錄音的小程序的文章就介紹到這了,更多相關(guān)Go語言實現(xiàn)錄音內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言設(shè)計實現(xiàn)在任務(wù)欄里提醒你喝水的兔子
這篇文章主要為大家介紹了Go語言設(shè)計實現(xiàn)在任務(wù)欄里提醒你喝水的兔子示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
go開發(fā)alertmanger實現(xiàn)釘釘報警
本文主要介紹了go開發(fā)alertmanger實現(xiàn)釘釘報警,通過自己的url實現(xiàn)alertmanager的釘釘報警,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-07-07
Go strconv包實現(xiàn)字符串與數(shù)值轉(zhuǎn)換的深度解析
在 Go 語言開發(fā)中,字符串與基本數(shù)據(jù)類型之間的轉(zhuǎn)換非常常見,Go 標(biāo)準(zhǔn)庫中的 strconv 包正是為這些場景設(shè)計的,下面小編就和大家詳細(xì)介紹一下它的具體應(yīng)用吧2026-03-03
Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例
這篇文章主要為大家介紹了Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06

