Golang实现音频播放的方法与技巧
Golang实现音频播放的方法与技巧 是否可以在 Go 中播放音频(特别是在 macOS 上且不执行“afplay”命令)?
你需要什么程度的控制?为什么 afplay 不够用?
通常,你会有类似这样的命令。或者一个用于播放文件或音频片段的高级解决方案 API。或者一个用于处理事务的流式 API,这是低级的。
对于高级方案,我在 macOS 上不太了解,但对于流式音频,StackOverflow 上有相关代码。 Portaudio 为你提供了最佳的跨平台流式音频解决方案。我想这也会是你实现音频流最简单的方式,你只需要编译共享库并学习其 API。
如果你正在处理音频流,通常音频数据需要符合流式 API 的要求(幸运的是,Portaudio 允许你选择流的采样数据类型)。或者你拥有需要先解码的压缩文件/数据。这就会涉及到另一个库,比如 ogg vorbis 或 flac。
也许可以从这里开始,看起来没问题:
Playing audio file with golang
go
package main
import (
"github.com/gordonklaus/portaudio"
"encoding/binary"
"fmt"
"io"
"os"
"os/signal"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("missing required argument: input file name")
return
}
fmt.Println("Playing. Press Ctrl-C to stop.")
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill)
更多关于Golang实现音频播放的方法与技巧的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中直接播放音频确实有一定挑战,但可以通过以下方法实现:
1. 使用PortAudio绑定
PortAudio是一个跨平台音频库,可以通过cgo调用:
package main
/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: -L/usr/local/lib -lportaudio
#include <portaudio.h>
*/
import "C"
import (
"fmt"
"time"
"unsafe"
)
func playSineWave() {
C.Pa_Initialize()
var stream *C.PaStream
sampleRate := 44100.0
frequency := 440.0
C.Pa_OpenDefaultStream(&stream,
0, // 输入通道数
1, // 输出通道数
C.paFloat32,
C.double(sampleRate),
256,
nil,
nil)
C.Pa_StartStream(stream)
time.Sleep(2 * time.Second)
C.Pa_StopStream(stream)
C.Pa_CloseStream(stream)
C.Pa_Terminate()
}
2. 使用beep库(纯Go实现)
beep是一个纯Go的音频播放库:
package main
import (
"fmt"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/mp3"
"github.com/faiface/beep/speaker"
"os"
)
func playMP3() error {
f, err := os.Open("audio.mp3")
if err != nil {
return err
}
defer f.Close()
streamer, format, err := mp3.Decode(f)
if err != nil {
return err
}
defer streamer.Close()
err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
if err != nil {
return err
}
done := make(chan bool)
speaker.Play(beep.Seq(streamer, beep.Callback(func() {
done <- true
})))
<-done
return nil
}
3. 使用oto库(跨平台)
oto是一个跨平台的底层音频播放库:
package main
import (
"io"
"os"
"github.com/hajimehoshi/oto"
"github.com/hajimehoshi/go-mp3"
)
func playAudio() error {
f, err := os.Open("audio.mp3")
if err != nil {
return err
}
defer f.Close()
d, err := mp3.NewDecoder(f)
if err != nil {
return err
}
c, err := oto.NewContext(d.SampleRate(), 2, 2, 8192)
if err != nil {
return err
}
defer c.Close()
p := c.NewPlayer()
defer p.Close()
if _, err := io.Copy(p, d); err != nil {
return err
}
return nil
}
4. macOS特定:使用Core Audio
通过cgo直接调用macOS的Core Audio框架:
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework AVFoundation -framework Foundation
#import <AVFoundation/AVFoundation.h>
void playAudioFile(const char* path) {
NSString *filePath = [NSString stringWithUTF8String:path];
NSURL *url = [NSURL fileURLWithPath:filePath];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[player play];
// 等待播放完成
while ([player isPlaying]) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
}
*/
import "C"
import "unsafe"
func playWithCoreAudio(filePath string) {
cPath := C.CString(filePath)
defer C.free(unsafe.Pointer(cPath))
C.playAudioFile(cPath)
}
5. 生成并播放WAV格式音频
纯Go生成并播放音频数据:
package main
import (
"bytes"
"encoding/binary"
"github.com/hajimehoshi/oto"
)
func generateAndPlayWAV() error {
// 生成440Hz正弦波
const (
sampleRate = 44100
duration = 2 // 秒
frequency = 440
)
numSamples := sampleRate * duration
data := make([]byte, numSamples*2)
for i := 0; i < numSamples; i++ {
sample := int16(32767 * 0.5 *
sin(2*math.Pi*float64(i)*frequency/float64(sampleRate)))
binary.LittleEndian.PutUint16(data[i*2:], uint16(sample))
}
// 创建WAV头
header := createWAVHeader(len(data), sampleRate)
// 播放
ctx, err := oto.NewContext(sampleRate, 1, 2, 8192)
if err != nil {
return err
}
defer ctx.Close()
player := ctx.NewPlayer()
defer player.Close()
player.Write(append(header[:], data...))
return nil
}
这些方法都不需要调用外部afplay命令,可以在macOS上直接使用。beep和oto库提供了较好的跨平台支持,而Core Audio方案则针对macOS进行了优化。

