去提问»
提问背景 Question:
如果我想用 golang实现,下面的播放呢 X: 1 T: Summer Breeze M: 4/4 L: 1/4 K: C C D E C | C D E C | E F G2 | E F G2 | G/A/ G/F/ E C | G/A/ G/F/ E C | C G, C2 | C G, C2 |

Golang 实现 ABC 记谱法音频播放

发布时间:2026-08-22 14:54 👁️ 阅读:1

在 Go 语言中,要播放你提供的 ABC 记谱法文本,最直接、不依赖外部重型 MIDI 播放器的方法是:解析 ABC 文本中的音符与节拍,通过 Go 音频库(如 faiface/beep)实时合成正弦波(Sine Wave)并输出到扬声器。

下面为你提供一个完整的、开箱即用的 Go 程序。它将解析你给出的《Summer Breeze》旋律,并将其转化为声波播放出来。


1. 准备工作

首先,你需要初始化一个 Go 项目并安装跨平台的音频基础库 github.com/faiface/beep

mkdir go-abc-player
cd go-abc-player
go mod init go-abc-player
go get github.com/faiface/beep

系统依赖提示(Linux 用户)
如果你在 Linux 上运行,可能需要先安装 ALSA 开发库(Ubuntu/Debian 运行 sudo apt-get install libasound2-dev)。Windows 和 macOS 无需额外依赖。


2. 完整 Go 源码

创建 main.go,将以下代码粘贴进去:

package main

import (
	"fmt"
	"math"
	"time"

	"github.com/faiface/beep"
	"github.com/faiface/beep/speaker"
)

// 定义音符频率 Map (C3 到 A4)
var noteFreqs = map[string]float64{
	"C,": 130.81, // 低音 C3
	"G,": 196.00, // 低音 G3
	"C":  261.63, // 中音 C4
	"D":  293.66,
	"E":  329.63,
	"F":  349.23,
	"G":  392.00,
	"A":  440.00,
}

// Note 代表一个要播放的音符
type Note struct {
	Pitch    string        // 音名,如 "C", "G,"
	Duration time.Duration // 持续时间
}

// SineWave 是一个简单的正弦波生成器,实现了 beep.Streamer 接口
type SineWave struct {
	freq   float64
	sample float64
	sr     beep.SampleRate
}

func NewSineWave(sr beep.SampleRate, freq float64) *SineWave {
	return &SineWave{
		freq: freq,
		sr:   sr,
	}
}

func (s *SineWave) Stream(samples [][2]float64) (n int, ok bool) {
	for i := range samples {
		// 正弦波计算公式:sin(2 * pi * f * t)
		val := math.Sin(2 * math.Pi * s.freq * s.sample / float64(s.sr))
		// 降低一点音量,避免爆音,并输出到左右双声道
		samples[i][0] = val * 0.3 
		samples[i][1] = val * 0.3
		s.sample++
	}
	return len(samples), true
}

func (s *SineWave) Err() error {
	return nil
}

func main() {
	// 1. 初始化扬声器,采样率为 44100Hz
	sr := beep.SampleRate(44100)
	err := speaker.Init(sr, sr.N(time.Second/10))
	if err != nil {
		panic(err)
	}

	// 2. 将你的 ABC 旋律转化为简易的音符队列
	// 根据:M:4/4 (四分音符为一拍), L:1/4 (基础音符长度为四分之一拍)
	// 我们设定 1 拍 (L:1/4) 的时间为 400 毫秒
	baseDuration := 400 * time.Millisecond

	melody := []Note{
		// C D E C | C D E C |
		{"C", baseDuration}, {"D", baseDuration}, {"E", baseDuration}, {"C", baseDuration},
		{"C", baseDuration}, {"D", baseDuration}, {"E", baseDuration}, {"C", baseDuration},
		// E F G2 | E F G2 |
		{"E", baseDuration}, {"F", baseDuration}, {"G", baseDuration * 2}, // G2 占两拍
		{"E", baseDuration}, {"F", baseDuration}, {"G", baseDuration * 2},
		// G/A/ G/F/ E C | G/A/ G/F/ E C |
		{"G", baseDuration / 2}, {"A", baseDuration / 2}, // G/A/ 是半拍(八分音符)
		{"G", baseDuration / 2}, {"F", baseDuration / 2},
		{"E", baseDuration}, {"C", baseDuration},
		{"G", baseDuration / 2}, {"A", baseDuration / 2},
		{"G", baseDuration / 2}, {"F", baseDuration / 2},
		{"E", baseDuration}, {"C", baseDuration},
		// C G, C2 | C G, C2 |
		{"C", baseDuration}, {"G,", baseDuration}, {"C", baseDuration * 2},
		{"C", baseDuration}, {"G,", baseDuration}, {"C", baseDuration * 2},
	}

	fmt.Println("♪ 正在播放: Summer Breeze (ABC Notation)... 🎶")

	// 3. 顺序播放音符
	for _, note := range melody {
		freq, exists := noteFreqs[note.Pitch]
		if !exists {
			// 如果遇到无法识别的音符,静音等同时间
			time.Sleep(note.Duration)
			continue
		}

		// 创建该音符的正弦波流
		sine := NewSineWave(sr, freq)
		// 限制该波形流的播放时长
		limitSine := beep.Take(sr.N(note.Duration), sine)

		// 播放音符
		done := make(chan bool)
		speaker.Play(beep.Seq(limitSine, beep.Callback(func() {
			done <- true
		})))

		<-done
		// 每个音符之间微调停顿 30ms,听起来更有颗粒感和乐感,而不是糊成一片
		time.Sleep(30 * time.Millisecond)
	}

	fmt.Println("播放完毕!")
}

3. 如何运行?

保存代码后,在终端执行以下命令:

go run main.go

你会听到电脑扬声器中传出经典的 8-bit 电子合成器风格的《Summer Breeze》旋律!

4. 代码逻辑解析:

  1. 音高与频率转换:在 noteFreqs Map 中,我们定义了 C 为中音 C4 (261.63Hz),C,(带逗号)表示低八度的 C3 (130.81Hz)。
  2. 音符时长转换
    • 基础拍长 baseDuration(代表 L: 1/4)设为 400ms
    • G2 尾部带数字 2,表示时长乘以 2(即 baseDuration * 2)。
    • G/(或 G/A/)表示斜杠分音,时长减半(即 baseDuration / 2)。
  3. 正弦波合成:利用物理公式 sin(2 * pi * freq * t),实时计算每个音频采样点(Sample)的振幅,生成人耳能听到的纯音。