YMF825を使ってFM音源でサファリメロディーを響かせてみた

**🎄この記事は [けものフレンズ Advent Calendar 2017](https://adventar.org/calendars/2115) 22日目の記事です🎄**

世界中にサファリメロディーを響かせたい、それをFM音源で実現させたことについての記事です。
ハード、ソフトの両面からアプローチを紹介していきます。


# きっかけ

ニコニコ動画で PC-88 の実機演奏動画があり、**FM音源** で何か演奏してみたいと考えました。

- **<a href="http://www.nicovideo.jp/watch/sm31361159">【ＦＭ音源】「ぼくのフレンド」（PC-88版 実機演奏）</a>**
- **<a href="http://www.nicovideo.jp/watch/sm31727452">【ＦＭ音源】「ようこそジャパリパークへ」（PC-88 実機演奏）ほか</a>**

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/231b8f7f-6ef8-edb0-c177-7ceb4efae168.jpeg" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/231b8f7f-6ef8-edb0-c177-7ceb4efae168.jpeg)

もともとFM音源で演奏させるハードとして [**RE:Birth**](http://ym2203.com/rebirth/) シリーズや [**FMの塔**](http://www.pyonpyon.jp/~fmtower/) を持っていましたが、自身でデータを打ち込んで実機演奏させたことはありませんでした。
偶然にも長期間、趣味に時間をかけられる機会が得られたため、**ハードとソフトの両方を自作して演奏させてみよう**、というのがきっかけです。

# 演奏させるために必要なコト、モノ

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/912bc625-dc12-9c11-603f-4a5142feb7ac.png" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/912bc625-dc12-9c11-603f-4a5142feb7ac.png)


FM音源に限りませんが、「演奏」には「**音源**」と「**制御**」が必要です。
ピアノ演奏には、「ピアノという音源」と「ピアニストという制御」が必要です。FM音源での演奏も同様に、FM音源自体とそれをコントロールするためのシーケンサが必要です。

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/0e3a7ed2-2e2f-68ff-d1a3-9a070591b370.png" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/0e3a7ed2-2e2f-68ff-d1a3-9a070591b370.png)

今回はFM音源は専用ICを使い、さらに **MIDI** を使って制御を行うことにしました。

MIDI を使って制御することで、演奏データの作成と編集は他ソフトを使えるようになり作業量が軽減します。
専用ICは MIDI の情報を直接扱えないために MIDI -> ICレジスタ への変換ドライバを自前で用意する必要があります。また、音色の制御も必要なため、音色エディタも必要となります。

（ところで、音源は IC でなくともエミュレータであればハード的にも楽になります。ただ、私は制限されたリソースの中で音楽を作ることにも興味があります。完全にソフトウェアに完結するのは本当に手軽ですが、できることが増えるため、後々に考えるべきことも増えてしまうことが多いです）

# YMF825 と YMF825Board

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/999d9469-c4dc-3c5c-4610-5b67ff565886.jpeg" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/999d9469-c4dc-3c5c-4610-5b67ff565886.jpeg)

今回は音源モジュールに [**YMF825Board**](http://uda.la/fm/) を用いました。単なる音源ICである **YMF825** を、水晶振動子やヘッドフォン端子などを付属し、より扱いやすいモジュールにしたものが YMF825Board です。[秋月電子](http://akizukidenshi.com/catalog/g/gM-12414/) などで販売しており、容易に入手が可能です。

YMF825 の簡単なスペックは以下の通りです。

- **電源**: 5V
- **ロジックレベル**: 5V または 3.3V に改造可能
- **同時発音数**: 最大 16 音
- **接続インタフェース**: SPI (max 10MHz)
- DAC搭載 (モノラル出力)
- 3-バンドイコライザ搭載

今回は YMF825Board を **同時に2枚使用** し、それぞれ左チャネルと右チャネルに割り当てることでステレオ出力としました。

# FT232H, Arduino, そして FT232HL

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/fef568c0-4a4e-516f-5dff-12a4b9d47a1d.png" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/fef568c0-4a4e-516f-5dff-12a4b9d47a1d.png)

さて、YMF825 を扱うためには接続インタフェースは [**SPI**](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E3%83%BB%E3%83%9A%E3%83%AA%E3%83%95%E3%82%A7%E3%83%A9%E3%83%AB%E3%83%BB%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9) (Serial Peripheral Interface) である必要があります。PC から SPI を直接使うことはできないため、USBを経由して USB から SPI に信号を変換するデバイスを使用しなくてはなりません。

Raspberry Pi や Arduino のようなデバイスは標準で SPI に対応しています。また、USB から SPI に変換するデバイスも多数発売されています。
今回は検証と実験のために以下のデバイスにて回路を組みました。

- [Adafruit FT232H Breakout](http://akizukidenshi.com/catalog/g/gM-08942/)
- [Arduino Nano](http://akizukidenshi.com/catalog/g/gM-09059/)
- [AE-FT232HL](http://akizukidenshi.com/catalog/g/gK-06503/)

どのデバイスも PC から受け取ったデータを SPI に変換する機能を搭載あるいはプログラム可能です。後述の経緯により、最終的には Arduino Nano と AE-FT232HL を使用しました。

# LibMPSSE-SPI のレイテンシと解決

## Adafruit FT232H Breakout

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/5a360aee-9c63-ff1e-fc84-df67f9490fba.jpeg" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/5a360aee-9c63-ff1e-fc84-df67f9490fba.jpeg)

当初は画像のように **Adafruit FT232H Breakout** を使っていましたが、問題にぶつかってしまいました。
PC から FT232H を介して SPI を扱えるライブラリである [LibMPSSE-SPI](http://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm) を使用していましたが、**1回の書き込み命令ごとに必ず 1 ms のレイテンシ（待ち時間）が必要になること** がわかりました。

YMF825 の場合、1回の書き込み命令で1レジスタ分書き込むことができ、複数のレジスタを1回の命令で書き込むことはできません。また、YMF825 は1音鳴らすためには3バイト分、消音するのに2バイト分を最低でもレジスタに書き込む必要があります。MIDI のように16チャネル分16音を鳴らす想定でいるため、16音同時発音には最短でも **48 ms** かかります。

**48 ms** という時間は一見して高速に見えます。しかし演奏目的の場合、音量や細かい音程、音色データも送る必要があるために、テンポの乱れやもたつきが頻発し、演奏が破綻します。さらにステレオの場合は2倍のレジスタ書き込みが必要なために、**96 ms** となります。

## Arduino Nano

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/69239404-bd56-6672-b3b9-8d7817c2e248.jpeg" height=360px>](https://qiita-image-store.s3.amazonaws.com/0/30370/69239404-bd56-6672-b3b9-8d7817c2e248.jpeg)

この USB to SPI でのボトルネックを解決すべく、次に使用したのが **Arduino Nano** です。
PC からのデータをシリアル通信で受け取り、SPI に変換する [**スケッチ**](https://github.com/nanase/ymf825/tree/for_arduino_nano/Arduino) を書きました。Arduino を扱うのはこれが最初だったこともあり少々手間取りましたが、1回のレジスタ書き込みにかかる時間は **208 μs**[^1] と4.8倍ほど高速になりました。

[^1]: Arduino Nano と 115,200 baud/s で通信をし、書き込み命令を送るのに3バイト (24バイト) 送るため、$$\frac{24\,\rm{bytes}}{115200\,\rm{baud/s}} = 208.333...\,\rm{\mu s} $$ よりおよそ 208 μs かかる。ただし SPI 書き込みの時間は考慮していない。

## AE-FT232HL

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/59a8a9b3-b644-1457-3495-dd178fb9f972.jpeg" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/59a8a9b3-b644-1457-3495-dd178fb9f972.jpeg)

Arduino Nano でもギリギリ演奏ができるレベルに達していたのですが、今回 [**YMF825Boardステレオ化基板**](https://sites.google.com/site/lostechmsx/home/products/ymf825stereo) を制作者の方から*人柱*として拝領することができたため、これにともなって **AE-FT232HL** を使って高速化を行いました。ブレッドボードからきちんとした基板の上で動かし、さらに電源周辺も最適化されたため音質は大幅に改善しています。

実は前述の Adafruit FT232H 使用時には気が付きませんでしたが、LibMPSSE-SPI よりもさらに低レベルの [**D2XX**](http://www.ftdichip.com/Drivers/D2XX.htm) というライブラリを使うことで高速に SPI を操作可能ということがわかりました。理論値の計測はしていませんが、Arduino Nano 使用時よりも実測値で **40～50倍**[^2] ほどレジスタの書き込み時間を短縮することに成功しました。

[^2]: 詳細は https://twitter.com/nanase_coder/status/939907411028017152 を参照。

# YMF825 MIDI Driver

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/7acedae2-5277-7431-6152-7a33be2b476f.png" width=721px>](https://qiita-image-store.s3.amazonaws.com/0/30370/7acedae2-5277-7431-6152-7a33be2b476f.png)

次はソフトウェアについて考えていきます。
前述のように、MIDI のデータを YMF825 は直接解釈できないため、変換を行うドライバを作る必要があります。YMF825 MIDI Driver を C# で制作し、画像のような構成を取りました。

**MIDI Driver** では、MIDI-IN から受け取った MIDI メッセージを解読して、YMF825 の命令に変換する部分です。そのため、MIDI ノートナンバーやピッチベンド、ボリューム、エクスプレッション、ベロシティなどを記憶し、それぞれ周波数と音量の値に変換しています。

**YMF825 Driver** では、MIDI Driver から受け取った命令を YMF825 が直接解釈できる アドレスとレジスタ のデータに変換します。また、レジスタ書き込みには一部で不可分なものがある[^3]ため、レジスタ書き込みをまとめ、一括で書き込むための **セクション** という機構を導入しました。

[^3]: Fnum/Block指定 (`0x0d`, `0x0e`)、ボイス番号指定 (`0x0b`) など、順序に依存した一連のレジスタ書き込みのことを指す。

**SPI Wrapper** は **D2XX** で SPI 通信を行うためのラッパークラスです。低レベルAPIの操作やバッファ管理、排他処理を行っています。

# トーンエディタGUI制作

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/f8f17244-a1eb-6a88-2a2f-290cc54eb8ce.png" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/f8f17244-a1eb-6a88-2a2f-290cc54eb8ce.png)

MIDI だけでは演奏の情報しか扱わないため、音色の情報についてもエディタを用意する必要がありました。
せっかくの GUI なので、FM音源のトーンパラメータの数値を直接弄るようなことは避け、画像のようなスライダやコンボボックスを使用した GUI にしました。特に、アルゴリズムやオペレータの波形などは数値表示ではわかりにくいため、画像を表示して選択できるようにしています。

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/9ddb9ae0-7cb9-b50f-ed92-2d32bb20a27e.png" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/9ddb9ae0-7cb9-b50f-ed92-2d32bb20a27e.png)

また、ようこそジャパリパークへ のオーイシおにいさんパートを演奏するにあたり、バンドパスフィルタをかけたような篭った音はFM音源だけでは難しいため、YMF825 に搭載されている **3-バンドイコライザ** を使用しました。イコライザと銘打たれていますが、実際は画像のように **双2次フィルタ** (Biquad filter) となっており、各係数を **実数で** 直接指定できるため、自由度の高いフィルタを実現できます。

こちらも GUI の持ち味を活かし、双2次フィルタのブロック図を表示させてみました。

[<img src="https://qiita-image-store.s3.amazonaws.com/0/30370/2ea26b8a-352c-f681-0c42-777264fd53e6.png" width=720px>](https://qiita-image-store.s3.amazonaws.com/0/30370/2ea26b8a-352c-f681-0c42-777264fd53e6.png)

また、今回は検証しながらの開発だったこともあり、レジスタが正しい値になっているかを確かめるためのレジスタマップも制作しました。本来ならばこのレジスタマップは IPC (プロセス間通信) で表示させたかったのですが、断念したためにホストアプリケーションに埋め込むような形になっています。こうした経緯もあり、このウィンドウだけ Windows Forms を使っています。他はすべて WPF です。

# 世界中に響けサファリメロディー

実機で演奏した動画を Youtube にて公開しています。
シーケンサを左上に、レジスタマップを右下に表示しています。

## [ようこそジャパリパークへ](https://youtu.be/vxJTOZNd3II)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">YMF825Board : どうぶつビスケッツ×PPP / ようこそジャパリパークへ<a href="https://t.co/z3fuF7JXO5">https://t.co/z3fuF7JXO5</a><br><br>動画公開しました～！<br>書き込み高速版なので前回よりもピッチベンド多めです。<a href="https://twitter.com/hashtag/ymf825?src=hash&amp;ref_src=twsrc%5Etfw">#ymf825</a> <a href="https://twitter.com/hashtag/%E3%81%91%E3%82%82%E3%81%AE%E3%83%95%E3%83%AC%E3%83%B3%E3%82%BA?src=hash&amp;ref_src=twsrc%5Etfw">#けものフレンズ</a></p>&mdash; 七瀬 (@nanase_coder) <a href="https://twitter.com/nanase_coder/status/942799580642422784?ref_src=twsrc%5Etfw">2017年12月18日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

AE-FT232HL、YMF825ステレオ化基板を使った高速・音質向上版です。イコライザを使用しています。

## [ぼくのフレンド](https://youtu.be/GQQDqepkJtg)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">YMF825Board : みゆはん / ぼくのフレンド<a href="https://t.co/qRkTgsrVo1">https://t.co/qRkTgsrVo1</a><br><br>公開しました。本日の YMF825Board交流会#1 にて紹介した演奏のフルバージョンです。<a href="https://twitter.com/hashtag/ymf825?src=hash&amp;ref_src=twsrc%5Etfw">#ymf825</a> <a href="https://twitter.com/hashtag/%E3%81%91%E3%82%82%E3%81%AE%E3%83%95%E3%83%AC%E3%83%B3%E3%82%BA?src=hash&amp;ref_src=twsrc%5Etfw">#けものフレンズ</a> <a href="https://twitter.com/hashtag/%E3%81%BF%E3%82%86%E3%81%AF%E3%82%93?src=hash&amp;ref_src=twsrc%5Etfw">#みゆはん</a></p>&mdash; 七瀬 (@nanase_coder) <a href="https://twitter.com/nanase_coder/status/936956332875194369?ref_src=twsrc%5Etfw">2017年12月2日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

こちらのほうが制作は先です。Arduino Nano をブレッドボード上で使用したもので、低速版です。
実は MIDI Driver のバグにより、音色のビブラートが **まったく** かかっていません。

# おわりに

回路製作、プログラム制作開始は10月上旬、それから3ヶ月で演奏できるレベルまで到達できました。実は自身、本日が誕生日でしたが25歳最後の大仕事ということで、サファリメロディーを響かせることができました。

久しぶりに WPF を使ったため GUI 制作に一番時間がかかってしまいましたが、こちらもバイナリは公開予定ですので以下の「ソース」からご参照ください。

[けものフレンズ Advent Calendar 2017](https://adventar.org/calendars/2115) を作らせていただきましたが、宣伝不足の中で参加表明・寄稿してくださった方々にこの場にて感謝の意を表したいと存じます。誠にありがとうございました。

2018年も けものフレンズと、けものフレンズを愛する皆さんにとって実り多き一年となることを切に願っています。

## ソース

**自身の制作物**

- **GitHub nanase/ymf825**
 - [Adafruit FT232H Breakout 向けソース](https://github.com/nanase/ymf825/tree/for_ft232h)
 - [Arduino Nano 向けソース](https://github.com/nanase/ymf825/tree/for_arduino_nano)
 - [AE-FT232HL 向けソース](https://github.com/nanase/ymf825/tree/20171222) - 執筆時点でのソースのためドキュメント未整備

- [**YMF825のFNUM, BLOCK値の計算方法について**](https://scrapbox.io/nanase/YMF825のFNUM,_BLOCK値の計算方法について)
- [**YMF825Boardで演奏する (1)**](http://nanase.hatenablog.jp/entry/2017/12/05/193626) - Arduino Nano を使うまでの詳しい経緯があります
- **YMF825Board交流会 #1**
 - 2017/12/02 に秋葉原で行われた交流会のLT資料と動画です
 - LT資料: https://www.slideshare.net/nanase_t/c-ymf825board
 - 発表動画: https://www.youtube.com/watch?v=0KVM0hIRRzw