質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

852閲覧

Uncaught Error: Go program has already exited の理由がわからない

aaaa____

総合スコア25

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2022/10/25 12:40

編集2022/10/26 11:20

環境

・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

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

3つほど指摘が見つかりました。(ー>追加でもう一つ)

  • osc.Call("connect", "gain")の第二引数が文字列なのはおかしいです。js.Value型gainオブジェクトである必要があります。
  • bpm := 120これはint型になってしまい、後続のnote_length := 60 / bpmの結果は0になってしまいます。
  • osc.Get("frequency").Set(...)はこのやり方では不適切です。ループ終了のタイミングを確認してみましょう。
  • 最後oscをbodyにappandChildしていますが、oscはDOMオブジェクトではないのでエラーになります。(oscの親はAudioContextに設定されていて特に親子関係を操作する必要はありません)

投稿2022/10/25 14:09

編集2022/10/25 23:06
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aaaa____

2022/10/26 11:37 編集

ご指摘ありがとうございます. ひとまず簡単に理解することができた上二つのものは修正し,追記としてコードを上げさせていただきました. 4つ目のご指摘も,理解はまだでしたがとりあえず該当部分を削除いたしました. . エラーは出なくなりましたので,質問の教えてほしいこと自体は解決していただいたのですが,追加でご指摘いただいた部分がありますので,その部分についていくつか質問をさせていただければと思います. 3つ目のご指摘は,周波数をセットする部分がおかしいのでしょうか,ループごとに音が変わって行くことを期待しておいているのですが,実際はランダムの1音しか鳴りません,.音自体はなっているので,指定の仕方などは問題ないように思えるのですが,位置がおかしいということでよろしいでしょうか. ループごとに変えるにはfor文の中に置くしかないと思うのですが,gain.Getの上に置いてみても結果は変わりませんでした.そもそも構造として間違ってしまっているのでしょうか. 4つ目のご指摘において,「oscはDOMオブジェクトではないのでエラーになります」とあるのですが,動いた段階のコードで動いているのはなぜなのでしょうか.追記の部分にoscのappendChildを加えても挙動自体は変わらないように見えました.
nobonobo

2022/10/26 11:44

エラーになるかどうかは未確認でした。ただbody配下のDOMツリーにOSCを加える意義はないと思います。 ループの終了タイミングは確認されましたか?for構文の外にprintln("loop exit")などを書いてみてJSコンソールのログを観てみてください。
aaaa____

2022/10/26 12:36

なくても動作は同じならない方がいいのは確かですね,ありがとうございます. ひとまずfor文の外にfmt.Println("loop exit")を randNoteの定義の次にfmt.Println(randNote)を付け加えてコンソールを見てみたのですが,何回か実行して確かめたところ,最後にrandNoteに入ったものだけがなるようになっていると考えました.音がなり終わる前にloop exitも出力されていますし. 構造的には,JavaScriptのものと同じようにできたと思っているのですが,なぜ最後のところしかならないのでしょうか.
nobonobo

2022/10/26 13:13

120回のfrequencyへのSetは一瞬で終わりますよね。 そして最後の音だけが鳴っているということ。周波数の指定を120回やり切っていて手前の119個は一瞬過ぎて耳に聞こえないというわけです。 そしてその解決方法はサンプルコードなりゲインの設定ですでに使っている方法です。
aaaa____

2022/10/27 11:17

参考にしていたJavaScriptのコードでは周波数を入れるところは"setValueAtTime"を使っていたのを見落としていました. osc.Get("frequency").Call("setValueAtTime", num_to_freq(randNote), ctx.Get("currentTime").Float()+start_t) にすることで期待通りの結果を得ることができました, ありがとうございます.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問