PCMシンセの音源部分のブラックボックス化

この記事は [WebAudio Web MIDI API Advent Calendar 2016](http://qiita.com/advent-calendar/2016/webaudio) の3日目です。

目的はズバリこれ。

**「音源のプログラムはさほど気にせずに楽器アプリを作れるようにしたい」** 

Web Audio APIは[12月2日にmohayonaoさんがおっしゃている通り](http://qiita.com/mohayonao/items/c506f7ddcaac63694eb9)で「少しばかりは音楽プログラミングの知識が必要」なのです。でも、そこを何とかチャチャッと仕上げたいですよね。そこでシンセサイザーの音源部分をブラックボックス化しました。

#音源部分をブラックボックス化？
シンセサイザーの音源部分とは、指定された音階を鳴らす部分を指しています。今回はアナログ音源ではなくて、一般的に販売されているシンセサイザーが採用している方式であるPCM（Pulse-code Modurationの略、簡単に具体的に言うならば、例えばWaveデータ）を使う部分をブラックボックス化しました。
よって、音源部分の内部で何が行われているか理解せずともシンセサイザーの音源を作れるようになっています。

#Web的に使った技術やライブラリ
 + [Web Audio API](https://www.w3.org/TR/webaudio/)
 + [Web MIDI API](https://www.w3.org/TR/webmidi/)
 + [Polymer(Web Components)](https://www.polymer-project.org)

#デモアプリ
では、早速[デモアプリ](https://ryoyakawai.github.io/x-tonegenerator/)です。
<a href="https://ryoyakawai.github.io/x-tonegenerator/" target="_blank"><img width="203" alt="Screen Shot 2016-12-01 at 5.41.49 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/61221/a45cce69-2a32-7203-48da-ce4040afaf0a.png">
</a>
鍵盤をクリックすると音の出るアプリです。ピアノの音が鳴ります。とても基本的なアプリケーションです。MIDI鍵盤を接続することでリアル鍵盤からも演奏可能になっています。
ではソースを見てみてみましょう。60行程度と超短いです。これなら楽ちんです。
そしてよく見ると以下のような見慣れないタグが・・・。

```html:html
<x-webmidirequestaccess id="x-webmidi" input output></x-webmidirequestaccess>
<x-tonegenerator id="tg" config="./voiceconfig.json"></x-tonegenerator>
<x-keys id="xkeys"></x-keys>
<x-webmidiinput id="input-port"></x-webmidiinput>
````

これらはPolymerのエレメントで、エレメントの中にゴニョゴニョいろいろな処理が書いてあります。が、今回は心配無用です。気にしないでください。
もし、それでもPolymerが気になる方はHTML5 Expert.JPの連載記事[「基礎からわかる Web Components 徹底解説 〜仕様から実装まで理解する〜」](https://html5experts.jp/series/web-components-2/)を御覧ください。

#ブラックボックスした音源の本体はどれ？
**[x-tonegenerator.html](https://github.com/ryoyakawai/x-tonegenerator/blob/master/x-tonegenerator.html)** です。
大抵のPCMシンセサイザーは88鍵盤の全てを録音して再生している訳ではなく、幾つかの音（サンプル）を録音し変調させることで88鍵盤を鳴らしています。x-tonegeneratorではこの動作を提供してくれます。


##音源は変えることできるの？
もちろん可能です。
簡単にご説明いたします。まずはコードは[こちらからダウンロード](https://github.com/ryoyakawai/x-tonegenerator/archive/gh-pages.zip)して、解凍後に`index.html`を御覧ください。

###音源の設定
設定ファイルは以下のように`x-tonegenerator`タグ内に属性として`config`に指定しています。

````html:html
<x-tonegenerator id="tg" config="./voiceconfig.json"></x-tonegenerator>
````

 - `config`：設定データを書き込んだJSONのあるURLを指定

###設定ファイル（ [`voiceconfig.json`](https://github.com/ryoyakawai/x-tonegenerator/blob/master/voiceconfig.json)）
指定したほうがよい変数は以下の通り。
 - path: サンプルの実ファイルを格納するディレクトリ名
 - `name`: 音源名
 - `noteno`: 指定した音源のノート番号 （[対応表](http://ws.finalemusic.jp/UserManuals/Finale2012Win/Content/Finale/MIDI_Note_to_Pitch_Table.htm)）
 - `file`: ファイル名を指定します（対応ファイルはWave[hoge.wav]のみ）
 - `mode`: N（非ループ音源）かL（ループ音源）で指定（例えばオルガンはループ音源なのでLを指定）

###サンプルの実ファイルのコピー
`voiceconfig.json`の`path`で指定したディレクトリにコピーしてください。


以上で出来上がりです。Webサーバに全てを配置して（ファイル取得にFetch APIを使っていますので *file://* へのアクセスはクロスオリジンの制約に引っかかり正常に動作しません）、そのURLにアクセスしたら独自の音源を再生できるようになります。

##ピアノっぽくない部分がある！
1音のみのサンプルを88鍵盤に変調したからそんなことが起こります。
そこで、複数のサンプルを使い音程によって使うファイルを適切に変えることで[こうなります](https://ryoyakawai.github.io/x-tonegenerator/piano/)。
<a href="https://ryoyakawai.github.io/x-tonegenerator/piano/index.html" target="_blank"><img width="203" alt="Screen Shot 2016-12-01 at 10.34.09 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/61221/20d6eff2-359f-f387-6067-4c28b4b6d998.png">
</a>
この[「1つのみのサンプル利用したデモ」](https://ryoyakawai.github.io/x-tonegenerator/)と[「5つのサンプル利用したデモ」](https://ryoyakawai.github.io/x-tonegenerator/piano/)の低い音、高い音を聞くと認識できるはずです。上図の赤丸の「+」「-」でオクターブを変えて聴き比べてみてください。

## 5つのサンプル利用したデモの内部
5つのサンプルを1〜5chに割当て、それぞれの音程において、適切な音量、適切なチャンネルを鳴らす、つまり高い音は高い音程のサンプルで、低い音は低い音程のサンプルで鳴らすことでよりピアノっぽく聞こえるようになります。実際の処理は[このファイル](https://github.com/ryoyakawai/x-tonegenerator/blob/gh-pages/piano/scripts/piano.js)で行っています。
ご興味あればのぞいてみてください！

#おまけ：利用したPolymerのエレメントの役割り
##x-webmidixxx
Web MIDI APIを使い、MIDIデバイスの管理、メッセージのハンドリングを担当するエレメントです。[GitHub](https://github.com/ryoyakawai/x-webmidi/)、またBowerにx-webmidiとして登録しています。以下の一行でスクリプトのImportしています。

````html:html
<link rel="import" href="bower_components/x-webmidi/x-webmidirequestaccess.html">
````

##x-keys
画面のキーボードに関する動作を担当するエレメントです。クリック/タップでどの鍵盤が押下された/開放されたかを検出してメッセージとして伝えています。[GitHub](https://github.com/ryoyakawai/x-tonegenerator/)にx-tonegeneratorの中に、またBowerにもx-tonegeneratorとして登録しています。以下の一行でスクリプトの本体をImportしています。

````html:html
<link rel="import" href="bower_components/x-tonegenerator/x-keys.html">
````

##x-tonegenerator
押下された鍵盤によって音程によって作り出し発音を担当するエレメントです。[GitHub](https://github.com/ryoyakawai/x-tonegenerator/)、またBowerにもx-tonegeneratorとして登録しています。以下の一行でスクリプトの本体をImportしています。

````html:html
<link rel="import" href="bower_components/x-tonegenerator/x-tonegenerator.html">
````

#謝辞
音源部分であるx-tonegeneratorは[@g200kg](https://twitter.com/g200kg)さんに許可を得て[GMPlayer](http://www.g200kg.com/webmidilink/gmplayer/)のコードを改造して作成しています。g200kgさんありがとうございました 🙂