gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 01:33:41 27 4
gpt4 key购买 nike

首先,我是 Go 和低级编程领域的新手,所以请多多包涵...:)

所以我要做的是这个;使用 libsndfile 读取 .wav 文件binding去和玩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)
}

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

最佳答案

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

想想数据流:磁盘上 .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()并在此之后添加 sleep ,否则您的程序可能会在有机会播放任何内容之前退出。

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

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

PortAudio 提供了另一个 API(“阻塞”API)来为您执行此操作。对于 portaudio-go,这是通过将 slice 传递给 OpenStream 来调用的。而不是一个函数。使用阻塞 API 时,您通过 (a) 填充您传递到 OpenStream 的 slice 将示例数据泵入流中(b) 调用 stream.Write() .

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

关于audio - 如何使用 portaudio 和 sndfile 在 Go 中播放 wav 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28489786/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com