数年前に製作したFM音源ジュークボックスをソフトウェア化してみました。
OS Xコマンドライン版(OS X10.4以上、PowerPC/Intel/ARM)とクラシック版(漢字Talk7.5以上、68020以上)を作りました。
おまけで、Windowsコマンドライン版もあります。

(2015/03/22更新)
Raspberry Pi 2版を追加しました。
(2017/12/17更新)
Metal/OpenCL版、CodeWarrior(PPC)版を追加しました。
(2018/06/03更新)
複数曲の演奏が途中で終わることがあるバグを修正しました。
(2021/05/23更新)
Apple Silicon Macに対応しました。
(2021/07/11更新)
Apple Silicon Macで、音源データでパーカッションを指定した場合に発音が正しくない不具合を修正しました。
(2023/02/05更新)
Windows用の修正
ピッチベンドセンシティビティ31まで対応
(2023/03/19更新)
エンベロープが正しく設定されないことがあるバグを修正しました。
ソースをGitHubに移しました。

https://github.com/kwhr0/FM-tone-generator

OS X版

対応CPUはPowerPC/Intel/ARMで、性能を出すため、AltiVec/SSE/NEONに対応します。
AltiVecでプログラムを書こうとしてAppleのサイトに行っても当時の資料は無くなっているため、
資料を探したところ、チップメーカーのfreescaleにありました。

AltiVec(TM) Technology Programming Environments Manual
AltiVec(TM) Technology Programming Interface Manual

あたりです。
SSEは

Intel IntrinsicsGuide

など、
NEONは

RealView(R) Compilation Tools アセンブラガイド
ARM Compiler toolchain Compiler Reference

などを見れば書けます。

SIMDの部分は、intrinsicのままでは見づらいので演算子のオーバーロードを多用しました。
LLVMではintrinsicをベタに書いたのとほぼ同じ性能が出ましたが、PowerPC用の古いコンパイラではだいぶ差が出たので、PowerPCだけベタに書いたものもあります。
OS X10.6以上ではGCDによるマルチスレッドに対応します。
ついでに、AUGraphでリバーブ効果を付けています(Audio.mm)。

htsfmsの音色ファイルを読むことができます。
tone1.dat, tone2.dat, …という名前にして、場所をMakefileのTONEDIRで指定します。
TONESは3つ定義していますが、tone4.bin以降増やすこともできます。

音色コンバータをC#で書いたので、まずMonoをインストールします。
Intelなら最新版で大丈夫ですが、PowerPCではバージョン2.4.3くらいがいいようです。
古いファイルはここにあります。
Monoがインストールできたら、MakefileのCXXFLAGSを環境に合ったものに設定し、
$ make
でビルドして、
$ ./fm_OSX [オプション] <MIDIファイル>
で実行します。
オプションで、同時発音数、音色ファイルの番号、PSGモードの指定ができます。
PSGモードは、元になる波形が矩形波で、変調はかからなくなり、PSGっぽい音になります。
詳しくは引数なしでfm_OSXを実行してください。

音切れしない最大発音数は、PowerPC G4/1.33GHzで70音程度、Core2Duo/1.83GHzで150音程度でした。
i7/2.7GHzだと200音でもCPU使用率50〜60%で済んでいます。

クラシック版

Intel版、PowerPC版があるのなら68k版も。ということでクラシック版も作りました。
かつてのApple純正ツール、MPWを使います。
余談ですが、その昔仕事で書いていたときはSymantec C++を使っていて、MPWは今回初めて使いました。
まずMPW-GM.imgを検索してダウンロードし、マウントして中身をHDDにコピーします。
OSが7.6以前の場合、Interfaces&Libraries:RuntimeLibraries:Required for MPW:Required for Pre-Mac OS 7.6の中身を機能拡張フォルダにコピーして再起動します。
OS X版をmakeしたあとmake mpwしてできるフォルダmpwを68kMacに持っていきます。
そのフォルダ内のfm_68k.makeをダブルクリックすると、fm_68k.makeとWorksheetが開きます。
Worksheetの最後に
scpp prefix.h -mc68020 -dumpc prefix.pch
と入力してenterを押します(returnではありません)。マウスカーソルが通常に戻るまで待ちます。
続いてcommand+Bを押すとダイアログが出るので、プログラム名fm_68kを入れてOKを押します。
エラー無く終了したらアプリケーションfm_68kができます。



演奏するファイルはアプリケーションと同じフォルダに置き、fm_68k.iniにファイル名を書きます。
fm_68kをダブルクリックして実行します。

BasiliskIIで実行してみたところ、そのJITコンパイラは驚異的な速さで、fs=22kHzで40音くらい出ます(MacMini2011/i7)。
8bit/22kHzのザラついた再生音が、懐かしい感じです。

Raspberry Pi 2版

最近Raspberry Pi 2 Model B(ラズパイ)を買ったので、これで走るものも作りました。
Raspbianがインストールされていることが前提です。
まず、
$ sudo apt-get install libasound2-dev
でサウンドライブラリをインストールしておきます。
OS X版をmakeしたあとmake raspiしてできるfm_raspi.tarをラズパイに持っていきます。
ラズパイ上で、
$ tar xvf fm_raspi.tar
$ cd fm_raspi
$ make
でビルドします。あとはOS X版と同様です。
スピーカー端子はバグかと思うくらいノイズが酷いので、USBスピーカーなどを繋ぐのがいいでしょう。

最大発音数はNEONなし1コアで21音、NEONあり4コアで110音程度でした。
クロック比から、旧ラズパイでは16音程度と推定され、新ラズパイは確かに6倍以上のパワーがあるようです。

なお、fm_raspi/Makefileで、CXXFLAGSをRaspberry Pi / general Linuxの方でビルドすると、PCのLinux用になります(CentOS5.11で確認済み)。

Metal/OpenCL版

6年間使っていたMac mini 2011が壊れてしまい、同2014の中古を買いました。
MetalとAVX2命令が使えるようになったのでそれぞれ書いてみました。
ついでなのでOpenCL版も書いてみました。
AVX2についてはSSEより遅くなってしまいました。
AVX2とSSEの切り替えペナルティかと思って逆アセンブルしたものを見てみましたが対策済みのコードになっていました(当たり前か)。
改善できそうにないのでそのままです。
Metalの方は、CPUより劇的に速いということはなく、使用電力は増えてしまいました。
GPGPUのノウハウがなく、こう書いた方が速いかな、というのがことごとく外れたので最適化はコンパイラ任せです。
OpenCLはMetalより遅かったです。
今回の実験では、CPUで演算した方が良かったという結論です。
せっかく作ったので何かの参考になればと公開します。
上記の変更以外に、複数曲の連続再生機能をつけました。Ctrl+Zで次の曲に進めます。

音数を256まで徐々に増やしていく過程の消費電力を計りました。
水色がCPUコアのみ、青がGPUなどを含めた消費電力です。
Metal:半分くらいから効率が悪化していきます。
OpenCL:特徴的なカーブを描いています。1/4くらいまで効率が悪化していくと思いきや、元に戻り、ギザギザしながら一定のペースで上がっていきます。
SIMD:一番優れています。一定のペースで上がっていきます。
noSIMD:SIMDより傾きが大きく、4/5くらいで飽和します。飽和しているときは1コア使い切って音切れしています(MULTI_THREADを定義しない場合)。

Metal

OpenCL


SIMD

noSIMD

CodeWarrior(PPC)版

Classic PPC用をCodeWarriorでビルドできるようにしました。
mpw用のフォルダをPPCマシンに持って行き、
(1) 全てのファイルのクリエータをCWIEに、ファイルタイプをTEXTにする。
(2) MacOS Toolboxのプロジェクトを作る。
(3) *.cppと*.rをプロジェクトに登録する。
(4) プロジェクト設定のC/C++ LanguageのPrefix Fileをprefix.hにする
これでビルドすればアプリができます。あとは68k版と同様です。

おまけ

OS X版をmakeしたあとmake vsしてできるフォルダvsをWindowsマシンに持っていきます。
Visual StudioでWin32コンソールアプリケーションの「空のプロジェクト」を作り、持ってきたファイルとwinmm.libを追加してビルドすると、Windows版ができます。
Visual Studio 2008 Express Editionで動作確認済みです。