ニンテンドーDSにはPSG音源という音源が入っています。これを使って特定の周波数の音を出す事もできます。
特定の周波数の音を出す
PA_PlayPSG(8,//チャンネル8-15が指定できる。 vol,//ボリューム(0-127)を指定 64,//パン(0-127)を指定(左右の音量の比率) freq,//周波数(単位Hz)を指定 duty);//デューティ比(0-7)(音としては音色のようなものが多少変わる)
音を消す場合は同じチャンネルで上書きすればいい
PA_PlayPSG(8,//チャンネル 0,0,0,0);
(PAlibでのこれらの関数のサンプル「examples」->「Sound」->「ASlib」->「PSG」)
MIDIファイルは音の発音されるタイミングとどの音を発音するかという情報と消音されるタイミングとどの音を消音するかという情報の集まりです。(実際にはもっと細かい情報を保持していますが、とりあえず音がでればいいという場合はこれだけ考えれば十分でしょう。)
MIDIデータはヘッダチャンクと呼ばれるファイルの先頭部分に時間単位というものが書かれており、ここに4分音符の長さの値が入っており、この値との相対的な時間ですべての時間を考えます。(デルタタイム)
では、この4分音符の具体的な長さはどのようにして決めるかというと、セットテンポというメタイベントを通してμsec単位で表します。これにより、スピードの変更などが容易にできるような作りになっています。
実際にMIDIファイルをバイナリで読み込むのは大変な作業です。なぜならMIDIはファイルを少しでも小さくするために可変長となっている部分が多いからです。また、フォーマットも0、1、2とあり、すべてに対応させるのは大変です。そこで、よくできたライブラリを使うことにします。このライブラリを使い、必要のない情報を読み飛ばしつつ、実際に音を出す情報だけを切り出します。
作り出すデータは以下のような感じ
種類(unsigned char) |
時間:単位ms(unsigned long) |
値(unsigned char) |
0:消音 | 消す音の番号 | |
1:発音 | 発音する音の番号 |
このようにして作りだしたデータをDetuneMIDI構造体の配列として定義します。
struct DetuneMIDI{ unsigned char type; unsigned long milisec; unsigned char key; }; struct DetuneMIDI out[]={ {1,13800,83},{0,13966,83},{1,126000,82},{0,126166,82},{1,126200,78}, /*中略*/ {0,126366,78},{1,126400,78},{0,126566,78},{1,126600,80},{0,126673,80},{1,126700,80} }; #define out_size (sizeof(test)/sizeof(DetuneMIDI))
この配列を実際のプログラムにコピペ(またはファイルごとリンク)して、この配列を読みながら音を出していきます。このような、ソースコードを自動的に吐き出すプログラムを作りました。エラー処理などの面倒な部分は省いてありますが、実用上問題のない程度にはなっていると思います。
ダウンロード上のDetuneMIDIを再生するに当たり、時間というものを計測する必要があります。しかもミリ秒程度の精度が求められる。PAlibで時間というとPA_RTC構造体があるが、これは日付等までわかる代わりに秒までしか測定してくれない。(PAwiki Day 8 - DS Hardware)
ここでは1秒の60分の1まで図れるPA_VBLCounterを使う。(自分が調べた中ではこれが最も細かく測れる方法だと思ったが、もっと細かく測る方法があるのかもしれない)
測定開始(測定中なら値はリセットされる)
PA_VBLCounterStart(0);//カウンタの番号(0-15で指定)
現在の値を取得(測定開始関数からの経過時間)
PA_VBLCounter[0];//配列の添字にカウンタの番号(単位は1/60秒)
カウンタの停止
PA_VBLCounterPause(0);//カウンタの番号
detuneMIDIを再生するだけの簡単なプログラムを書いてみた。
ダウンロード