質問編集履歴
3
編集
test
CHANGED
File without changes
|
test
CHANGED
@@ -6,6 +6,15 @@
|
|
6
6
|
### 最終的に目指していること
|
7
7
|
https://www.amazon.co.jp/dp/4873118220?tag=note0e2a-22&linkCode=ogi&th=1&psc=1
|
8
8
|
この本で作っているMonkeyという言語に,組み込み関数としてplay関数を作り,```play(60)```のようにMIDIノートナンバーを引数としてその音を鳴らすことができるようにする.
|
9
|
+
|
10
|
+
まず,Live Codingという,即興で音楽をコーディングによって作るものに使用する言語の設計を考えていまして,そのための基盤(Monkey言語と呼ばれる参考書を見ながら作る対話型独自言語)を質問本文に記載した参考書にしたがってGo言語で作成いたしました.
|
11
|
+
https://interpreterbook.com/waiig_code_1.4.zip
|
12
|
+
その言語に対して,組み込み関数(例えばlen("aiueo");と入力したら5と返してくるようなlen関数のようなもの)として,MIDIノートナンバーを引数として,その音をWebAudioAPIを用いて鳴らすplay()関数を付け加えようと考えています.
|
13
|
+
文字数の都合上,細かい実装した手順などは本文に記載させていただいた記事に任させていただこうとおもうのですが,現状,音を鳴らす機能がない(与えられてMIDIノートナンバーをそのまま返すだけ)play()組み込み関数の追加はすることが出来ました.そのあとに,命令としてplay(60);としたら60の音が鳴るように評価の部分を変更しようとしたのですが,方法が分からない,といったことになっております.
|
14
|
+
|
15
|
+
ブラウザ上で操作するのではなく,あくまでもコーディングとしてターミナル上で命令を記述し,ブラウザではボタンを押すだけでコーディングしたように音が鳴るようにしたいと考えております.
|
16
|
+
|
17
|
+
|
9
18
|
|
10
19
|
|
11
20
|
### 今できていること
|
2
編集
test
CHANGED
File without changes
|
test
CHANGED
@@ -80,3 +80,135 @@
|
|
80
80
|
このサイトにあるように,ブラウザに値を打ち込んでやる方法を取るしかないのでしょうか.
|
81
81
|
|
82
82
|
|
83
|
+
### 追記
|
84
|
+
一つ上のサイトを参考に,以下のようにして,ブラウザにテキストボックスとボタンを配置しそのMIDIノートなんばーの音を鳴らすことは出来ましたが,これは少し趣旨とは異なってしまいます.
|
85
|
+
|
86
|
+
##### index.html
|
87
|
+
```html
|
88
|
+
<html>
|
89
|
+
<head>
|
90
|
+
<meta charset="utf-8"/>
|
91
|
+
<script src="wasm_exec.js"></script>
|
92
|
+
<script>
|
93
|
+
const go = new Go();
|
94
|
+
WebAssembly.instantiateStreaming(fetch("build.wasm"), go.importObject).then((result) => {
|
95
|
+
go.run(result.instance);
|
96
|
+
});
|
97
|
+
</script>
|
98
|
+
</head>
|
99
|
+
<body>
|
100
|
+
<input type="text" id="noteNum" />
|
101
|
+
<button onClick="play('noteNum');" id="playButton">play</button>
|
102
|
+
</body>
|
103
|
+
</html>
|
104
|
+
```
|
105
|
+
|
106
|
+
##### webassembly.go
|
107
|
+
```go
|
108
|
+
package main
|
109
|
+
|
110
|
+
import (
|
111
|
+
"syscall/js"
|
112
|
+
"time"
|
113
|
+
"math/rand"
|
114
|
+
"math"
|
115
|
+
"fmt"
|
116
|
+
"strconv"
|
117
|
+
)
|
118
|
+
|
119
|
+
func num_to_freq (notenum int) float64{
|
120
|
+
// 基準音から何音高い/低いかを計算する
|
121
|
+
from_concert_a := notenum - 69
|
122
|
+
// 周波数を実際に計算する
|
123
|
+
// 十二平均律では2音の最小の周波数差は`2^(1/12)`となる
|
124
|
+
freq := math.Pow(2, float64(from_concert_a) / 12) * 440;
|
125
|
+
return freq;
|
126
|
+
}
|
127
|
+
|
128
|
+
func registerCallbacks() {
|
129
|
+
js.Global().Set("play", js.FuncOf(play))
|
130
|
+
}
|
131
|
+
|
132
|
+
func textToStr(v js.Value) string {
|
133
|
+
return js.Global().Get("document").Call("getElementById", v.String()).Get("value").String()
|
134
|
+
}
|
135
|
+
|
136
|
+
func play(this js.Value, args []js.Value) interface{} {
|
137
|
+
value := textToStr(args[0])
|
138
|
+
|
139
|
+
noteNum, _ := strconv.Atoi(value)
|
140
|
+
ctx := js.Global().Get("AudioContext").New()
|
141
|
+
osc := js.Global().Get("OscillatorNode").New(ctx)
|
142
|
+
gain := js.Global().Get("GainNode").New(ctx)
|
143
|
+
gain.Get("gain").Set("value", 0)
|
144
|
+
|
145
|
+
bpm := 120.0
|
146
|
+
note_length := 60.0 / bpm
|
147
|
+
|
148
|
+
osc.Call("connect", gain)
|
149
|
+
gain.Call("connect", ctx.Get("destination"))
|
150
|
+
osc.Call("start")
|
151
|
+
|
152
|
+
for n := 0; n < 60; n++{
|
153
|
+
start_t := float64(n) * note_length
|
154
|
+
end_t := start_t + 1.0
|
155
|
+
osc.Get("frequency").Call("setValueAtTime", num_to_freq(noteNum), ctx.Get("currentTime").Float()+start_t)
|
156
|
+
gain.Get("gain").Call("setValueAtTime", 0.3, ctx.Get("currentTime").Float()+start_t)
|
157
|
+
gain.Get("gain").Call("setValueAtTime", 0., ctx.Get("currentTime").Float()+end_t)
|
158
|
+
}
|
159
|
+
osc.Set("type", "sawtooth")
|
160
|
+
return nil
|
161
|
+
}
|
162
|
+
|
163
|
+
func main() {
|
164
|
+
|
165
|
+
registerCallbacks()
|
166
|
+
|
167
|
+
// グローバルオブジェクト(window)を取得します
|
168
|
+
window := js.Global()
|
169
|
+
|
170
|
+
// document オブジェクトを取得します
|
171
|
+
document := window.Get("document")
|
172
|
+
|
173
|
+
// bodyを取得します
|
174
|
+
body := document.Get("body")
|
175
|
+
|
176
|
+
// ボタンのDOMを作成し、Clickイベントを設定します
|
177
|
+
btn := document.Call("createElement", "button")
|
178
|
+
btn.Set("textContent", "music start!")
|
179
|
+
btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} {
|
180
|
+
ctx := js.Global().Get("AudioContext").New()
|
181
|
+
osc := js.Global().Get("OscillatorNode").New(ctx)
|
182
|
+
gain := js.Global().Get("GainNode").New(ctx)
|
183
|
+
gain.Get("gain").Set("value", 0)
|
184
|
+
|
185
|
+
bpm := 120.0
|
186
|
+
note_length := 60.0 / bpm
|
187
|
+
|
188
|
+
osc.Call("connect", gain)
|
189
|
+
gain.Call("connect", ctx.Get("destination"))
|
190
|
+
osc.Call("start")
|
191
|
+
|
192
|
+
rand.Seed(time.Now().UnixNano())
|
193
|
+
|
194
|
+
for n := 0; n < 120; n++{
|
195
|
+
randNote := rand.Intn(20) + 55
|
196
|
+
fmt.Println(randNote)
|
197
|
+
start_t := float64(n) * note_length
|
198
|
+
end_t := start_t + 1.0
|
199
|
+
osc.Get("frequency").Call("setValueAtTime", num_to_freq(randNote), ctx.Get("currentTime").Float()+start_t)
|
200
|
+
gain.Get("gain").Call("setValueAtTime", 0.3, ctx.Get("currentTime").Float()+start_t)
|
201
|
+
gain.Get("gain").Call("setValueAtTime", 0., ctx.Get("currentTime").Float()+end_t)
|
202
|
+
}
|
203
|
+
osc.Set("type", "sawtooth")
|
204
|
+
return nil
|
205
|
+
}))
|
206
|
+
// ボタンをbodyに追加します
|
207
|
+
body.Call("appendChild", btn)
|
208
|
+
|
209
|
+
// プログラムが終了しないように待機します
|
210
|
+
select {}
|
211
|
+
|
212
|
+
}
|
213
|
+
```
|
214
|
+
|
1
編集
test
CHANGED
File without changes
|
test
CHANGED
@@ -75,3 +75,8 @@
|
|
75
75
|
}
|
76
76
|
```
|
77
77
|
|
78
|
+
### 調べてでてきたもの
|
79
|
+
https://ludwig125.hatenablog.com/entry/2022/03/06/080759
|
80
|
+
このサイトにあるように,ブラウザに値を打ち込んでやる方法を取るしかないのでしょうか.
|
81
|
+
|
82
|
+
|