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

如何在Go with portaudio和sndfile中播放wav文件,audio,go,Audio,Go,首先,我对围棋和低级编程的世界还不熟悉,所以请容忍我……:) 所以我想做的是:;用for Go读取.wav文件并播放 我找不到任何这样的例子,很明显,我缺乏实现这一点的指针、流和缓冲区的基本知识。这是我到目前为止对它的看法,我试着阅读文档和我能找到的几个例子,并把它们组合在一起。我想我能够打开文件和流,但我不知道如何连接这两个 package main import ( "code.google.com/p/portaudio-go/portaudio" "fmt" "

首先,我对围棋和低级编程的世界还不熟悉,所以请容忍我……:)

所以我想做的是:;用for Go读取.wav文件并播放

我找不到任何这样的例子,很明显,我缺乏实现这一点的指针、流和缓冲区的基本知识。这是我到目前为止对它的看法,我试着阅读文档和我能找到的几个例子,并把它们组合在一起。我想我能够打开文件和流,但我不知道如何连接这两个

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)
}

如果能为初学者提供一个工作示例和一些提示/链接,我将不胜感激。如果您有一个解决方案涉及上述两个库之外的其他库,那也没关系。

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

考虑一下数据流:磁盘上.wav文件中的一组位被程序读取并发送到操作系统,操作系统将它们传送到声卡,在声卡上它们被转换成模拟信号,驱动扬声器产生声波,最终到达你的耳朵

这种流动对时间波动非常敏感。如果它在任何一点被举起,你都会在最后的声音中感觉到明显的、有时是刺耳的伪影

一般来说,操作系统/声卡都是可靠且经过良好测试的——大多数音频瑕疵都是由美国开发人员编写的劣质应用程序代码造成的;)

PortAudio之类的库通过处理一些线程进程的黑魔法并使调度变得容易实现来帮助我们。本质上,它说“好的,我将启动这个音频流,当我需要下一个样本数据位时,每X毫秒我将回调您提供的任何函数。”

在本例中,您提供了一个用随机数据填充输出缓冲区的函数。要播放wave文件,需要更改此回调函数

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

对于本例,最简单的方法可能是在启动流之前加载样本,并在回调中使用如下内容:


isample:=0
回调:=func(out[]int32){
对于i:=0;i

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

事实上,你也需要告诉它开始!打开后,调用
stream.Start()
,然后添加睡眠,否则程序可能会在有机会播放任何内容之前退出

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

当然,预先加载所有示例数据,以便您可以在音频回调中引用它并不是一个好方法,除非您通过了hello world。通常使用环形缓冲区或类似的东西将示例数据传递给音频回调

PortAudio提供了另一个API(“阻塞”API)来为您实现这一点。对于portaudio go,这是通过将一个片段传递到
OpenStream
而不是函数来调用的。使用阻塞API时,通过(a)填充传递到
OpenStream
中的切片,以及(b)调用
stream.Write()
将样本数据泵入流中


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

您在portaudio的示例目录中看到这个播放示例了吗?是的,我看到了,但是我无法用.wav复制它。