如何在Go中使用portaudio和sndfile播放wav文件

Jim*_*ung 2 audio go

首先,我是Go和低级编程世界的新手,所以忍受我... :)

所以我要做的就是这个; 读取带有libsndfile 绑定的.wav文件,然后使用portaudio播放它.

我找不到任何这方面的例子,显然我缺乏关于指针,流和缓冲区的基本知识来实现​​这一点.到目前为止,这是我对它的看法,我试图阅读文档和我能够找到的几个例子并将各个部分组合在一起.我想我能够打开文件和流,但我不知道如何连接这两个.

package main

import (
    "code.google.com/p/portaudio-go/portaudio"
    "fmt"
    "github.com/mkb218/gosndfile/sndfile"
    "math/rand"
)

func main() {
    portaudio.Initialize()
    defer portaudio.Terminate()

    // Open file with sndfile
    var i sndfile.Info
    file, fileErr := sndfile.Open("hello.wav", sndfile.Read, &i)
    fmt.Println("File: ", file, fileErr)

    // Open portaudio stream
    h, err := portaudio.DefaultHostApi()
    stream, err := portaudio.OpenStream(portaudio.HighLatencyParameters(nil, h.DefaultOutputDevice), func(out []int32) {
        for i := range out {
            out[i] = int32(rand.Uint32())
        }
    })
    defer stream.Close()
    fmt.Println("Stream: ", stream, err)

    // Play portaudio stream
    // ....
    framesOut := make([]int32, 32000)
    data, err := file.ReadFrames(framesOut)
    fmt.Println("Data: ", data, err)
}
Run Code Online (Sandbox Code Playgroud)

我会非常感谢一个有用的例子和初学者的一些提示/链接.如果您的解决方案涉及除上述两个之外的其他库,那也没关系.

sqw*_*eek 5

啊哈,音频编程!欢迎来到软实时计算世界:)

想想数据流:磁盘上的.wav文件中的一堆位被程序读取并发送到操作系统,操作系统将它们交给声卡,然后将它们转换为模拟信号,驱动扬声器产生声波终于到达了你的耳朵.

此流程对时间波动非常敏感.如果它在任何一点被抬起,你会在最后的声音中感觉到明显的,有时是刺耳的瑕疵.

通常,操作系统/声卡是可靠且经过良好测试的 - 大多数音频工件都是由我们开发人员编写伪劣应用程序代码引起的;)

像PortAudio这样的图书馆通过处理一些线程概率黑魔法并使调度变得平易近人来帮助我们.基本上它说"好吧我打算开始这个音频流,每当我需要下一个样本数据时,每隔X毫秒,我就会回调你提供的任何功能."

在这种情况下,您提供了一个用随机数据填充输出缓冲区的函数.要改为播放wave文件,您需要更改此回调函数.

但!您不希望在回调中执行I/O. 从磁盘读取一些字节可能需要几十毫秒,而portaudio 现在需要采样数据,以便及时到达声卡.同样,您希望避免获取可能阻止音频回调的锁或任何其他操作.

对于这个例子,在启动流之前加载样本可能是最简单的,并使用类似这样的回调:

isample := 0 callback := func(out []int32) { for i := 0; i < len(out); i++ { out[i] = framesOut[(isample + i) % len(framesOut)] } isample += len(out) }

请注意,这% len(framesOut)将导致加载的32000个样本反复循环 - PortAudio将使流保持运行,直到您告诉它停止为止.

实际上,你需要告诉它也要开始!在打开之后打电话stream.Start()并在此之后添加一个睡眠,或者你的程序可能会在它有机会玩之前退出.

最后,这还假设wave文件中的样本格式与您从PortAudio请求的样本格式相同.如果格式不匹配,你仍会听到一些声音,但它可能听起来不太好听!无论如何,样本格式都是一个完整的问题.

当然,预先加载所有的样本数据,这样你就可以在音频回调中引用它,这不是一个很棒的方法,除非你超越了hello world的东西.通常,您使用环形缓冲区或类似的东西将样本数据传递给音频回调.

PortAudio提供了另一个API("阻塞"API),可以为您完成此任务.对于portaudio-go,通过传递切片OpenStream而不是函数来调用它.使用阻塞API时,您可以通过(a)填充传入的切片OpenStream和(b)调用将样本数据泵入流中stream.Write().

这比我预想的要长得多,所以我最好把它留在那里.HTH.