環境
・macOS Big Sur ver. 11.5.1(20G80) -> 12.6 (21G115)
・MacBook Air M1, 2020 メモリ 8GB
・go1.18.3 darwin/arm64
最終的に目指していること
https://www.amazon.co.jp/dp/4873118220?tag=note0e2a-22&linkCode=ogi&th=1&psc=1
この本で作っているMonkeyという言語に,組み込み関数としてplay関数を作り,play(60)
のようにMIDIノートナンバーを引数としてその音を鳴らすことができるようにする.
まず,Live Codingという,即興で音楽をコーディングによって作るものに使用する言語の設計を考えていまして,そのための基盤(Monkey言語と呼ばれる参考書を見ながら作る対話型独自言語)を質問本文に記載した参考書にしたがってGo言語で作成いたしました.
https://interpreterbook.com/waiig_code_1.4.zip
その言語に対して,組み込み関数(例えばlen("aiueo");と入力したら5と返してくるようなlen関数のようなもの)として,MIDIノートナンバーを引数として,その音をWebAudioAPIを用いて鳴らすplay()関数を付け加えようと考えています.
文字数の都合上,細かい実装した手順などは本文に記載させていただいた記事に任させていただこうとおもうのですが,現状,音を鳴らす機能がない(与えられてMIDIノートナンバーをそのまま返すだけ)play()組み込み関数の追加はすることが出来ました.そのあとに,命令としてplay(60);としたら60の音が鳴るように評価の部分を変更しようとしたのですが,方法が分からない,といったことになっております.
ブラウザ上で操作するのではなく,あくまでもコーディングとしてターミナル上で命令を記述し,ブラウザではボタンを押すだけでコーディングしたように音が鳴るようにしたいと考えております.
今できていること
https://note.com/clean_camel994/n/ne3dd24b6f116
この記事でやっているように,コードの段階でMIDIノートナンバーを入れてGo言語のwebassembly変換によってWebAudioAPIの音を鳴らすことはできています.
https://note.com/clean_camel994/n/n74f27e86abb8
この記事で書いてあるように,処理を意味のない簡単なものに置き換えたplay関数を認識させることもできています.
わからないこと
対話型インタプリタで入力された数字を用いてWebAudioAPIで音を鳴らす方法.
main.goを実行する前にビルドをしてwasm変換をしていると思うのですが,入力による引数で音を鳴らすことは可能なのでしょうか.
ひとまず,次のwebseembly.goでランダムにMIDIノートナンバーを入れている部分を入力で指定できるようにする方法でも助かります.(この記事のものです:https://note.com/clean_camel994/n/ne3dd24b6f116)
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://ludwig125.hatenablog.com/entry/2022/03/06/080759
このサイトにあるように,ブラウザに値を打ち込んでやる方法を取るしかないのでしょうか.
追記
一つ上のサイトを参考に,以下のようにして,ブラウザにテキストボックスとボタンを配置しそのMIDIノートなんばーの音を鳴らすことは出来ましたが,これは少し趣旨とは異なってしまいます.
index.html
html
1<html> 2 <head> 3 <meta charset="utf-8"/> 4 <script src="wasm_exec.js"></script> 5 <script> 6 const go = new Go(); 7 WebAssembly.instantiateStreaming(fetch("build.wasm"), go.importObject).then((result) => { 8 go.run(result.instance); 9 }); 10 </script> 11 </head> 12 <body> 13 <input type="text" id="noteNum" /> 14 <button onClick="play('noteNum');" id="playButton">play</button> 15 </body> 16</html>
webassembly.go
go
1package main 2 3import ( 4 "syscall/js" 5 "time" 6 "math/rand" 7 "math" 8 "fmt" 9 "strconv" 10) 11 12func num_to_freq (notenum int) float64{ 13 // 基準音から何音高い/低いかを計算する 14 from_concert_a := notenum - 69 15 // 周波数を実際に計算する 16 // 十二平均律では2音の最小の周波数差は`2^(1/12)`となる 17 freq := math.Pow(2, float64(from_concert_a) / 12) * 440; 18 return freq; 19} 20 21func registerCallbacks() { 22 js.Global().Set("play", js.FuncOf(play)) 23} 24 25func textToStr(v js.Value) string { 26 return js.Global().Get("document").Call("getElementById", v.String()).Get("value").String() 27} 28 29func play(this js.Value, args []js.Value) interface{} { 30 value := textToStr(args[0]) 31 32 noteNum, _ := strconv.Atoi(value) 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 for n := 0; n < 60; n++{ 46 start_t := float64(n) * note_length 47 end_t := start_t + 1.0 48 osc.Get("frequency").Call("setValueAtTime", num_to_freq(noteNum), ctx.Get("currentTime").Float()+start_t) 49 gain.Get("gain").Call("setValueAtTime", 0.3, ctx.Get("currentTime").Float()+start_t) 50 gain.Get("gain").Call("setValueAtTime", 0., ctx.Get("currentTime").Float()+end_t) 51 } 52 osc.Set("type", "sawtooth") 53 return nil 54} 55 56func main() { 57 58 registerCallbacks() 59 60 // グローバルオブジェクト(window)を取得します 61 window := js.Global() 62 63 // document オブジェクトを取得します 64 document := window.Get("document") 65 66 // bodyを取得します 67 body := document.Get("body") 68 69 // ボタンのDOMを作成し、Clickイベントを設定します 70 btn := document.Call("createElement", "button") 71 btn.Set("textContent", "music start!") 72 btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} { 73 ctx := js.Global().Get("AudioContext").New() 74 osc := js.Global().Get("OscillatorNode").New(ctx) 75 gain := js.Global().Get("GainNode").New(ctx) 76 gain.Get("gain").Set("value", 0) 77 78 bpm := 120.0 79 note_length := 60.0 / bpm 80 81 osc.Call("connect", gain) 82 gain.Call("connect", ctx.Get("destination")) 83 osc.Call("start") 84 85 rand.Seed(time.Now().UnixNano()) 86 87 for n := 0; n < 120; n++{ 88 randNote := rand.Intn(20) + 55 89 fmt.Println(randNote) 90 start_t := float64(n) * note_length 91 end_t := start_t + 1.0 92 osc.Get("frequency").Call("setValueAtTime", num_to_freq(randNote), ctx.Get("currentTime").Float()+start_t) 93 gain.Get("gain").Call("setValueAtTime", 0.3, ctx.Get("currentTime").Float()+start_t) 94 gain.Get("gain").Call("setValueAtTime", 0., ctx.Get("currentTime").Float()+end_t) 95 } 96 osc.Set("type", "sawtooth") 97 return nil 98 })) 99 // ボタンをbodyに追加します 100 body.Call("appendChild", btn) 101 102 // プログラムが終了しないように待機します 103 select {} 104 105}

回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/11/01 07:06 編集
2022/11/01 07:42
2022/11/01 07:54 編集
2022/11/01 08:04
2022/11/01 08:38
2022/11/01 11:14
2022/11/01 12:16
2022/11/02 11:49 編集
2022/11/02 13:18
2022/11/04 01:04