S.F.@SFPGMR
今さらクォータニオンを学習中。。
クォータニオン (Quaternion) を総整理! ~ 三次元物体の回転と姿勢を鮮やかに扱う ~ - Qiita # 0. はじめに: クォータニオンについて思うこと
はじめまして!
[NTTデータ数理システム](http://www.msi.co.jp/)で[機械学習](https://ja.wikipedia.org/wiki/%E6%A9%...
あかん。難しい。今まで3次元空間での回転は軸固定でx,y,zのオイラー角で表してたのだが、回転軸ベクトルとラジアンを使って3次元の回転を表す方法をいまさら理解してさらにクォータニオンに行き着いたわけだが、朝一でこれはきつい。
回転のアニメーションをオイラー角でしていると妙な違和感があって。x,y,zを同じ角速度で回してるのになんかこう回転速度が変化するような感じになってしまうのはなんでかな。。と昔から思ってた。
No Image8x8x8Voxelの表示 - YouTube
voxファイルの表示 voxファイルの表示
x,y,z軸固定でオイラー角でx,y,zをx,z,yの順番に回転行列を掛け合わせるのだが。各軸周りの回転が等速になってるように見えんところが気になるところだ。
これはひょっとして「ジンバル・ロック」によるものなのかと考えたりとかしてたり。わからんけどね。
そこでまあ「ジンバル・ロック」のない回転とやらはどんな感じなのかと思ったわけだ。そこで思い出したのが「クォータニオン(四元数)」である。
しかし、「ジンバル・ロック」とやらはいくら頭の中で考えても全くイメージできんわけだ。これが。。
ジンバル - Wikipedia
x,y,zの各軸は互いに直交してるわけで、なんで軸が重なるねん??となって頭が混乱するわけだ。
でまあこれを読んでみたわけだが。理解しとらんが「いいね」をつけとこう。。
クォータニオン (Quaternion) を総整理! ~ 三次元物体の回転と姿勢を鮮やかに扱う ~ - Qiita # 0. はじめに: クォータニオンについて思うこと
はじめまして!
[NTTデータ数理システム](http://www.msi.co.jp/)で[機械学習](https://ja.wikipedia.org/wiki/%E6%A9%...
こっちはクォータニオンを使うに必要な知識が書いてある。
No Imageその10 クォータニオンを学んでみよう!
クォータニオンはさておいて、軸ベクトルとラジアン1個で任意の回転をあらわすことができることは理解したのである。。
軸ベクトルをつかうと例えばx軸周りの回転は
(1,0,0)になるわなあ。そしてx軸かつy軸周りの回転は
(1,1,0)となるのだろうなと思う。でまあy軸だけ逆回転させたくば(1,-1,0)とすればよいのかなと。たしかに考えやすい気がするねえ。。考え方が間違って理解してなければの話だけど。。
複数のボクセルデータを表示するために今改造してるところなんだけど、これを機会にオイラー角での指定をやめて、軸ベクトル+ラジアンで回転を表すようにしてみるつもりである。
複数のボクセルデータを表示するのはサクっと作ってしまうつもりだったんだけど、WebGL2.0の理解が深まるにつれいろいろ盛り込みたいことが増えてきてる。盛り込みたいというか変更したいというか。。
ここまでの記録をまとめた。
自作WASMプリプロセッサとWeb要素技術を使ってレトロ・シューティングを作る - Togetter 自作WASMプリプロセッサとWeb要素技術を使ってPSGエミュレータを軽く作ってみた。そしていまレトロ・シューティングを作っている(進行中)。
160x100pxへのこだわりはやはり「N-TYPE」を見てからですな。これはほんとにすごい。
ようやく複数オブジェクトの表示ができるようになった。。
これがそう。なんの変哲もないポリゴンの立方体のように見えるが実はポイントスプライトで表示しているという。
No ImageYouTube
ここでUBO使おうと思ったんだけど、現時点の実装では思ったほどUniformバッファの恩恵を受けられないことが分かったのでやめた。。
続いて複数キャラを同時に表示する部分を作らんといかんな。。
違うわ。複数の異なるオブジェクトだった。。
ようやく所望のものができた感じですな。。
3Dスプライトといったところか。
この粗々なピクセルの感じがレトロっぽくて好みなんだよね。。
オブジェクトの描画は登場キャラの頂点情報を1つのArrayBufferにまとめて、drawArraysにオフセットをあたえることでキャラの切り替えをするわけですな。
gl.drawArrays(gl.POINTS, objInfo.vindex,objInfo.count);
こうすればバッファ切り替えしなくても良くなる。でもバッファの切り替えなんてポインタの切り替えコストくらいじゃないかと思うからメリットはそんなにないかな。。
オブジェクトの情報も1つの大きなArrayBufferの1区画で管理するようにして、それをwasmから見えるようにする。wasmからそのメモリに書き込むことでキャラクターをコントロールするのですな。
512個ほどキャラを表示。混沌として何が何だか。。
現在の成果物。。Z座標に応じて輝度を暗くしてみている。フォグのかわり。。
voxファイルの表示 voxファイルの表示
次は入力とサウンド関係のめどをつけるか。。
DataView,ArrayBuffe,TypedArrayを使うとJSか?と思うほど型やメモリアドレスを意識したメモリアクセスができるのがすごいなあ。
仮想テキストVRAMとボクセルデータ用のスプライトバッファを1つのArrayBufferにまとめることができた。更に同じArrayBufferに入力やサウンドのI/OをマッピングすればwasmモジュールからメモリマップドI/Oで操作できるようになる。
ここまでは想定通り実装できているわけですな。
しかしWebGL2.0のシェーダーも整数値のビット演算ができるようになったから、頂点情報にフラグ情報をビット単位で詰めて渡せるようになったので頂点あたりのバイト数も節約できたわ。
ここまでをnoteにまとめた。
MagicaVoxelデータをWebGLのポイント・リスト(ポイント・スプライト)で疑似表示する|sfpgmr|note mwasmのプリプロセッサをある程度完成させたところで、それ使ってゲームを作ろうとしている。wasmでゲームを作ろうと思ったら、リソースをメモリにマップしてアクセスできるようにしてやるか、JS側でリソースアクセスのための関数を作ってwasmにエクスポートする必要がある。JS側の関数を呼び出すのは安直でよいが、呼び出しのオーバーヘッドが気になるのでメモリにマップしてそこにwasmに書き込ませ、JS側でメモリをデコードして各種リソースにアクセスするようにすることにした。 内容はレトロ風の横スクロールシューティングである。パワーアップ可能な自機が敵をなぎ倒して進めるようなタイプのやつで
入力部分は過去作ったコードを若干手直しして利用することにした。自分が作ったコードにしては割とよくできてる方でよかった。。次はサウンド部分ですな。。これはJSで作った波形メモリ音源をwasm
で書き直すつもりだがさてどうなるか。
ただタッチパネルには対応してないんだな。これが。。
これが一応入力クラスのテスト画面。ふつうはキーボードでゲームパッドを刺すとそっちに切り替わる。ボタンカスタマイズもできるようにしてるけどUIでは割愛。。
https://t.co/HMQLUKpBPZ No Imageゲーム用入力クラスの作成
さて、サウンド機能を実装するか。。以前JSで作った波形メモリ音源をwasm化しようかなと思ったが、PSGエミュレーション+SCCエミュレーション+αにしようかなと思ってる。YM2203相当のFM音源もいれてみたいと思うけどねえ。AudioWorklet+wasmでそこまでできるだろうか。。
とりあえずPSGエミュレータにSCC相当の音源を載せることからはじめるか。シーケンサー機能も併せて作ろう。これはMIDIシーケンスデータを再生する仕様にしようかね。。
以前作った波形メモリ音源はWebAudioベッタリの仕様で、ゲインノードでエンベロープ・ジェネレータを作ってたりしてた。今回はなるたけAudioworklet内で処理を完結しようと思ってるので、ほぼ作り直しになる。作り直すならSCCに近いものを...と思ったわけ。
できる限り整数(固定少数)を使ってチマチマ作るつもり。よって作業時間が相当かかりそうな気がする。
FM音源もあの出音の割には簡素なしくみだし、C/C++のソースならゴロゴロ転がってるのでそれを移植するだけで済みそうだよな。。
SCCの情報
No Image
PSG3音、SCC5音、4オペFM3音、SID3音、8ビットDA2音くらいのスペックにできれば申し分ないんだけどなあ。。私にできるだろうか。言うても先人の遺産が大量にあるので、できんことはないだろうけどね。。ほぼ全部C/C++ のソースコードあるしなあ。私はチマチマwasm
に書き換えるだけなんだけどね。。
MAMEのコードがすごいんだよなあ。
しかしなんでこんなに音源を豊富にしたいかというと、単なる趣味ですが。SPC700なんかも興味あるんだよなあ。。ゲームコンソールの中で唯一興味のある音源ですわ。。
SPC700も調べて過去にブログ記事にしてみたんだけど、なぜかAD-PCMの解説みたいになっちゃった。。
No Image
完全なエミュレーションをするのではなく、オシレータをFMか波形メモリかSIDの波形選択としてあとはフィルタとEGを作ればいいだけのような気もするな。。
結局チップに足りない機能はソフトウェアで補完して、最終的にはそのような形になるもんね。。
MAMEのソースコード久しぶりに読もうと思ってリポジトリクローンしたら結構大きいな。。
mame/src/devices/sound のソースがもう宝の山というか。「サウンドチップ・エミュレータの聖地」といっても過言ではない。。ありがたく拝読させていただこう。
やっぱりあれだよな。オシレータをPSG/SCC/SID/FMとすればいけるよなあ。コントロールI/Fはオリジナルでいこう。といってもEGのかかり方はそれぞれ(
SID/FM)だからそこをどう考えるかだな。「まんま」の音を出すつもりではなくて、いちおうオリジナル音源にするつもりだからな。
MAMEの6581(SID)ソースのオシレータ部分を見るとやっぱり波形テーブルから読みだしてピッチ変える感じになってるんだよなあ。なのでこれも波形メモリがROMの音源の一種とみることができるな。
いやしかし、SIDの設計はPSGチップとしては秀逸だと思うなあ。。
結局波形メモリにするとSCC/PSG/SIDのオシレータはカバーできるということになるな。だけどSCC/PSGとSIDはピッチの狂い方がそれぞれ異なるようだから、そこをどうするかだよなあ。ピッチは合わすようにするか。オリジナル音源だということをすぐ忘れそうになるなあ。。
自分で調べたドキュメントも見直すか。。
No Image
ほとんど内容忘れとるわ。。
SIDはほんと「シンセ」の落としてるわ。フィルタがあるのが大きいんだよな。やっぱり。
No Image
このSIDチップの存在は当時雑誌で知った。なのでC64が欲しくて仕方なかった。しかし結構高かったんだよな。日本でのC64は。
コモドール64miniを買ってしまった。。以外に安かったので。。
No ImageAmazon.co.jp: コモドール64 mini
ということで間接的だが長年の夢がかなったわ。。
波形メモリのオシレータ+PSGオシレータ、FM変調、リング変調、オシレータシンク、フィルタ、アンプ・フィルタEGで構成すればまあ所望のものになるよなあ。普通のシンセの構成ですがな。。
波形メモリの内容を6581のにすればSIDっぽい音も鳴るだろうし。問題はフィルタ特性かあ。。
まあとりあえずはSCCのまがい物を作るところから始めるとするか。
実装をし始めた。SCCの音源をBGMにとスペースマンボウの動画を流しながらやってたら、聴いてるうちにやっぱりEG必要だよなあ、LFO
必要だよなあ、フィルタも最初からあったほうがいいよなあ。。とかなってきて、結局普通の波形メモリ音源を実装する感じになってる。。
SCC自体波形再生とピッチ、ボリュームだけなので、EGやLFOはソフトウェアで補ってる。そこも作らんといかんので音源の仕様として入れこんでしまおう思ったわけ。全部ソフトウェアになるからね。結局。。
オシレータは差し替え可能にしようかなと思う。そうすれば大半の音源はサポートできるんではないかなと。。
ちょっとwasm用のプリプロセッサに手を加えようと思ってる。最低やらないといかんのは
・リニアメモリの利用開始オフセット指定
・構造体による相対オフセット・アドレス指定
ほんとはマクロ機能も付けたい&記法も見直したいところだが、それはできればということで。。
プリプロセッサの機能追加は思いのほか簡単だった。とりあえずこれでいこうか。マクロ機能はどうするかなあ。久しぶりにいじってみると独自記法の部分がなんだかなあ。。って感じになっている。
メモリのオフセット定義もJSでもなくかといってS式でもなく、C風の表記になってるんだよね。ここはS式にしておけばよかったかなあと今は思ってる。
ようやく波形テーブルを指定のピッチ・ボリュームで再生するWASMを書き終えたところ。これからUIとAudioWorkletとのつなぎコードを書いて、そのあとデバッグしようと思う。EG・LFO・フィルタはそのあとだなあ。
波形テーブルはSCCに倣って8ビット×32サンプルの波形を5CH持たせている。でまあこれを指定ピッチ(Hz)で鳴らすのをどうしようかと高校野球観戦(千葉県大会)の合間に考えていた。
画面はPSGエミュレータのを少し改造して、これに波形エディットするUIを加えればいいかなあと思っている。 https://t.co/gjQ79kx24X
今週はUIを作って音を鳴らすところまでを目標とするか。。そのあとEG・LFO・フィルタを作ればまあなんとかなるだろう。
テスト用のUIを構築中。。 https://t.co/QdnXNaRgYX
ここまで作りかけてあれなのだが、設計を見直すことにした。というか昔書いた古いコードの改良で済ますことにしたといったほうがいいだろうか。古いコードというのはこれなんだけど。
2dshooting2/audio.js at master · sfpgmr/2dshooting2 · GitHub HTML5,WebGL,WebAudioで縦スクロールシューティングゲームを作る. Contribute to sfpgmr/2dshooting2 development by creating an account on GitHub.
いろいろ考えると、AudioWorkletの中でいろいろやるのはパフォーマンス的にもまたWeb Audio APIを生かすという面でもあまり意味はないなあ。。と考えた。
オシレータ部分のみをAudioWorkletにして、そこをwasmで書く感じに変えようかなと思ったんだよね。考えはまた変わるかもしれんけどもね。。
例えばPSGのオシレータ部分だけとかね。
今得られた知見(wasmとかAudioWorklet)を使って書き直すとどうなるのかといったところ。。
とはいえ昔書いたコードもすっかり忘れているのでソースコード読んで理解しなくてはならんのだが(笑)。シーケンサ部分はモハヨナオさんのmml-parser使ってるんだよね。確か。
私が持ってるchuwiのandroid tabでPSGエミュレータを動かしてみたんだけど、やっぱりもたつく。もたつくと音がとぎれとぎれになる。何が原因かなと考えた。私のコードの書き方が悪いのかはそれは置いといて。
ひとつ思ったのはJSからwasmへのコールがやっぱり言語境界を超えるので遅いのかなと。この記事を過去に読んだからなんだけど。
Calls between JavaScript and WebAssembly are finally fast 🎉 - Mozilla Hacks - the Web developer blog At Mozilla, we want WebAssembly to be as fast as it can be. This started with its design, which gives it great throughput. Then we improved load times with a ...
ちなみにPSGエミュレータというのはこれなんだけどね。
No ImageWASMでPSG Emulatorを作る
この記事によるとJS->wasmへの呼び出しは1億回で5500msだと書いてある。とすると1回あたり約0.055μsということになりますな。
サンプリングレートが48khzのとき、AudioWorkletが呼び出される周期は
1/48000 * 1000 * 1000 * 128 = 2666μs
となる。1サンプルあたりだと約20.8μsか。a-rateのパラメータを考えると1サンプルごとに処理しなくてはならないから0.055*128=7.04μsか。。そんなに大きな影響はないのかな。
あ、ここで自分の環境での計測ができるのか。。
No Image
あら、JStoJSとJStoWasmと関数呼び出し比較するとJsTowasm関数呼び出しのほうが2倍以上遅いみたいだな。まあ言語境界におけるオーバーヘッドがあるのはしょうがないわな。 https://t.co/Ye8j8vAqvV
ちなみにさっきのはchromeでの話で、firefoxだとJsToWasmのほうが速くなるんだな。。 https://t.co/KWBIaXfgKa
これはなんでかというと先ほどの記事に書いてあるんだな。トランポリン関数を介さずネイティブ同士で呼び出すようにして、呼び出しコストを削減してるんだよな。簡単にいうと。。
Calls between JavaScript and WebAssembly are finally fast 🎉 - Mozilla Hacks - the Web developer blog At Mozilla, we want WebAssembly to be as fast as it can be. This started with its design, which gives it great throughput. Then we improved load times with a ...
ようし、これからはFirefoxでいこう!と言いたいところだが、AudioWorkletはFirefoxではサポートしてないんだな。これが。。
ただ96KHzのサンプリングレート、2.6ms以内で音声処理すべてをこなそうとするのはJS/WASMでは難しいかもしれんね。別スレッドで先行処理してバッファリングしておいて、AudioWorklet内ではバッファコピー位に留めておくくらいが現実的なのかもね。。
このようなことは別の手段でもできるから、このアイデアもあまりAudioWorkletをうまく使っているとは言えんねぇ。。
違うな。96khzだと1.3msか。。128サンプル単位だと。。
もしWASMを使うなら、AudioWorklet使うならJSでバッファコピーだけするようにして、別にタイマーかワーカーかで50ms単位くらいの大きい括りで処理するのがよいかもね。
これもちょっと試してみるか。。
メインスレッドのタイマー処理でオーディオをレンダリングしてバッファ(おそらくリングバッファ)に溜め込んでおいて、AudioWorkletがそのバッファを128byteずつ読み込んで処理することを考えている。
気になっているのが不要なメモリコピー。ワーカー間のデータのやり取りはpostMessageでやるんだけどその時にメモリのコピーが発生するんだよな。なのでAudioWorklet側とメモリを共有したいという欲求が出てくるんだよね。
こういう時のためにどうもSharedMemoryBufferちゅうのが用意されとるらしいのだが、ただMDNを見ると「Spectre対応のために無効化した」と書いてあるんだな。。 https://t.co/vivLDeTdf4
別の情報では復活したようなことも書いてあるんだけどもね。。
The Return of SharedArrayBuffers and Atomics - Blog | SitePen A common complaint of modern web apps is the concept of jank; web pages being unresponsive to user input and frame rates being low. Left unmitigated, this problem leads to a poor quality experience for end users of our web applications.
現状のブラウザサポート状況は惨憺たるものですな。Chromeだけのようだ。今は。。
https://t.co/DOrFhsodDa No ImageCan I use... Support tables for HTML5, CSS3, etc
まあしかし、AudioWorklet使ってる時点でChromeしか動かんのだが(笑)。近々にはEdgeでも動くようになると思うけどね。Chromeとべースは同じになるからね。。
SharedArrayBuffer使ってやってみますか。一度。
「所有権の譲渡 (Transferable Objects) によるデータの引き渡し」ちゅうのもあるのかな?
Web Worker の使用 - Web API | MDN Web Worker は、ウェブコンテンツがスクリプトをバックグラウンドのスレッドで実行するためのシンプルな手段です。 Worker スレッドは、ユーザーインターフェイスを妨げることなくタスクを実行できます。加えて、それらは (responseXML 属性や channel 属性は常に null ですが) XMLHttpRequest を使用して入出力を行うこともできます。生成された Worker は、生成元が指定したイベントハンドラーへメッセージを送ることにより JavaScript コードへメッセージを送ることができます (その逆も可能です)。本記事では、 Web Workers の使い方を詳しく紹介します。
しかしこれ使うと、所有権がワーカーに移るんだな。ということはオーディオをバッファにレンダリングするたびにnew ArrayBuffer()しなければならなくなるちゅうことですわ。そのコストも結構高い気がする。やっぱりSharedArrayBufferだよなあ。。
No ImageES proposal: Shared memory and atomics
そういえば、WebAudioってオフラインレンダリングできるんじゃなかったっけ?
GPUでオフライン処理するっちゅう手もあるわなあ。。だんだんWASMから離れていってるけど。
具体的にいうとtransform feedbackを使うということだけどね。
サンプルで理解するWebGL 2.0 - Transform Feedbackによるパーティクル表現 - ICS MEDIA
しかしこれもGPU <-> CPU間のデータの受け渡しで時間がかかるのではないかなということ。あとはコールのコストがあるのでできれば大きいまとまりで処理すべきかなと。
WebAudioのAudioWorkletでは128サンプル単位の処理となるから、サンプリングレートを考慮しながらバッファサイズを求めてそれを共有することになるだろうね。
wasmでやるならFixed Width SIMDが使えるようになるとうれしいのだが。
No ImageWebAssembly SIMD - Chrome Platform Status
主要ブラウザは「実装中」のステータスですな。
ちなみにこれはWebAudio + JSの波形メモリ音源。昔作ったやつに手をちょっとだけ加えた。
ちなみにこれだとAndroid Tabでも再生にモタリはほぼない。
フィルタはないし、音量のEGだけでピッチEGもないからそりゃまあそうかなあ。。といった感じだ。
AudioWorkletってhttpsで配信されんと動かんのね。localhostは例外的にhttpが利用可能とのこと。wsl -> wsl2に移行して気づいた。wsl2だと仮想マシンのIPがつくのでlocalhost(127.0.0.1)ではテストできんのよね。
それでAudioContext.audioWorklet.addModuleでエラー。httpだとAudioContext.audioWorkletプロパティ自体が存在しなくなるんだな。
一つのアプローチとして、
1. web workerの中でwasmを動かして、メモリにレンダリング
2. メインスレッドがメモリをAudioWorkletに渡す
3. AudioWorkletがメモリを128サンプルずつ処理
を試そうとしてる。この間を取り持つのをSharedArrayBufferに担ってもらおうと思った。
メモリ転送コストというか、コピーのコストは極力減らした形で処理したいと思ったんだな。しかし考えるとWebAssembly.Memoryで作るメモリはSharedArrayBufferにはなれないんですな。現状。
だからWASMのメモリからSharedArrayBufferへのメモリコピーが発生するんですな。それで過去こんなproposalがあったのですな。。
allow Memory.buffer to be a SharedArrayBuffer · Issue #950 · WebAssembly/design · GitHub Some background: I'm working on Browsix, a tool for running multiple C, C++, Go and node.js programs as processes in the browser. We do this for C programs by compiling with a (fork) of Emscrip...
この話はその後wasmのthreadsで議論されて、最終的には
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
「shared」を「true」とすることでSharedArrayBufferになることができるようだ。
threads/Overview.md at master · WebAssembly/threads · GitHub Threads and Atomics in WebAssembly. Contribute to WebAssembly/threads development by creating an account on GitHub.
threadsの仕様も読んどこう。
design/FutureFeatures.md at master · WebAssembly/design · GitHub WebAssembly Design Documents. Contribute to WebAssembly/design development by creating an account on GitHub.
threads/SharedArrayBufferはchromeではflagで使えるようにはできるみたいだな。 https://t.co/ruu1Rp4Z0s
ちょっと試してみようか。SharedArrayBufferはspectre問題で一度すべてのブラウザでdisableにされ、chrome以外のブラウザはまだ使えるようにはなってないようだが、そこの理由も知りたいところだが。
なかなか重い議論っぽいね。。このあたり。
SharedArrayBuffer and timing attacks (Meltdown and Spectre) · Issue #3 · tc39/security · GitHub I think everyone was aware that ShareArrayBuffer allows for creating accurate timers. With the recent release of Meltdown and Spectre, SharedArrayBuffer was disabled in all browsers.
そもそもspectreとかの話って何が問題なのかよくわかってないよなあ。私は。。過去記事をちゃんと読んどくか。そして現状その問題はどうなっているのかだよな。。
chromeがSharedArrayBufferを再度有効化したのはSite Isorationしたからなのか。。
Spectre/Meltdownとその派生 SCAIS 2019 (Small-workshop on Communications between Academia and Industry for Security) https://researchmap.jp/sug/SCAIS2019/ の発表資料
CPUのアーキテクチャを追っかけてたのはスーパースケーラとかというワードが目新しいころ。Penitum登場のころくらいまで。分岐予測とかね。今は3次キャッシュ、投機的実行、マルチコア、SIMDとかまあてんこ盛りでまったくついていけてないわ。。
キャッシュにのると実行時間が短縮されるから、それを高精度タイマーで計測して値を特定するということなのかね?なんかすごい話だな。
いやそういう話でもないのか。これ読んだけど全く理解できん。なんでこれで任意のメモリを読むことができるのか。。
図解でわかるSpectreとMeltdown - Speaker Deck A brief explanation of spectre(variant 1) and Meltdown(variant 3)
まあこれくらいの理解でいいか。。
ブラウザベンダーのSpectre and Meltdown対応 - abcdefGets Spectre and Meltdownについては前回書いたが、 各ブラウザベンダーがとっている対応について今回は書く。 といっても大したことではないのだが。 対応一覧 Google Chrome SharedArrayBufferのデフォルト無効化とPerformance.nowの解像度を下げる(どこまでかは不明) Per Site Isolationを有効化 Firefox SharedArrayBufferのデフォルト無効化とPerformance.nowの解像度を20μsまで下げる Safari SharedArrayBufferのデフォルト無効化とPerformance.nowの解像…
いろいろ調べて、ここに行き着いている。
No ImageWebAssembly Worker Based Threads - Chrome Platform Status
Chrome 74以降、WebAssemblyのスレッドサポートが既定で有効化されているんだな。ということはMemoryのオプションsharedが使えるということだよね。
ということはWebAssemblyのメモリをshared=trueで作成すると、そのベースとなるbufferは共有利用できる状態になる。つまりSharedArrayBufferになるというわけですわな。
threadサポートと言っても、別にwasmにスレッドコントロールができるわけではなくて、スレッドで問題となるメモリアクセス競合を回避するための命令が追加されるだけなんだよな。
つまり、WebAssembly.Memoryをshared=trueで作成すれば、メインスレッド、worker、AudioWorklet間でメモリコピーが発生することなくデータのやり取りができるようになるということなんだな。
スレッドにおけるメモリアクセス競合の回避手法についてはそりゃもうC/C++の世界では語りつくされた感があるよね。そのあたりを多少知ってると理解しやすいよね。
ちょっとそれるけど、chrome 75だとメモリのバルク・コピー命令も実装されたようだね。
No ImageWebAssembly Bulk Memory Operations - Chrome Platform Status
メモリ共有の恩恵を受けるのは大きいサイズの場合であって、小さい場合はコピーとそう変わらないんじゃないかという気もする。メモリ共有自体コストがかかる処理だもんね。。
さて高校野球チェックの合間にちょこちょこ実装してくとするか。。
ちょっと前に作ったPSGエミュレータのコードを少し改造してメインスレッドで作成したWebAssembly.MemoryをAudioWorklet上のwasmモジュールと共有できるか試してみた。
見た目は何も変わらないけどね。chromeはもうすでにwasmのthreadsサポートが既定で動くようになっていた。wasm側はメモリをimportして共有するように修正した。
https://t.co/tIhrsb3bYM No ImageWASMでPSG Emulatorを作る
(export "memory" (memory $memory))
↓
(import "env" "memory" (memory $memory 1 1 shared))
wasm側は「shared」を入れ、wat2wasmで--enable-threadsをつけてwasm化する。私はwabt.js使ってるけどね。気を付けなればいけないのはメモリについては最大サイズ指定がsharedの時はmustだってこと。
メインスレッド側のメモリは「shared」オプションをつけて作成する。
const memory = new WebAssembly.Memory({initial:1,shared:true,maximum:1});
maximum指定をしないとエラーになる。SharedArrayBufferが内部で使われるようだね。
#webassembly
このメモリはpostMessageで送っても参照のみのコピーとなって、メインスレッドとオーディオ・ワークレットでインスタンス化したwasmと共有できるようになる。
#WebAssembly
とりあえずできることが分かったので、psgエミュレータを別のワーカーで動かすようにして、メインスレッド・AudioWorklet・ワーカー間で協調動作するようにコードを変えてみる。
wabt.jsはドキュメント上には書いていないけれど、3つ目の引数としてfeaturesオプションがあって、ここで--enable-xxxxxを設定することができる。
wabt.parseWat(path,watText,features);
例えば--enable-threadsをしたくば
features.threads = true
みたくする。
wabt.jsのこの辺りのコードを読んで知った。
wabt/demo.js at master · WebAssembly/wabt · GitHub The WebAssembly Binary Toolkit. Contribute to WebAssembly/wabt development by creating an account on GitHub.
今回はリングバッファにして、できる限りメモリをロックせず動かそうかなと思う。。
昔と違ってマルチコアになってるので、それなりにスレッド使わないともったいないよね。。スレッドがコア別に分けられるかはよくわからんけどね。。
1980年代後半のアーケード基板はオーディオ用にサブCPUを載せてたりしたから、それに倣う感じ。
実装スピードは夏バテもありものすごく落ちている。超絶ノロノロ状態だが、一日数行でもコードを書いてるのでいつかはできるだろう。。
仕事から帰るとヘトヘトで何もしたくなくなるんだよなあ。。この時期。
ようやく実装を終え、デバッグのステータスに入った。
まだまともに動いてないのでなんなのだが、SharedArrayBufferは、webAssembly,web-worker,AudioWorklet,webgl,メインスレッド間で共有できることを理解した。
メインスレッドでWebAssembly.Memoryをshared=trueで作ると、そのメモリはコピーコストを伴わずwasm/webgl/worker/audioWorklet間で共有できる。これができるのは今のところchromeだけのようだけど。
共有メモリにはオペレーションの原子性を保証するメソッド群がAtomicsに定義されている。
このようなことがJSでできるようになってることはすごいことだね。今更だけど。
webAssemblyのステップトレースができるchromeもすごい。これがあるのでwatでコーディングするモチベーションを保つことができる。 https://t.co/YiEnMvTfSn
ウーム。問題発生。今やろうとしてるのは、
1.worker中のwasmでオーディオをレンダリング
2.AudioWorkletでレンダリングしたオーディオデータを再生
で、このデータを受け渡しをSharedArrayBufferでやろうとしてる。
もう少し細かくいうと
1.メインスレッドでWebAssembly.Memoryを共有モードで作成
2.そのメモリをpostMessageでworkerとAudioWorkletに渡す
ことでメモリを共有して、そのメモリにデータをロード・ストアしてスレッド間のデータ受け渡しをしようとしてる。
私はWebAssembly.Memoryを共有モード(shared :true)で作れば、postMessageで送っても内部的に作成されるSharedArrayBufferはクローンされずポインタのみ渡されると期待していたがそうではなかった。。クローンされてるようだ。どうもSharedArrayBufferそのものをpostMessageせんといかんようだ。
WebAssembly.Memoryを生成するときにShareArrayBufferを指定できればよいのだけれど、それはないので、ちょっと工夫しなければならないね。
WebAsssembly.MemoryにはbufferプロパティがあるのでこれをpostMessageすればよさそうだけど、そうするとworker側のwasmでメモリをインポートすることができんようになってしまうんだな。これが。
となると、
1.メインスレッド側は必要なメモリページ数を計算
2.workerへ必要メモリページ数をpostMessage
3.worker側でWebAssembly.Memoryを作成。メインスレッドへ内部SharedArrayBufferへの参照を返す。
4.メインスレッドはその参照をAudioWorkletにpostMessage
みたいにすればいいかな?
しかし現状のレベルではWebAssembly.Memory自体をスレッド間(worker/メインスレッド)で共有できんのですな。これはちと痛いですな。。
しかしこのサンプルを見ると、WebAssembly.Memoryはスレッド間で共有できてる風なんだけどなあ。ひょっとして何か凡ミスしてるかもしれん。。
https://t.co/WxuGvcjpGSthreads/Overview.md at master · WebAssembly/threads · GitHub Threads and Atomics in WebAssembly. Contribute to WebAssembly/threads development by creating an account on GitHub.
まあこのコード見たから「いける!」と思ったんだった。そういえば。。
どうも私の凡ミスっぽい。WebAssembly.Memoryはshared=trueにすればスレッド間で共有できるのは間違いないようだ。そりゃそうか。。
私のwasm関数がおかしな動きしてるだけというオチ。
とりあえずメモリが共有できてることだけは確認できた。が、まだ動いてない。。しょうもないバグだった。しかしまあ、サンドボックス環境(安全性がそこそこ担保された環境)でリニアメモリを各スレッド間で共有して自由にアクセスできるのはちょっとうれしいね。
PSGエミュレータのworker改造版が動けば、いよいよオリジナル?の波形メモリ音源をWebAssemblyベースで作って先に進めるとしよう。。
うーむ。音が鳴らんなあ。。
Bugを追い込んでるが、凡ミス部分がなかなか発見できん。。いうても1日1時間もいじってないけど。。モチベーション低下中。。
なんかこう、workerのデバッグってやりにくいなあ。。
いやぁ。ようやく音が鳴るようになったなあ。workerの中で25msのsetTimeoutだとタイマーの間隔が短すぎるようだ。100msくらいにしないとまともに音が鳴らないわ。。
と思ったけどBugを見つけた。25msくらいでも行けるようになったなあ。。
現状の成果物はこんな感じ。
これが一応私の環境で動いてるやつ。。
Chromeしか動きませんが。。
No ImageWASMでPSG Emulatorを作る
えらい時間がかかってしまったなあ。。
しかしこれで技術的なめどはついたので、いよいよ波形メモリ音源をwasmで作るとするか。。バグを潰してる間もどうしようかいろいろ考えたんだよねえ。。
波形メモリ+EG+フィルタだよな。やっぱり。ちゅうことでSCC+SIDみたいなやつでいこうかなと。とりあえずそれで設計しつつ実装すると。
あとはシーケンサかあ。MML書けるようにするかそれともStandard MIDIをそのまま再生できるようにするか。。。
私REAPERというDAWを使ってるので、JS Pluginで同じ仕様の音源を作って、REAPERで作曲してそのデータを再生するようにしようかなとも思ってる。
そういえば思い出したのが、WebAudio APIを使ってモジュラータイプのやつを途中までつくってたんだっけ。。ステップシーケンサー作ってる途中でやる気なくしたんだよな。
https://t.co/0qxAPewFLs No Image
レコポっぽいシーケンス・エディタを志向しててそれなりにできてた気もするけどなあ。。 https://t.co/kxJeodX2IX
これ改造してエディタ+シーケンサを作れんかな。。
まあでもあれかあ、REAPERでやってみるかあ。。
前作って放置したWebAudioのやつを動画にしてみた。エディタはレコポのコマンドのまんまにしようとしてたみたいね。。
これはなかなかいいね。。
No ImagePlaying Fasttracker 2 .XM files in Javascript – a1k0n.net
GitHub - a1k0n/jsxm: FastTracker 2 .xm module player in Javascript FastTracker 2 .xm module player in Javascript. Contribute to a1k0n/jsxm development by creating an account on GitHub.
おぉ。これはFastTracker IIのクローンか。。
No Image16-bits.org - home of 8bitbubsy
うーむ。。。いいね。。
WebSid - the first HTML5/JavaScript C64 music player
SID界隈のソースをじっくり読もうかのう。。
帰省中も少し音源のことを考えた結果、音源作る前にシーケンサーのコードをwasmで書こうかなと思った。それでPSGを演奏してみようかなと。MMLっぽい言語のパーサもついでにpegで書いて、それをメモリに落とすコンパイラを作り、シーケンサーに渡して演奏させるようにするつもり。
と思ったがやっぱりまずは波形メモリ音源を先に作ろうと思う。。どっちやねん。。
そういうわけで先ずはEGから作り始めている。。
コツコツmwasm(自作プリプロセッサ)でEQを実装中。もうちょっとでできる。
だいぶwatでの実装も慣れてきた感じ。S式ってシンプルでいいなあ。。 https://t.co/JbmXjRRuul
EQじゃなくてEGだった。。
EGを作る
↓
WaveTable部分を作る
↓
一度音を鳴らしてみる
↓
LFOを作る
↓
フィルタを作る
の順番で作っていくつもり。AudioWorklet+Workerで処理が間に合うかはよくわからんね。。
とりあえずEG出来た。。
sandbox/wpsg.mwat at master · sfpgmr/sandbox · GitHub JS,WebGL,three.jsをいじるためのレポジトリ. Contribute to sfpgmr/sandbox development by creating an account on GitHub.
さて次は波形テーブル部分を作るとしますかな。
実のところEnvelope Genetator(EG)もADSRのそれぞれのフェーズへの移行を直線的、つまり一次関数で実装する分には楽なのだが、アナログ・シンセのEGをまねるとなると結構面倒くさくてですな。。
VSTiの作り方 - 7.おまけ (ADSRの実装) | g200kg Music & Software
私のEGはもれなくいい加減なやつなんですな。つまり直線的なやつですわ。
波形メモリもなんちゅうか、エイリアシングノイズが発生するという問題があるので、それをできるだけ抑えてきれいな波形を出すとなると周波数帯域に応じた複数の波形データを用意する必要があったりして、凝りだすと止まらんわけですな。マルチ・サンプルとか言ったりもしますわな。。
しかしそれは楽器としての話で、例えば古いサウンド・チップなんかはコストがかかる話なので、エイリアシング・ノイズは承知の上で割り切ったりしてるんですな。例えばSCCなんかは8ビット×32サンプルで発声可能なすべての音域をカバーするもんね。
ということで我々はシステムに起因するノイズが含まれた音を聞くことになるのだけど、そのノイズが聴感上耳障りなのかどうかというのはまた別の話なんですな。そこに独特の味や良さを感じるひとたちもいるわけで。
SCC・PSGはさらにピッチ・コントロールに制約があるのでちょっと音痴になったりするんだけど、それに違和感を感じる人もいれば、感じない人もいるわけで。
波形メモリというのはオシレータの一種なわけで。オシレータもいろいろな作り方があるんだよな。例えば計算によってサイン波・矩形波・のこぎり波などの周期波形を出力する方法もある。
リアルタイムに計算によって波形出力するのは昔はコストが高かったので、あらかじめメモリに波形情報を保存しておいて、それを読み出すことによって波形合成する方式がまず普及したんですな。
BLITなんかも昔調べたよなあ。。
VSTiの作り方 - 8.おまけ (BLITのお話) | g200kg Music & Software
サンプリング周波数変換なんかも結構奥が深くてですな。やっぱりノイズを含む問題があって、それを抑えるためのテクニックがあるんですわ。
No Imageサンプリング周波数変換 - Wikipedia
サンプリング周波数変換だけで一冊の本になってしまうくらいですからな。。
No Imageマルチレート信号処理―レート変換とマルチレートフィルタ | 高橋 宣明, 武部 幹 |本 | 通販 | Amazon
ちょっと内部的なサンプリングレートを24KHz固定にしようかなぁと考えててね。いまどきのPCはオーディオのサンプリングレートは可変で、それに合わせて内部処理してもいいんだけど、高周波数になるとちょっと処理的にきつそうだし、Lo-Fiな音で十分だという気持ちもある。
そうはいってもPCの方は再生サンプリング周波数は可変だから、もし内部の周波数を固定にするとレート変換をやらなくてはいけなくなるんだな。でも私のPCだと96KHzくらいでも処理できたりできてるけどね。。さて、どうするか。。
WaveTableの実装はとりあえず終わり。これからテストに入る。。
テスト用の音源&UIを作って鳴らしてみる。
このwasmのデバッガはありがたい。スタックやメモリの内容、ローカル変数の値が見れるしね。S式でソースを書いてるのでフラットモードを追うのはちと面倒だけど。。 https://t.co/QNW2PlC26Y
wasmで書いた波形メモリのオシレータはようやく動くようになった。
波形メモリ音源の体裁を整える作業を実施中。コンピュータはメモリへのロード・ストアがいかに多いかがwasmを直接書いてみると実感できる。しかもS式で書きなれてきて、スタックが今どんな状態か頭で想像できるようになってきたわ。
平行してデジタル・フィルタのコードを物色中。とりあえずはMAMEのSIDのコードを参考にwasmで書こうかなと思っているけど、やっぱりフィルタは重い処理だね。。
Worker自体で処理するよりは、コードをGLSLで書いてGPUにさせたほうがいいかもしれんな。まあ書いてみんとわからんけど。
RolandのVSCも最初はフィルタなしだったもんなあ。。
波形メモリシンセとして動くコードをwasmで書いてるが、call_indiectの書き方でハマり中。。wabt.jsからよくわからないエラーが出てコンパイルできんのですわ。。
仕様だけで書き方がわからない時は、wasmのテストコードを読んだりしてる。。
spec/call_indirect.wast at master · WebAssembly/spec · GitHub WebAssembly specification, reference interpreter, and test suite. - WebAssembly/spec
(table funcref
...
ん、funcref ? なんだこれ。
spec/call_indirect.wast at master · WebAssembly/spec · GitHub WebAssembly specification, reference interpreter, and test suite. - WebAssembly/spec
これか。。
reference-types/Overview.md at master · WebAssembly/reference-types · GitHub Proposal for adding basic reference types (anyref) - WebAssembly/reference-types
anyfunc から funcrefに変わっとるのね。。
仕様もそうなっとるな。。
No ImageWebAssembly Core Specification
それはそれとして、元々の問題は凡ミスだった。。
波形メモリ音源の実装のめどがつきそうな感じになってきたな。。音が鳴ったらフィルタを作って終わりにしよう。演奏部分(シーケンサ)も作らんといかんしな。。年内にゲームシステムが完成するか微妙になってきたね。もう9月だもんね。。
ゲーム・システムが完成したらようやくゲームつくりに入れるからなあ。。
プログラミングにかける時間は平日は多くて1時間、休日は2-3時間くらいだからなかなか進まんよなあ。。それくらいしか集中力が続かんのよねぇ。。
もちろん趣味のプログラミングに割く時間だけだけどね。。
ぼちぼちテスト&バグつぶしをやってる。あれれ、memory.grow でエラー出るなあ。。 https://t.co/rFhbOcuVBc
うーむ。JS側でgrowしてもアウトですな。。 https://t.co/b98pbNl8Cr
shared arraybufferだとダメなのかな。。。
普通のmemoryだといけるな。。やっぱりsharedだとmemory.growできん仕様なのかな。。 https://t.co/F5xHT3nT1g
これはちょっと行き詰った感ありですな。。
しかしまあ特別shared memoryだからといってmemory.growがfailするとは書いてないからなあ。。
threads/Overview.md at master · WebAssembly/threads · GitHub Threads and Atomics in WebAssembly. Contribute to WebAssembly/threads development by creating an account on GitHub.
「grow_memory of a shared linear memory is allowed.」だもんねぇ・・。
threads/Overview.md at master · WebAssembly/threads · GitHub Threads and Atomics in WebAssembly. Contribute to WebAssembly/threads development by creating an account on GitHub.
どうもこのBugっぽいねぇ。。
No Image8564 - Loading issue... - Monorail
このBugはまだ解消されてない模様。というかIssue9380のためにブロックされているようだ。
No Image9380 - Loading issue... - Monorail
ちょっと待ちかあ。初期アロケートメモリを大きくしてテストを続けるかな。。
そういえばガベコレも動かせるようになったんだよな。。 https://t.co/KIowNFTnbx
おっと、自作プリプロセッサのバグを発見。修正せねばいかんな。。
プリプロセッサのバグを取り終え、波形メモリ音源プロトタイプのテスト&バグつぶしを再開。まだちゃんと動いてはいない。。
プリプロセッサもS式を崩さないように構文設計すればよかったかなあと思ったりするな。。
今のところ、フィルタなし、波形メモリによるオシレータ、ピッチLFO/EG、アンプLFO/EGという構成になってる。
WSGやSCCの時代はチップとソフトウェアでこれくらいのスペックを実現してると思う。。
MOD界隈でもなかなかデジタルフィルタをソフトウェアかつリアルタイムで動かすことはできなかった。そこは長い間DSPが担う部分だったんだよな。。
とりあえず実行すると何らかの波形出力が得られるようになった。これからUIと簡易シーケンサを作って実際に鳴らしてみるとするか。。
PSGエミュレータに比べてパラメータが圧倒的に増えたんでちょっと面倒なんだよねえ。。
先にフィルタを実装してみることにした。。
このコードを参考にして。。
簡単なデジタルフィルタのサンプルコード | C++でVST作り 簡単なデジタルフィルタの実装についてで説明したフィルタのサンプルコードです。 「Z変換」や「伝達関数」といった高度な数学を極力使わずに実装するフィルタのサンプルコードです。 「簡単なデジタルフィルタの実装について」も合わせてお読みください。
フィルタ実装は今半分くらい進んでいる。サンプルコードがすごくわかりやすくてbiquadフィルタの理解も少しは高まった気がしないでもない。しかし原理の理解は浅い。。
離散数値で構成されるデジタルの音の世界では、ある「一定期間の音声データの平均をとること」≒「ローパスフィルタをかける」ことということは感覚的に理解している。
がそれ以上はよくわからない。よくわからないがしかしフィルタのコード自体はいたって簡単である。がしかしなぜこれでLPFやHPFになったりするのかがよくわからんのであった。。
2019年のWebAssembly事情 - Qiita 2年前に、[「(Learn the Hard Way)nodejs-8でのWebAssembly自体を調べてみた」](https://qiita.com/bellbind/items/a6435b0c8f10306128b8)として、...
v8では--wasm-math-intrinsicsが使えるのであった。。これでsin/cosの呼び出しコストが抑えられるのでjsのMathを利用することにした。新たにsin/cosを作るのはとりあえずやめておこう。
下のサンプルコードをwasmに書き換えた。このフィルタを使えるように波形メモリシンセの修正にこれから入ろうと思う。その前に耳鼻科に行かんとね。。
簡単なデジタルフィルタのサンプルコード | C++でVST作り 簡単なデジタルフィルタの実装についてで説明したフィルタのサンプルコードです。 「Z変換」や「伝達関数」といった高度な数学を極力使わずに実装するフィルタのサンプルコードです。 「簡単なデジタルフィルタの実装について」も合わせてお読みください。
うーむ。思ったほど進捗できず。。
まあしかし少しずつでも進めればいつかはできるからね。。
ようやく音が鳴り、UI作りに入る。UIができたら各パラメータをいじって所望の動きになっているかテストする。 https://t.co/6EARekLIJd
まだしかし音源としては機能が足りてないので、そこは追加実装しなくてはならない。さて、この音源使い物にはたしてなるのか。。
まず、波形テーブルの編集部分から作り始めるとするか。。
そういうことで作成中。数式で波形を生成する機能+ハンド・ドローイングで波形をエディットすることにしてみた。 https://t.co/qHxEAfX5h3
波形メモリのサンプル数が512だとやっぱり綺麗だな。。 https://t.co/38VKWy2ChD
サンプル数が16だとレトロな感じ。。 https://t.co/85BrpIejqi
これを波形メモリに直接反映して結果をすぐ確認できるようにするつもり。波形メモリは共有メモリに格納してるのでこの辺は簡単かと。。
式で波形生成できるようにしてみたがこれが結構面白くてちょっとハマってる。。
FM変調風 https://t.co/O1EwWWh3ni
フーリエ級数による矩形波。ギブス現象が出とる。。 https://t.co/I6lfmzOsen
級数の数を増やすとほほほぼ矩形波になるな。。 https://t.co/8BhpQJeG9p
矩形波はこんな式でもいけるけどね。。 https://t.co/DbBAkxdWzu
鋸波 https://t.co/saMexy7ELA
三角波 https://t.co/zqRhaIphHW
フーリエ級数による鋸波
やはり項が有限だとギブス現象が出ますな。。 https://t.co/nHI34guVx6
多分正弦波の和ですべての波形は表現できるんだろうなあ。フーリエさんちゅうのはすごいですなあ。。
FMは簡単な実装で複雑な波形を作ることができる。しかもEGだけでフィルタを使わずに含まれる周波数の成分を変えることができるという。
原理は簡単なんでチップも安価に作ることができるし、PSGに比べてその音色の豊かさは比較にならない。しかも従来のシンセでは出ない音も出すことができたという。。
FM音源を開発したヤマハ社にはリスペクトしかないですな。おかげでゲーム音楽がリッチになったもんな。。
波形エディタがようやく動くようになったので動画にしてみた。
WebAssembly.MemoryのbufferがSharedArrayBufferのときgrowできんというBugどうやらFixした模様ですな。。
No Image8564 - Loading issue... - Monorail
これがいつChromeに反映されるかだが。
波形メモリの編集部分がそこそこできたので、音色(Timbre)エディタ部分を作ろうかなと思う。EGやフィルタ・パラメータをいじる部分。
音色エディタを作成中。ようやく音量のEGとLFOがエディットできるようになった。
年内に所望のゲーム・システムはできるだろうか。やばくなってきたな。。その上に載るゲームのアイデアもモヤっとしとるがな。。
フィルタのUI作成中。ああ、面倒くさい。。 https://t.co/nzhIOhNolg
とりあえずフィルタのパラメータいじるとこまでUIコードを書いたがうんともすんともいわない状態。。
ようやくフィルタが動くようになった。まだなおさないといけないところはある。。
これが終わったらピッチのテストか。。これでようやく波形メモリシンセとしては完成か。。次は演奏部分(シーケンサ)を作らなくてはいけないな。過去何度も演奏エンジンだけで作ってエディタで挫折してるので、なんとか乗り越えたいところ。。今回はpegで演奏用の言語(MMLではない)を作ろうかなと。
今回作ってて面白いなと思ったのは、式からの波形生成。以下のような式を作って波形を作ると、鋸波と正弦波が半分ずつミックスされて聞こえるところ。式から聞こえる音がなんとなく想像できるのが楽しいなと。当たり前の話だけどね。。
Math.sin(t * Math.PI) * 0.5 + t * 0.5
ちなみにtは0-1の間で変化する。。
まだ単音でしか鳴らせてないので、これが8音ポリくらいになったときに処理負荷がどれくらいになるのか想像がつかんので、やってみるしかないんだけど、そこがちょっと不安かなと。あ、そういえばマルチ・ティンバーで鳴るようにするコードも書かんといかん。
あとサンプラ機能もな。。
ピッチ部分のUIも作ったが、見事にBugっている。ピッチEGいじってるうちにパックマンがやられた時のような音が出た。。
波形はこんな感じで、正弦波を加算合成して作ったのですわ。。
(Math.sin(t * Math.PI) + Math.sin(2 * t * Math.PI) + Math.sin(4 * t * Math.PI)) / 3 https://t.co/aZi53CdbtE
ピッチの算出式はそもそも間違ってるようなので見直さんといかんな。。
算出式は間違ってなかったけど、アルゴリズムが間違っていたな。。ピッチの計算は誤差の蓄積やら桁あふれやらデノーマルやらを考えるとf32ではなくu64の固定小数点で計算したほうがよさそうだな。。それでやってみるか。。
f32のままでやってもいいけど小数点以下の桁数を抑えてデノーマルを防ぐようにするという手もあるかなぁ。でも結局波形テーブル引きするときに整数化するからなあ。。
64bit幅、小数部16bitの固定小数点で波形メモリのピッチを計算しなおすことにして実装中。固定小数点の乗算・除算のコツをすっかり忘れてしまってた。オーバーフローとか乗算後のシフトとか、除算前のシフトとかをね。。
これまでの成果。LFOとかピッチがちょっとおかしい。。
フィルタ使えると管楽器的な音がリアルになるよなあ。やっぱり。時間的な周波数成分の変化は音色にとって重要だなということがわかる。
chromeでWASMのSIMDが試験的に実装されたので、それをいつどこで使うかだよなあ。まあもうちょっとちゃんと動くようになってから考えるか。
いちおうオシレータの部分は他の波形生成方式に差し替えできるように作ってるので、音源として完成したらFM方式のオシレータも作ってみたいんだよな。。AFM音源みたいなやつ。。
それよりもゲーム作りを進める方が先か。。
あ、それとフィルタのQが全然効いてないのだった。。
まあしかし、この部分はほんとはAudioWorkletでなく、oscillator nodeやgain node、filter nodeを使用すればもっと簡単に実装できるのだが。
フィルタのQが効くようになった。またしても凡ミス。LPFはいい感じだけど、HPFの動作がおかしいね。。
やっぱりレゾナンス効かせるとシンセっぽくていいなあ。。
LFO当たりの修正を加える。さらにシンセっぽい音が鳴るようになったな。。
シーケンサに手をつけ始めた。シーケンサの構造を考えながら音源のポリ化を進めようかなと。パン二ングも実装せんとなあ。爆発音に必要なノイズジェネレータも実装せんといかんなあ。。
シーケンサの仕様が固まったので実装しつつ、仮想メモリ音源のマルチティンバー化も並行して進めている。8音か16音ポリのマルチティンバーとなる予定だが果たしてどうか。処理が追いつくのだろうか。。
多分音を鳴らすだけなら問題ないんだろうけど、ゲームのBGM用だからなあ。。フィルタの処理がやっぱり重いんで気になるところですわ。
シーケンサの動作確認用も兼ねてMMLみたいな簡易言語を作るつもりである。書いたらバイナリ・データにコンパイルするやつである。
SIMD使うと2音~4音くらい同時に処理できてパフォーマンスアップできそうだけどね。。
simd/SIMD.md at master · WebAssembly/simd · GitHub Branch of the spec repo scoped to discussion of SIMD in WebAssembly - WebAssembly/simd
まあとりあえず全部組み上げてから考えよう。最適化は。。