Golang实现音频播放的方法与技巧

Golang实现音频播放的方法与技巧 是否可以在 Go 中播放音频(特别是在 macOS 上且不执行“afplay”命令)?

2 回复

你需要什么程度的控制?为什么 afplay 不够用?

通常,你会有类似这样的命令。或者一个用于播放文件或音频片段的高级解决方案 API。或者一个用于处理事务的流式 API,这是低级的。

对于高级方案,我在 macOS 上不太了解,但对于流式音频,StackOverflow 上有相关代码。 Portaudio 为你提供了最佳的跨平台流式音频解决方案。我想这也会是你实现音频流最简单的方式,你只需要编译共享库并学习其 API。

如果你正在处理音频流,通常音频数据需要符合流式 API 的要求(幸运的是,Portaudio 允许你选择流的采样数据类型)。或者你拥有需要先解码的压缩文件/数据。这就会涉及到另一个库,比如 ogg vorbis 或 flac。

也许可以从这里开始,看起来没问题:

stackoverflow.com

Playing audio file with golang

go

github.com

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进行了优化。

回到顶部