golang 使用beep 播放音频

依赖库

# 安装工具pkg
sudo apt install pkg-config

# 安装 alsa 开发库
sudo apt-get install libalsa-ocaml-dev 

主要代码

package main

import (
    "log"
    "os"
    "time"

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

func main() {
    f, err := os.Open("./Lame_Drivers_-_01_-_Frozen_Egg.mp3")
    if err != nil {
        log.Fatal(err)
    }

    streamer, format, err := mp3.Decode(f)
    if err != nil {
        log.Fatal(err)
    }
    defer streamer.Close()

    speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))

    done := make(chan bool)
    speaker.Play(beep.Seq(streamer, beep.Callback(func() {
        done <- true
    })))

    <-done
}

获取系统的音频设备

// Copyright 2021 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !android && !darwin && !js && !windows && !nintendosdk

package main

// #cgo pkg-config: alsa
//
// #include <alsa/asoundlib.h>
import "C"

import (
    "fmt"
    "sync"
    "unsafe"
)

type context struct {
    channelCount int

    suspended bool

    handle *C.snd_pcm_t

    cond *sync.Cond


    ready chan struct{}
}

var theContext *context

func alsaError(name string, err C.int) error {
    return fmt.Errorf("oto: ALSA error at %s: %s", name, C.GoString(C.snd_strerror(err)))
}

func deviceCandidates() []string {
    const getAllDevices = -1

    cPCMInterfaceName := C.CString("pcm")
    defer C.free(unsafe.Pointer(cPCMInterfaceName))

    var hints *unsafe.Pointer
    err := C.snd_device_name_hint(getAllDevices, cPCMInterfaceName, &hints)
    if err != 0 {
        return []string{"default", "plug:default"}
    }
    defer C.snd_device_name_free_hint(hints)

    var devices []string

    cIoHintName := C.CString("IOID")
    defer C.free(unsafe.Pointer(cIoHintName))
    cNameHintName := C.CString("NAME")
    defer C.free(unsafe.Pointer(cNameHintName))

    for it := hints; *it != nil; it = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(it)) + unsafe.Sizeof(uintptr(0)))) {
        io := C.snd_device_name_get_hint(*it, cIoHintName)
        defer func() {
            if io != nil {
                C.free(unsafe.Pointer(io))
            }
        }()
        if C.GoString(io) == "Input" {
            continue
        }

        name := C.snd_device_name_get_hint(*it, cNameHintName)
        defer func() {
            if name != nil {
                C.free(unsafe.Pointer(name))
            }
        }()
        if name == nil {
            continue
        }
        goName := C.GoString(name)
        if goName == "null" {
            continue
        }
        if goName == "default" {
            continue
        }
        devices = append(devices, goName)
    }

    devices = append([]string{"default", "plug:default"}, devices...)

    return devices
}


func main() {
    deviceList := deviceCandidates()
    for  idx,device := range deviceList {
        println(idx)
        println(device)
    }
}