Q&A
環境
・macOS Big Sur ver. 12.6 (21G115)
・MacBook Air M1, 2020 メモリ 8GB
・go1.18.3 darwin/arm64
やっていること
JavaScript
1let ctx = new AudioContext() 2// オシレータ 3let osc = new OscillatorNode(ctx) 4osc.frequency.value = 440 5// 音量制御用のノード 6let gain = new GainNode(ctx) 7gain.gain.value = 0 8 9// 接続してオシレータを開始 10osc.connect(gain) 11gain.connect(ctx.destination) 12osc.start() 13 14// テンポに従って音の鳴り始める(音量が0.3になる)時間と鳴り終わる(音量が0になる)時間を設定する 15let bpm = 120 16let note_length = 60 / bpm 17// 120回分のメトロノームの音を設定する 18for (let n = 0; n < 120; n++) { 19 // 音の開始・終了時間を計算する 20 let start_time = n * note_length; 21 let end_time = start_time + 0.05 22 // gain (音量)を時間指定で設定することで鳴らしたり止めたりする 23 gain.gain.setValueAtTime(0.3, ctx.currentTime + start_time) 24 gain.gain.setValueAtTime(0.0, ctx.currentTime + end_time) 25 // 小節の最初の音だけ高くする 26 if (n % 4 == 0) { 27 osc.frequency.setValueAtTime(880, ctx.currentTime + start_time) 28 } else { 29 osc.frequency.setValueAtTime(440, ctx.currentTime + start_time) 30 } 31}
このコードを元にしてGo言語で120泊の間,ランダムの周波数で音を鳴らすようにしたいと考え,次のようにコードを書きました.
webassembly.go
1package main 2 3import ( 4 "syscall/js" 5 "time" 6 "math/rand" 7 "math" 8) 9 10func num_to_freq (notenum int) float64{ 11 // 基準音から何音高い/低いかを計算する 12 from_concert_a := notenum - 69 13 // 周波数を実際に計算する 14 // 十二平均律では2音の最小の周波数差は`2^(1/12)`となる 15 freq := math.Pow(2, float64(from_concert_a) / 12) * 440; 16 return freq; 17} 18 19func main() { 20 // グローバルオブジェクト(window)を取得します 21 window := js.Global() 22 23 // document オブジェクトを取得します 24 document := window.Get("document") 25 26 // bodyを取得します 27 body := document.Get("body") 28 29 // ボタンのDOMを作成し、Clickイベントを設定します 30 btn := document.Call("createElement", "button") 31 btn.Set("textContent", "music start!") 32 btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} { 33 34 ctx := js.Global().Get("AudioContext").New() 35 osc := js.Global().Get("OscillatorNode").New(ctx) 36 gain := js.Global().Get("GainNode").New(ctx) 37 gain.Get("gain").Set("value", 0) 38 39 bpm := 120 40 note_length := 60 / bpm 41 42 osc.Call("connect", "gain") 43 gain.Call("connect", ctx.Get("destination")) 44 osc.Call("start") 45 46 rand.Seed(time.Now().UnixNano()) 47 48 for n := 0; n < 120; n++{ 49 randNote := rand.Intn(20) + 55 50 start_t := float64(n * note_length) 51 end_t := start_t + 0.5 52 gain.Get("gain").Call("setValueAtTime", 0.3, ctx.Get("currentTime").Float()+start_t) 53 gain.Get("gain").Call("setValueAtTime", 0., ctx.Get("currentTime").Float()+end_t) 54 osc.Get("frequency").Set("value", num_to_freq(randNote)) 55 } 56 osc.Set("type", "sawtooth") 57 body.Call("appendChild", osc) 58 return nil 59 })) 60 // ボタンをbodyに追加します 61 body.Call("appendChild", btn) 62 63 // プログラムが終了しないように待機します 64 select {} 65 66}
しかし,次のようなエラーがブラウザ側のコンソールで出てしまいました.
console
1Uncaught Error: Go program has already exited 2 at globalThis.Go._resume (wasm_exec.js:536:11) 3 at HTMLButtonElement.<anonymous> (wasm_exec.js:549:8)
調べたこと
https://www.kabuku.co.jp/developers/annoying-go-wasm
このサイトを見ましたが,select{}で待機させることはできていますし,これではないなとなりました.
https://blog.narumium.net/2019/03/05/%E3%80%90go%E3%80%91webassembly%E3%82%92%E3%83%93%E3%83%AB%E3%83%89%E3%81%97%E3%81%A6%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%A7%E5%8B%95%E4%BD%9C%E3%81%95%E3%81%9B%E3%82%8B/
ここを見てみてもチャンネルのロックと同じようなことが書いてあり,対処方が得られませんでした.
動いた段階のコード
ランダムに1音をずっと鳴らすと言う次のコードは普通に動いていました.
webassembly.go
1package main 2 3import ( 4 "syscall/js" 5 "time" 6 "math/rand" 7 "math" 8) 9 10func num_to_freq (notenum int) float64{ 11 // 基準音から何音高い/低いかを計算する 12 from_concert_a := notenum - 69 13 // 周波数を実際に計算する 14 // 十二平均律では2音の最小の周波数差は`2^(1/12)`となる 15 freq := math.Pow(2, float64(from_concert_a) / 12) * 440; 16 return freq; 17} 18 19func main() { 20 // グローバルオブジェクト(window)を取得します 21 window := js.Global() 22 23 // document オブジェクトを取得します 24 document := window.Get("document") 25 26 // bodyを取得します 27 body := document.Get("body") 28 29 // ボタンのDOMを作成し、Clickイベントを設定します 30 btn := document.Call("createElement", "button") 31 btn.Set("textContent", "music start!") 32 btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} { 33 ctx := js.Global().Get("AudioContext").New() 34 osc := js.Global().Get("OscillatorNode").New(ctx) 35 rand.Seed(time.Now().UnixNano()) 36 randNote := rand.Intn(80) 37 osc.Get("frequency").Set("value", num_to_freq(randNote)) 38 osc.Set("type", "sine") 39 osc.Call("connect", ctx.Get("destination")) 40 osc.Call("start") 41 body.Call("appendChild", osc) 42 return nil 43 })) 44 // ボタンをbodyに追加します 45 body.Call("appendChild", btn) 46 47 // プログラムが終了しないように待機します 48 select {} 49 50}
コードを書く際に参考にしたサイト
https://teratail.com/questions/jzo8yydnsl73wy#reply-pofq0y1rw8moek
ここの質問のところに書いてあります.
教えてほしいこと
エラーの対処法
追記
intとなってしまっていて計算結果が0になってしまうところをfloat64に直しました.
またgainの引数の取り方を修正しました.
この状態でビルドして実行し.ブラウザに行くと,ボタンを押すと,ランダムな1音がずっと残る状態になりました.
go
1package main 2 3import ( 4 "syscall/js" 5 "time" 6 "math/rand" 7 "math" 8) 9 10func num_to_freq (notenum int) float64{ 11 // 基準音から何音高い/低いかを計算する 12 from_concert_a := notenum - 69 13 // 周波数を実際に計算する 14 // 十二平均律では2音の最小の周波数差は`2^(1/12)`となる 15 freq := math.Pow(2, float64(from_concert_a) / 12) * 440; 16 return freq; 17} 18 19func main() { 20 // グローバルオブジェクト(window)を取得します 21 window := js.Global() 22 23 // document オブジェクトを取得します 24 document := window.Get("document") 25 26 // bodyを取得します 27 body := document.Get("body") 28 29 // ボタンのDOMを作成し、Clickイベントを設定します 30 btn := document.Call("createElement", "button") 31 btn.Set("textContent", "music start!") 32 btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} { 33 ctx := js.Global().Get("AudioContext").New() 34 osc := js.Global().Get("OscillatorNode").New(ctx) 35 gain := js.Global().Get("GainNode").New(ctx) 36 gain.Get("gain").Set("value", 0) 37 38 bpm := 120.0 39 note_length := 60.0 / bpm 40 41 osc.Call("connect", gain) 42 gain.Call("connect", ctx.Get("destination")) 43 osc.Call("start") 44 45 rand.Seed(time.Now().UnixNano()) 46 47 for n := 0; n < 120; n++{ 48 randNote := rand.Intn(20) + 55 49 start_t := float64(n) * note_length 50 end_t := start_t + 0.5 51 gain.Get("gain").Call("setValueAtTime", 0.3, ctx.Get("currentTime").Float()+start_t) 52 gain.Get("gain").Call("setValueAtTime", 0., ctx.Get("currentTime").Float()+end_t) 53 osc.Get("frequency").Set("value", num_to_freq(randNote)) 54 } 55 osc.Set("type", "sawtooth") 56 return nil 57 })) 58 // ボタンをbodyに追加します 59 body.Call("appendChild", btn) 60 61 // プログラムが終了しないように待機します 62 select {} 63 64} 65
回答1件
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
2022/10/26 11:37 編集
2022/10/26 11:44
2022/10/26 12:36
2022/10/26 13:13
2022/10/27 11:17