2015年8月15日土曜日

ソフトシンセの作り方(7) - VGS-SAL

VGSのソフトシンセ(波形メモリ音源)の開発で得られた知見を元に、PCやスマートフォンで動くソフトシンセの作り方を解説していきます。一冊の本が書ける程度の分量なので、幾つかのパートに区切って解説していきます。このシリーズを一通り読めば、サウンドプログラミングについて全くの素人でも、PCやスマホなどのプラットフォームで動くオリジナルのソフトシンセが作れる程度になります(たぶん)

なお、このシリーズで扱うプログラミングの例題のビルドには、パソコンが必要です。OSは私が使っているMac OS X向けに解説を記述しますが、Linux(※ALSAに対応しているもの)やWindowsでも問題ありません。

今回は、VGS-SALについて解説します。

VGS-SAL

前回の記事で紹介した低レベルAPIのひとつひとつを解説すると、それだけで膨大な量の解説が必要になってしまいます。そこで、低レベルを直接叩くのではなく、VGSSALSound Abstraction Layout)を利用することにします。
VGS-SALは、WindowsDirectSound)、LinuxALSA)、MaciOSAndroidの低レベルAPIを抽象化したものです。これを用いることで、全OS共通実装で「波形直書き」の実装ができます。

sal-sample

vgs2リポジトリの sample/sal-sample ディレクトリにVGS-SALを用いたサンプルプログラムが格納されています。
このサンプルプログラムでは、VGS-SALを用いて440Hzのサイン波を鳴らし続けます。
サンプルプログラムは、ターミナルで以下のコマンドを実行すれば、ビルドできます。
$ cd ~/vgs2/sample/sal-sample/ && make
sample/sal-sample ディレクトリに格納されている makefile と saltest.c を見てみましょう

(1)makefile

  • saltest.cをビルドする手続きが書かれています

    (2)初期化: init_sound_cli

    • SALの初期化をします
    • cliとは Command Line Interface(コマンド) の略です
    • Windowsの場合、GUI用の初期化処理が別にありますが、その点についての解説は省略します
    • UNIXOSの場合、CLI用とGUI用の間に違いはありません

    (3)終了: term_sound

    • SALが用いているシステムリソースを解放します
    • かならずプログラムの最後で呼び出すようにしてください

    (4)バッファリング: sndbuf

    • 一定間隔毎にSALからコールバックされるバッファリング処理です
    • C++で実装する場合は、必ず extern "C" で宣言するようにしてください
    • 引数 buf : 波形情報を格納するバッファです
    • 引数 size : 波形情報を格納すべきサイズです
    • buf の内容を変更する処理は、必ず lock()  unlock() で囲まなければなりません
    • 上記サンプルでは、440Hzのサイン音 が鳴り続けることになります

    (5)バッファリング処理の内容

    sndbuf  lock()  unlock() で囲まれた範囲(バッファリング処理)の実装内容をもう少しブレイクダウンして見ていきましょう。
    1行目: for(i=0; i < size16; i++, ptr++) {
    2行目:    *ptr = (short)(sin(r) * 16384);
    3行目:    r += PI2 / (22050 / 440); /* 440Hz */
    4行目: }
    1行目
    • size16  引数buf (8bit array)  16bit array とした時の長さです
    • そして、i = 0  size16-1 の間、ptr をインクリメントしながらループすることになります
    • ptr  引数buf  16bit array にしたものです
    • ptr のインクリメントとは、__サンプリング周期のインクリメント(+1Hz)と等価ですね
    2行目
    • sin(r)  16384 にした数値を ptr に代入しています
    • sin関数 の戻り値は -1.0  1.0 の範囲の実数です
    • なので、ptr に代入している値は -16384  16384 の範囲の値です
    • つまり、この 16384 というマジックナンバーは 鳴らす音の大きさ を意味しています
    • 16bit -32768  32767 の範囲の数値なので、限界音量の 50% がこのプログラムで鳴らす音の最大音量となります
    3行目

    • r  2π ÷ ( 22050 ÷ 440 ) で加算しています
    • r  スタティック変数 なので、関数が return しても値が維持され続けます
    • そのため、sndbuf が呼び出され続けると 440Hz のサイン波 が発音され続けることになります

    追記
    umm...
    ここから先はコードベースの解説が多くなるのですが、bloggerだと結構辛いので、GitBook辺りを使ってイチから書き直すか。

    ソフトシンセの作り方(6) - 音を鳴らす方法

    VGSのソフトシンセ(波形メモリ音源)の開発で得られた知見を元に、PCやスマートフォンで動くソフトシンセの作り方を解説していきます。一冊の本が書ける程度の分量なので、幾つかのパートに区切って解説していきます。このシリーズを一通り読めば、サウンドプログラミングについて全くの素人でも、PCやスマホなどのプラットフォームで動くオリジナルのソフトシンセが作れる程度になります(たぶん)

    なお、このシリーズで扱うプログラミングの例題のビルドには、パソコンが必要です。OSは私が使っているMac OS X向けに解説を記述しますが、Linux(※ALSAに対応しているもの)やWindowsでも問題ありません。

    今回から実践的な内容に入ろうと思っていましたが、その前にコンピュータで音を鳴らす方法について、余談を挟みつつ簡単に解説します。


    コンピュータで音を鳴らす方法

    音と情報量の関係

    コンピュータが音を鳴らす方法は、時代と共に大きく変化(進化)してきました。
    そもそも、音というのはデータ量として極めて大きな部類のものです。
    昨今のスマートフォンアプリケーションは、市販ゲームなら数百MB以上のものがゴロゴロありますが、そのゲームの構成要素の内、音のデータが占める割合はかなり大きいです。
    一般的なCD音質の場合、1秒間のデータ量は 44100(サンプリング周波数)× 216bit× 2(ステレオ)バイト(176400バイト=172KB です。そのため、1分間で約10MBもの容量を必要とします。 圧縮技術の進化により、1/10程度への圧縮(エンコード)が可能になりましたが、それでも1分間で約1MBのディスク容量を必要とすることになります。
    RPGなどの音楽数が多いジャンルのゲームなら、音楽だけで数百MBもの容量を喰うことになります。

    チップチューン音源が生まれた理由

    昨今のPCやスマートフォンなら、それぐらいの容量でも問題無く処理できますが、8bit16bitの頃のコンピュータでは、そんなに大きな容量のデータ処理は不可能です。また、CPU性能も遅いので、仮に現代の圧縮技術があったとしても、リアルタイムにデコードしながら再生することは、ほぼ不可能です。
    そのため、制御信号だけで音を鳴らす仕組みとして、米国General Instruments社のAY-3-8910や、YAMAHAYM-2203OPN)といったLSIが誕生しました。

    チップチューン音源が廃れた理由

    しかし、Windows 95が登場した頃を契機に、PCが一般家庭に普及し、ストレージやメモリの容量、CPUの演算速度が爆発的に進化した結果、わざわざ音楽専用のLSIが無くても、音声全般を扱えるPCM音源だけで全て事足りるようになったため、チップチューン音源をPCに搭載する実用上の理由が無くなったことで、ハードウェアとしてのチップチューン音源 は、完全に姿を消しました。
    ただし、SUZUKI PLANはその時代の流れに逆らって、チップチューン音源を復活させることを目指して活動しています。東方BGM on VGSも、いわばその活動の一環です。
    それはともかくとして、現代のコンピュータで音を鳴らす手段は PCM音源に一本化された といえます。
    という訳で、コンピュータでPCM音源を用いて音声を再生する方法について解説します。

    PCM音源で音声を再生する方法

    PCM音源で音声を再生する手段には、大きく分けると 低レベルAPI高レベルAPI の2種類があります。

    低レベルAPI

    最も原始的なPCMを再生する手段で、「波形直書き」と呼ばれる方式です。
    低レベルAPIは、PCM音源デバイスに対して、PCMデータ配列を入力することで、PCM音源が発音するという極めてシンプルな仕組みで、WindowsLinuxMacAndroidiOSのどのOSでも提供されています。
    ただし、Androidの場合、波形直書きができるようになったのはバージョン2.3.3以降からのみで、バージョン2.2以前は、MediaPlayerと呼ばれる音楽を再生する仕組みと、SoundPoolと呼ばれるaacoggなどのファイルから効果音を鳴らす仕組みしか提供されていませんでした。

    高レベルAPI

    Android 2.2以前から有る MediaPlayer SoundPool は、非常に簡単に音を再生できる反面、独自に生成した波形データを再生することができないため、自由度が低く、またレイテンシが酷すぎてゲーム等では使い物にならない残念な機能です。 (iOSでもAudioUnitで似たような機能が提供されていますが、これはAndroidよりは大分マシです)
    このように、自由度を犠牲にしてより簡単に音を再生できる方式 のことを高レベルAPIと呼びます。 高レベルAPIでは自由度が低すぎるため、ソフトシンセを作ることは不可能なので、以後本書では取り扱いません。

    OSの低レベルAPI

    OS
    低レベルAPI
    備考
    Windows
    Wave Mapper, Direct Sound
    本書ではDirectSoundを使用
    Linux
    ALSA, OSS (Open Sound System)
    本書ではALSAを使用
    Mac
    Open AL
    iOS
    Open AL
    Android
    Open SL/ES
    Mac iOS OpenAL は、全く同じです
    見事に四者四様ですね。
    ですが、できることは皆同じ「波形直書き」なので、使い方にはそれほどの大差はありません。

    合理的ではないものを作りたい

    ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...