我正在写一个声音合成器。最终项目没有GUI,但现在我正在使用go-astilectron,这样我就可以轻松地测试具有钢琴键盘和所有振荡器设置(ADSR,滤波器,统一,ecc等)的合成器。
要输出我正在使用portaudio(https://github.com/gordonklaus/portaudio)的合成器发出的声音,这是负责它的代码块:
stream, err := portaudio.OpenDefaultStream(0, 1, sampleRate, bufferSize, func(out []float32) {
for i := 0; i < len(buf); i++ {
var valLive, valLoop float32
//If a single note has been set by the GUI
if note.Samples > 0 {
valLive = note.GetSample(instrument)
}
//If a loop of notes has been set by the GUI
if len(notes) > 0{
valLoop = notes.GetSample(instrument)
}
buf[i] = valLive + valLoop
}
//TODO: sound filters
for i, val := range buf{
out[i] = val
}
})
OpenDefaultStream
的最后一个参数是一个将输出作为写入PCM数据的参数的函数。
我假设portaudio程序包在goroutine中运行了回调,因为一旦我单击GUI的虚拟键盘上的一个键,该程序就不会停止,并且输出的音频将立即生效。 note
和notes
是我整个main
包中可用的全局变量,它们是由GUI发送的astilectron事件设置的,这是接收它们的代码段:
case "play":
// Unmarshal payload
if len(m.Payload) > 0 {
// Unmarshal payload
if err = json.Unmarshal(m.Payload, ¬e); err != nil {
payload = err.Error()
return
}
}
payload = note
note.Init(instrument)
case "loop":
// Unmarshal payload
if len(m.Payload) > 0 {
// Unmarshal payload
if err = json.Unmarshal(m.Payload, ¬es); err != nil {
payload = err.Error()
return
}
}
payload = notes
notes.Init(instrument)
有效载荷只是包含所有注释信息(半音,长度)的JSON,note.Init()
计算样本数并将其设置为note.Samples
,触发portaudio回调获取样本并输出他们。
到目前为止,这段代码已经完美地运行了,从来没有给我带来任何问题,但是有些事情令我担心。基本上,我让portaudio和electronic都访问相同的变量,并且我假设它们都使用goroutine,据我所知这是一个很大的禁忌。我应该改用频道吗?
如果是这样,我正在考虑创建2个全局通道,并使用portaudio回调内部的select提取实时和循环采样。
正如我所说,这段代码可以完美运行,因此在继续实施可能破坏它的渠道之前,我想知道它是否值得。
谢谢大家