WebAssembly で動作する FM 音源ライブラリー libymfm.wasm

去年くらいからつくりはじめていた、libymfm.wasm ですが、GitHub のリポジトリーにコミットするだけで、ブログにあまりあれこれ書いていませんでした…!(ので書いてみます)

libymfm.wasm は WebAssembly 上で動作する(主に) FM 音源シンセサイザーをエミュレートして PCM を生成するライブラリーです。ゲームなどのプログラムへの組み込みを考えて作成されました。

https://github.com/h1romas4/libymfm.wasm

This repository is an experimental WebAssembly build of the [ymfm](https://github.com/aaronsgiles/ymfm) Yamaha FM sound cores library.

FM 音源エミュレータコアとしては、多目的エミュレーションフレームワーク MAME の新 YAMAHA FM 音源エミュレーションコアを由来とする C++ でかかれた ymfm を使わせて頂いています。

ymfm の作者は MAME の中の人のアーロンさんで、元々 MAME 内での実装だった新 YAMAHA FM 音源コアを 3rdparty ライブラリー化したのが ymfm となります。

従来の MAME の FM 音源エミュレーションコアは YM2151 や YM2203、YM2612 などなど別チップは別エミュレーションの実装となっていましたが、機能差分以外は同じ回路が使われているのではないかという仮説から、 decap 解析などとの比較を経て完成したライブラリーです。ということで、ymfm は多くの YM 系のチップをそのひとつでサポートしています。

さて、表題の libymfm.wasm ですが、ymfm を使いながら、Rust でかかれた次のような実装を加えています。

VGM/XGM 形式のシーケンサーを搭載

VGM/XGM 形式の演奏形式をサポートしています。ファイルを渡すだけで発音可能です。

VGM はサポートしているサウンドチップのみ、またデーターブロックの圧縮が未サポートなどフル実装ではありません。XGM は PCM に不具合ありで修正予定です。 (多くのデータでテストはされていませんが修正済み)

サンプリングレートコンバート

各サウンドチップが出力するネイティブサンプリングレートはまちまちなので(YM2151 が 3.58MHz 動作で 55.9kHz 等々特殊です)、扱いやすいように指定したサンプリングレートにアップ、ダウンサンプリングで統一して PCM 出力します。

クロックの制御

ライブラリーに、出力サンプリングレートに対して何サンプル分の PCM が欲しいのか(時間をどれくらい進めるのか)を指定できます。これはゲーム組み込み用などで、1フレーム分のサンプルが欲しいケースや、バッファリング再生したい時に便利です。

WebAssembly 向けのインターフェース関数

ハイレベルインターフェースとして vgmplay xgmplay 関数、ローレベルインターフェースとして各サウンドチップに直接レジスターライトして、結果を任意のサンプリングレートとフレーム数で PCM 取得できます。

追加のサウンドチップ

ymfm サポート以外のサウンドチップも MAME からの移植でいくつか実装。主にメガドライブ、セガアーケード、X68K 構成を想定したチョイスです。

ライセンス

ymfm や追加音源、libymfm.wasm 全て BSD ライセンスになっています。


WebAssembly ライブラリー形式となっていますので、同一 .wasm シングルバイナリー(3MB 程度です)で Wasmer などの各言語向け WebAssembly バインディングを使うことで、ほとんどの OS、コンピュータ言語からコールして容易に使うことができます。 .wasm ファイルひとつで全環境動くので扱いやすいです。もちろんウェブブラウザーでも…!

リポジトリには Python からコールする例を入れています。リンク先の手順ですぐ発音すると思いますので、良ければ遊んでみてください〜。

VGM/XGM 再生

関数に VGM/XGM ファイルを与えると指定したチャンクサイズで PCM を取得できます。

sample_vgmplay.py 抜粋

# Output sampling rate settings
SAMPLING_RATE = 44100
SAMPLING_CHUNK_SIZE = 4096

# ...snip...

# Create Wasm instance
chip_stream = ChipStream()

# Setup VGM
header, gd3 = chip_stream.create_vgm_instance(VGM_INDEX, "./vgm/ym2612.vgm", SAMPLING_RATE, SAMPLING_CHUNK_SIZE)
# Print VGM meta
print(header)
print(gd3)

# Play
while chip_stream.vgm_play(VGM_INDEX) == 0:
    # Get sampling referance
    s16le = chip_stream.vgm_get_sampling_ref(VGM_INDEX)
    # Sounds
    sample = pygame.mixer.Sound(buffer=s16le)
    pygame.mixer.Sound.play(sample)
    # Wait pygame mixer
    while pygame.mixer.get_busy() == True:
        pass

サウンドチップダイレクトコール

以下の例は あぶり6800 さんの Z80 MSX サウンドドライバー(1/60 tick) の Python によるシミュレートで、YM2149 の SSG を発音させています。

ソースコードでは YM2149 ひとつを扱っていますが、サウンドスロットにぽこぽこ複数の音源を追加してレジスターライトして PCM を取得できるイメージです。

前述の vgmplay(xgmplay) で使っているインターフェースは全て Wasm 側にも公開しているので、PCM ROM がある音源の操作も含め、原理的には自前で vgmplay をかけるスペックになっています。

sample_direct.py 抜粋

# Create Wasm instance
chip_stream = ChipStream()

# Setup sound slot
chip_stream.sound_slot_create(SOUND_SLOT_INDEX, SOUND_DRIVER_TICK_RATE, SAMPLING_RATE, SAMPLING_CHUNK_SIZE)

# Add "one" YM2149 sound chip in sound slot
chip_stream.sound_slot_add_sound_device(SOUND_SLOT_INDEX, SoundChipType.YM2149, 1, YM2149_CLOCK)

# YM2149 initialize (write reg: 0x7, data: 0b10111000)
mixing = 0b10111000
chip_stream.sound_slot_write(SOUND_SLOT_INDEX, SoundChipType.YM2149, 0, 0x7, mixing)

# ...snip...

# Write YM2149
chip_stream.sound_slot_write(SOUND_SLOT_INDEX, SoundChipType.YM2149, 0, track + 0x8, volume)

さて、libymfm.wasm のプログラム部分についてですが、Rust でかいており、C/C++ の ymfm を wasi-sdk でビルドした上で、Rust の wasm32-wasi とリンクする形でビルドしています。

このことから、.wasm は WASI の形になっています。(とはいえ、現在のところは WASI の機能はほとんど使っていません。一部、YM2608 の ROM ファイル読み込みで機能しています)

ウェブブラウザーインターフェースでは、WASI をブラウザー上で動作させるため、wasmer-js を使い、WASI バインディング関数をシミュレートして動作させています。

ウェブブラウザーインターフェースは次のリンクから試すことができ、演奏ファイルのドラッグアンドドロップ(複数可能)で楽曲が再生されるはずです。(画面クリックでもデモ曲が演奏されます)

https://chipstream.netlify.app

libymfm.wasm とは直接関係ありませんが、ウェブブラウザーの JavaScript の実装的には AudioWorklet と Woker と SharedArrayBuffer を使った音声出力の実装になっています。

iOS/macOS の Safari は現時点で SharedArrayBuffer に対応しているものの、どうも SharedArrayBuffer の notify がうまく飛ばないようで発音しません。どうやら AudioWorklet に渡された際に共有ではなくコピーになるようです。Safari 16 で修正されそうです(SharedArrayBuffer posted to AudioWorkletProcessor is not actually shared with the main thread

(2022/8 追記。Safari Technology Preview 149 で修正されているのを確認しました)

Windows/macOS/Linux の各ブラウザーで音切れしないように確認しながらつくっていますが、まだだめな環境があるかもです。。結構苦戦しました。。ちなみに手元の環境では、Linux の Firefox が一番素直に動作するようです。

てなわけで、みなさまのアプリに組み込みが容易になっている libymfm.wasm の紹介でした。新作ゲームに FM 音源ミュージックのリアルタイム再生など、いかがでしょうか…!

JavaScript、Python のサンプル実装含めてソースコードは、GitHub にコミットしてありますので、良ければ見てみてください。

https://github.com/h1romas4/libymfm.wasm

This repository is an experimental WebAssembly build of the [ymfm](https://github.com/aaronsgiles/ymfm) Yamaha FM sound cores library.

時折あるアップデート通知は https://twitter.com/h1romas4 のツイッターにて〜 😀

関連

MSX ゲーム開発 2022年

1980年代から90年にかけて家庭に広く普及した MSX パソコン(当時はマイコンと言ってましたね!)にて、ゲームをつくる活動を 2022年に復活してみました…!

当時の開発は MSX-BASIC もしくは Z80 アセンブラをつかったものでしたが、今回は さまざまな 8bit Z80 系のレトロコンピュータに対応する Z88DK ツールチェインによる C 言語を使っています。

Home: z88dk

z88dk is the only C and assembler development kit that comes ready out-of-the-box to create programs for over 100 z80-family machines.

Z88DK のセットアップやサンプルコードについては、以下の文書にまとめています。C 言語ですが、BASIC より簡単かも、、ですので良ければ遊んでみてください。この記事で紹介しているゲームのソースコードへのリンクもつけています。

Z88DK を使って MSX のゲームをつくるための環境構築メモ

この文書は、Z80 を CPU に持つコンピュータ向けの C コンパイラ・アセンブラツールチェーンである Z88DK を使って MSX のゲームをつくるための環境構築メモです。

てなわけで、この Z88DK を使いまして、新作ゲーム(?) をふたつつくってみましたので紹介したいと思います。ゲームはウェブブラウザーで動作する MSX エミュレータから楽しめますので合わせてリンクをしています。

PONPON for MSX

80年代のコンピュータ誌に投稿された PONPON という名前の投稿プログラムの MSX クローンです。自分はプログラムポシェット誌でみて打ち込んで楽しんだ覚えがあります。

自動的に上下に移動する主人公「◯氏」を赤ブロックに激突しないように左右に操作して $ を取得して点数を競うゲームです。

次のリンクからウェブブラウザで遊べます。

WebMSX で PONPON を遊んでみる…!

記憶だけを頼りにつくっていますが、この MSX 版はプログラムポシェット掲載 PC88 PONPON の “改造版” の移植です。オリジナルは 40 * 25 桁(WIDTH 40,25) でもっと綺麗に壁や赤ブロックが並んでいたと思います。

当時小学生だった自分はごちゃごちゃ改造していて、確かこのような感じになった気がします、、懐かしいです。。

NOBORUNOCA for MSX

MSX の VDP はいわゆるスクロール機能を持たず、スクロールをしたい場合は通常 VRAM ブロック転送による PSG(8ドット) 単位スクロールとなりますが、これを PCG キャラクターをドットずらしで用意することでスムーズスクロールを実装する技がありました。

当時の自分はスムーズスクロールしてみたくても、プログラミング技術もあまりなく実装を諦めており、これまで数十年間心の何処かにひっかかっていた課題のひとつだったのですが、2022年になってようやく実装することができました。

というわけで、構想数十年、製作 5日の NOBORUNOCA です。

強制縦スクロールのワンキーためジャンプアクションゲームです。使うのは SPACE キーのみ…!のぼるのか…のぼらないのか…

次のリンクからウェブブラウザで遊べます。

WebMSX で NOBORUNOCA を遊んでみる…!

操作はシンプル、割と奥が深いを目指してつくってみました。ゲーム特有の落ち着けばなんとかなる…!がうまくつくれたと思いますので、良ければ遊んでみてください…!(ツイッター #NOBORUNOCA タグでみなさまのハイスコアを拝見しております… 😀

裏技的な攻略情報がいくつかあるので書いておきます。

  • 足場生成に関わるゲームの乱数シードはタイトル画面のパワーゲージ値によります。
  • レベルエクステンド時に、必ず穴がない休憩足場が生成されます。
  • ジャンプ上昇中、ジャンプ落下、歩き落下中もパワーゲージがチャージできます。
  • 落下中から、着地前直前十数フレームで、その場再ジャンプが可能な猶予フレームがあります。これは足場がない最下段でも適用可能なので、目押しスペース離しで復活できることがあります。また、成立するとボーナス点が入ってます。
  • ジャンプ落下中は足場判定が横に広がっています。このため左右の縦壁でも再ジャンプ可能なパターンがあります。

いろいろ実装していましたら、当時の BASIC ゲームも面白くなるようにいろいろ調整して楽しんでいたことを思い出してノスタルジー。

ゲームミュージック

マイコンゲームに音楽をつけるのも当時の課題のひとつで、サウンドドライバー問題とシーケンサーどうする問題がありましたが、今回 @aburi6800 さん の MSX Z80 サウンドドライバーと、いちまるまるゲームズさんの、Lovely Composer & あぶり6800さんコンバータにより、見事、課題解決することができました…!

ありがとうございました…!

Lovely Composer (ラブリーコンポーザ)

家庭用ゲーム機の作曲ソフトの方向性を受け継いだ、かわいい作曲ツール!
レトロゲームのようなピコピコサウンドの音楽や効果音を、楽しく手軽に作れます。

https://github.com/aburi6800/msx-PSGSoundDriver

MSX用のPSGサウンドドライバです。
z88dkのz80asmでコンパイルできる形にしています。

楽曲は YAMAHA MODX シンセで曲のスケッチをかいて、Lovely Composer に打ち込む形で楽曲をつくり、lc2asm コンバータでゲームに取り込む手法でつくっています。

今回は MSX 向けで矩形波 2/3ch だけの打ち込みとしていますが、Lovely Composer は矩形波以外も波形メモリ音源的な音などなどピコピコサウンドを手軽なプリセットと操作系で使え楽しいです。:D

MSX ゲーム取り込み前には、YM2149 での鳴り具合を確認するために、自分が以前からつくっていました WebAssembly の ymfm エミュレータを Python からコールして、サウンドドライバー互換で発音させるスクリプトも利用しています。

https://github.com/h1romas4/noborunoca/tree/main/tools/lcconv

楽曲のほうですが、自分は音楽の方が楽器は持っているもののほとんど素人ですが、、レトロっぽいコミカルさが出るように、少ない小節数にすると心に決め、メロディーのリズムに気をつけてかきました。

密かにちょっと気に入っていますので良ければ聴いてみてください…!

最後に

残念ながら手元に MSX 実機がないためまだ実機動作を自分で見れていないのですが、Simple ROM Cartridge を使わせていただいて、いつか動作させたい…!夢の ROM 版ゲーム…

MSX用カートリッジ64K Simple ROM Cartridge

ちなみに PONPON は 16KB で NOBORUNCA はほんの少しだけはみ出て 32KB ROM になっています。

書き込み準備ヨシ…!(ちなみに写っている PSP 版は fMSX エミュレータによる動作です)

ゲームのソースコード

両ゲームとも GitHub Actions で ROM のビルドができるように仕込んでいます。また、リリースページに .rom ファイルを置いています。

PONPON

https://github.com/h1romas4/z88dk-msx-template

NOBORUNOCA

https://github.com/h1romas4/noborunoca


関連

Ubuntu 22.04 LTS アップグレードと Focusrite Scalett Solo オーディオインターフェース

Ubuntu 22.04 LTS がリリースされましたので、Raspberry Pi 400 と WSL2、そしてメインで使っている ThinkPad P14s に導入してみました。

Raspberry Pi 400:

導入済みの Ubuntu 20.04 LTS からのアップグレード。SSD 起動にしていますが、1時間ほどかかったでしょうか。無事 22.04 LTS となり、SDL の画面描画やオーディオ再生などうまく動作しているようです。

WSL2:

こちらは、Microsoft ストアより Ubuntu 22.04 LTS を選択して新規導入。ESP32/M5Stack のSDKである esp-idf に含まれる (riscv32-) gcc などのバイナリーパッケージが正常に動作することを確認。

ThinkPad P14s:

懸念していた esp-idf のバイナリーパッケージが WSL2 で正常動作しましたので、メインの ThinkPad P14s にも導入。 Ubuntu 20.04 LTS から Ubuntu 22.04LTS にアップグレードインストールしています。 30分ほど。

Wayland セッションを使うと、現在 Indicator Stickynotes(付箋)でウインドウ位置や Dock アイコンの制御、Alacritty のウインドウ描画が違和感のある感じになりましたので、いったん X.org セッションで動作させています。

ハードウェア的には 2点問題がでています。

Focusrite Scalett Solo オーディオインターフェース:

ThinkPad には USB オーディオインターフェースとして、Focusrite 社の Scalett Solo を接続していますが、ALSA からの認識で対応するサンプリングレートが低く(44100, 48000のみ)申告されてしまうという問題がでました。

$ uname -a
Linux thinkpad-p14s 5.15.0-27-generic #28-Ubuntu SMP Thu Apr 14 04:55:28 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

$ cat /proc/asound/USB/stream0
Focusrite Scarlett Solo USB at usb-0000:07:00.4-2, high speed : USB Audio

Playback:
  Status: Stop
  Interface 1
    Altset 1
    Format: S32_LE
    Channels: 2
    Endpoint: 0x01 (1 OUT) (SYNC)
    Rates: 44100, 48000
    Data packet interval: 125 us
    Bits: 24
    Channel map: FL FR

Ubuntu 20.04 LTS では次のように 44100, 48000, 88200, 96000, 176400, 192000 となります。

$ uname -a
Linux hiromasa-t420s 5.13.0-40-generic #45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

$ cat /proc/asound/USB/stream0
Focusrite Scarlett Solo USB at usb-0000:00:1d.0-1.2, high speed : USB Audio

Playback:
  Status: Stop
  Interface 1
    Altset 1
    Format: S32_LE
    Channels: 2
    Endpoint: 0x01 (1 OUT) (SYNC)
    Rates: 44100, 48000, 88200, 96000, 176400, 192000
    Data packet interval: 125 us
    Bits: 24
    Channel map: FL FR

96KHz などが設定できないと、ハイレゾ系の音源や高サンプリングレートのソフトシンセの開発に支障がでるためやや悩み。

カーネルのソースを眺めていたところ、次のようなソースが追加されていることを発見。

/*
 * Many Focusrite devices supports a limited set of sampling rates per
 * altsetting. Maximum rate is exposed in the last 4 bytes of Format Type
 * descriptor which has a non-standard bLength = 10.
 */
static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
					struct audioformat *fp,
					unsigned int rate)

Focusrite ではあるものの Solo が入っていないため、動作が不正しているのではないかと当たりをつけてさらにソースを追っていたところ、Skip reading sampe rate for devices というワークアラウンドフラグ(quirk_flags)がありましたので、試しに設定してみたところ、正しく(とは言わないかもですが…) 高いサンプリングレートも申告されるようになりました。(ちなみに device_setup=1 の指定で今回カーネルに追加された Focusrite のミキサードライバーが有効になるようです)

$ sudo vi /etc/modprobe.d/scarlett.conf
options snd_usb_audio vid=0x1235 pid=0x8211 device_setup=1 quirk_flags=0x1
$ cat /proc/asound/USB/stream0
Focusrite Scarlett Solo USB at usb-0000:07:00.4-2, high speed : USB Audio

Playback:
  Status: Running
    Interface = 1
    Altset = 1
    Packet Size = 144
    Momentary freq = 96000 Hz (0xc.0000)
  Interface 1
    Altset 1
    Format: S32_LE
    Channels: 2
    Endpoint: 0x01 (1 OUT) (SYNC)
    Rates: 44100, 48000, 88200, 96000, 176400, 192000
    Data packet interval: 125 us
    Bits: 24
    Channel map: FL FR

このことにより Jack も高いサンプリングレートで起動できるようになりました。(解決)

$ cat .jackdrc
/usr/bin/jackd -dalsa -dhw:USB -r96000 -p1024 -n2

ワークアラウンドなので ALSA に報告して修正してもらうのが正しいでしょうか。要調査。同件ですが Ubuntu 日本語フォーラムで質問させていただいておりました。

Apple AV アダプタ:

ThinkPad には外部の HDMI モニターを接続するために、USB-C Apple AV アダプタを転用して使っていましたが、接続したまま PC の電源を入れると画面が真っ黒になり Ubuntu 22.04 LTS がブートできない様子でした。5.15.0-27-generic にて確認。

対応する元気が出なかったので、いったん AV アダプタを使わず、本体の HDMI に直接モニターを接続するようにしたら good work となりました。(継続検証)

外部PPA と Snap アプリケーション:

登録している PPA は次のとおりです。

http://jp.archive.ubuntu.com/ubuntu jammy InRelease
http://jp.archive.ubuntu.com/ubuntu jammy-updates InRelease [109 kB]
http://jp.archive.ubuntu.com/ubuntu jammy-backports InRelease
http://packages.microsoft.com/repos/edge stable InRelease
http://jp.archive.ubuntu.com/ubuntu jammy-updates/main amd64 DEP-11 Metadata [6,632 B]
http://jp.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 DEP-11 Metadata [19.2 kB]
https://packages.microsoft.com/repos/code stable InRelease [10.4 kB]
https://packages.microsoft.com/repos/code stable/main arm64 Packages [82.7 kB]
http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
https://packages.microsoft.com/repos/code stable/main armhf Packages [82.6 kB]
http://archive.ubuntu.com/ubuntu jammy InRelease
http://ppa.launchpad.net/mmk2410/intellij-idea/ubuntu jammy InRelease
https://deb.nodesource.com/node_16.x focal InRelease
http://security.ubuntu.com/ubuntu jammy-security/main amd64 DEP-11 Metadata [6,632 B]
https://ppa.launchpadcontent.net/kicad/kicad-6.0-releases/ubuntu jammy InRelease

VSCode や Microsoft Edge、KiCAD、Intellij IDEA、Node.js 16 などうまく動作しています。

Lovery Composer Snap Linux 版も良い感じです…! Linux でもレトロぴこぴこシンセの打ち込みを楽しめます。:D

デスクトップアクセサリーの MaCoPiX もソースコードからビルドして起動できました。

$ sudo apt install libgtk-3-dev
$ git clone https://github.com/chimari/MaCoPiX.git
$ cd MaCoPiX
$ ./configure
$ make -j8
$ src/macopix

Ubuntu 22.04 LTS は、Wayland セッションが前述の理由でまだ使えないのが寂しいですが、GNOME 系のアニメーションもスムーズになって良さそうな雰囲気です。Snap 版 Firefox も今の所、げ!というところはなさそうです…(今後あるかもですがw

LTS 版にて5年間(+5年延長)、引き続き Ubuntu にお世話になります。ちなみに、メインで使う OS を Ubuntu (8.04から) にしてから 14年くらい経つようです。ひー。