用Go从头开始播放音频:弗雷尔·雅克(Frère Jacques)

2020-09-17 03:41:53

在上一篇文章中,我最后说我们将能够使用GoAudio来生成一些简单的曲目,在这篇文章中,我们将对此进行测试。点击视频收听最终结果:)。

约翰/弗雷尔·雅克兄弟(Brother John/Frère Jacques)是一首流行的护理韵律,也是我小时候想要弹的为数不多的几首钢琴曲子之一。因此,我认为这次尝试编写这首歌是合适的。演奏这首歌的注释取自true-piano-essons.com。代码采用从C键开始的注释。(也许既然它在围棋中,我应该从G开始。请随意更改它)。

在之前的帖子中,我们实际上忽略了将音符转换为频率的问题。GoAudio以给定的频率工作。因此,我们需要首先实现一种将音符转换为频率的方法。为此,我想出了一个有点疯狂的解决方案,但除了硬编码之外,我不知道还有什么更好的方法。

当我们使用等调音阶制作音乐时,我们可以计算每个音符的频率,给定一个参考频率。我使用中间C作为参考频率,可以这样从音乐会A(A440)导出:MidleC=(Concerta*math.Pow(2,3/12))/2。

为了了解我们是如何得出每个音符的频率的,我建议大家看看维基百科。然而,基本的想法是,我们在一个八度中有12个半音,所以对于每个八度,我们必须根据参考频率产生12个半音。然而,令人困惑的是,一个八度从C开始,而不是从A开始(它确实循环),所以我们得到:C,D,E,F,G,B。每次我们循环回到C,我们就上升一个八度(意味着更高的频率,因此更高的音高)。

您可以在此处查看从C0(倍频程为0的C)到B8(倍频程为8的B)的所有频率表。

这方面的代码有点老套,所以如果你有更好的方法,请让我知道!:-)。

Var(NOTES=[]string{";C";,";C#";,";D";,";D#";,";E";,";F";,";G";,";G#";,";A";,";A#";,";B";})//GenerateNotes基于参考频率FR创建<;音符、频率&>映射。//参考频率设置为440函数GenerateNotes()map[string]float64{ni:=0 Concerta:=440。中C:=(协奏曲*数学。POW(2,3./12.))/2.对于i:=0;i<;24;i++{fk:=FR*MATH,fr:=midleC notemap:=map[String]float64{}。战俘(2,Float64(I)/12.)注意:=NOTES[ni%len(NOTES)]八度:=4+i/12var octs字符串,如果八度!=0{octs=strconv.。Itoa(八度)}notemmap[注+octs]=fk ni++}notemap[";wait";]=0。返回notemap}

在这里,我从C4->;B5产生第四和第五个八度。如果我们打印由该代码生成的地图,我们会得到(缩写)。

还要注意,我在其中添加了频率为0的等待。这有点老生常谈(在围棋中也是不必要的)。我添加了这个,这样我们可以在播放歌曲的不同部分之间有更多的等待时间。因为浮点数的缺省零值是零,并且因为您可以在缺少Akey的情况下安全地访问地图,所以这是没有必要的。我们可以只用等待键调用地图,即使它没有插入,我们的结果仍然是正确的。

如果你正在读这篇文章,你很有可能知道这一点。但我想强调一下,因为我认为这是围棋的一个相当酷的功能!然而,在这种情况下,我选择了明确说明等待通知的意图。

好的,我们有我们的音符和与之相关的频率。我们实际上还需要产生一个波。为此,我引入了方便的函数“play”,它接受几个参数并返回一个帧:

这与过去有关ADSR的内容非常相似,就像在那篇文章中一样,我们也会在这里将ADSR信封应用于信号。请注意,“Play”需要一个LookupOscillator,在上一篇文章中,我们构建了几个LookupOscillator,用于生成正弦波、三角形、正方形等。

这个函数告诉我们,用给定的振荡器播放音符一定的持续时间(以秒为单位),然后返回一段帧。此功能允许我快速更改波形类型,并处理针对不同时间跨度演奏不同音符的结果。

Func Play(l*synth.。LookupOscillator,Note String,Duration float64,notefreqs map[String]float64)[]wave。FRAME{freq:=notefreqs[NOTE]FRAMES:=生成([]WAVE。Frame,int(持续时间*sr))变量adsrtime int for i:=范围帧{value:=synth.。ADSR(0.8,持续时间,0.2,0.1,0.5,持续时间-0.05,sr,adsrtime)adsrtime++帧[i]=波形。Frame(value*l.InterpolateTick(Freq))}返回帧}。

让ADSR信封听起来像我想要的那样,老实说,这是一种古老的反复试验的方式。😅.

我们终于把一切都准备好了,可以谱写这首歌了。下面的代码肯定不漂亮,但是它完成了工作!首先我们生成音符,然后我们创建一个我们想要的形状的波形表。我们将其插入振荡器,然后开始使用所需的音符调用Play函数。

为方便起见,我添加了一些变量来方便地调整歌曲各部分的时长。变量,耶!

Func main(){nm:=GenerateNotes()st,err:=synth。NewTriangleTable(8*1024,11)如果err!=nil{Panic(Err)}OSC,则err:=synth。新查找振荡器(sr,st,0)if err!=nil{Panic(Err)}waitdur:=.1cdecdur:=0.6cagfecdur:=0.5cgcdur:=0.5cgcdur:=0.5//备注:https://www.true-piano-lessons.com/frere-jacques.html输出:=[]波。FRAME{}//Frere Jacues输出=APPEND(OUTPUT,PLAY(osc,";C5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";d5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";E5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";C5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";C5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";d5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";E5";,cdecdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";C5";,cdecdur,nm)...)Output=append(output,play(osc,";wait";,waitdur,nm)...)//dormez-vous output=append(output,play(osc,";E5";,efgdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";F5";,efgdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";G5";,efgdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";WAIT";,0.2,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";E5";,efgdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";F5";,efgdur,nm)...)Output=append(output,play(osc,";G5";,efgdur,nm)...)//sonnez les Matines output=append(output,play(osc,";G5";,gagfedur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";A5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";G5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";F5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";E5";,gagfedur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";C5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";WAIT";,0.2,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";G5";,gagfedur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";A5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";G5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";F5";,gagect dur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";E5";,gagfedur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";C5";,gagect dur,nm)...)Output=append(output,play(osc,";wait";,gagfecdur,nm)...)//dindindon output=append(output,play(osc,";C5";,cgcdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";G5";,cgcdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";C5";,cgcdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(OSC,";WAIT";,0.1,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";C5";,cgcdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";G5";,cgcdur,nm)...)OUTPUT=APPEND(OUTPUT,PLAY(osc,";C5";,cgcdur,nm)...)WFMT:=波浪。NewWaveFmt(1,1,Sr,16,Nil)波。WriteFrames(output,wfmt,";frereJacq.wav";)}。

如果你喜欢这篇文章,并想知道我什么时候写了新帖子,最好的更新方式就是在Twitter上关注我。