GR-SAKURAでYM2203(OPN)を制御するFM音源シールドのライブラリとサンプルスケッチを公開します。ソフトの開発環境はHEW+GCC+E1です。Webコンパイラでもたぶん使えると思いますが、未確認です。
ファイル
YM2203_MMLplayer.h/cpp | MMLを解釈してYM2203で演奏するプレイヤーのクラス 下記のYM2203クラスをラップします。 |
YM2203.h/cpp | YM2203を直接制御するクラス |
YM2203_Timbre.h/cpp | YM2203の音色構造体 |
gr_sketch.cpp | サンプルスケッチ |
なお、YM2203_MMLplayerクラスではタイミング制御のためにTMR0によるタイマ割り込みを利用しています。このため、割り込みベクタとハンドラを定義している intvect.c に下記の修正を施す必要があります。
// TMR0_CMIA0 // void Excep_TMR0_CMIA0(void){ } ↑1500行めあたりのこの行をコメントアウトします。
YM2203_MMLplayerクラスの使用方法
下記ヘッダファイルをインクルードし、グローバルインスタンスMMLplayerが提供するメソッドをコールします。
#include "YM2203_MMLplayer.h"
以下に、メソッドの仕様を記します。
void MMLplayer.begin()
YM2203デバイスおよびMMLプレイヤーを初期化します。setup()でコールします。
void setup()
{
MMLplayer.begin();
}
void MMLplayer.setTempo(int bpm)
テンポを設定します。全チャンネル共通です。
- bpm テンポ。1分間の拍数を指定します。
MMLplayer.setTempo(58); // アダージョ MMLplayer.setTempo(72); // アンダンテ MMLplayer.setTempo(92); // モデラート MMLplayer.setTempo(132); // アレグロ
void MMLplayer.setVolume(int ch, int volume)
チャンネルごとに音量を設定します。
- ch チャンネル番号。0〜2がFM音源、3〜5がSSG音源です。
- volume 音量。0〜15で指定します。0が最小、15が最大の音量です。
チャンネル番号は、0〜5の数字の他、下記の定数でも指定できます。
定数名 | 値 | 意味 |
---|---|---|
FM_CH1 | 0 | FM音源チャンネル1 |
FM_CH2 | 1 | FM音源チャンネル2 |
FM_CH3 | 2 | FM音源チャンネル3 |
SSG_CH_A | 3 | SSG音源チャンネルA |
SSG_CH_B | 4 | SSG音源チャンネルB |
SSG_CH_C | 5 | SSG音源チャンネルC |
MMLplayer.setVolume(FM_CH1, 14); MMLplayer.setVolume(FM_CH2, 11); MMLplayer.setVolume(FM_CH3, 8); MMLplayer.setVolume(SSG_CH_A, 11); MMLplayer.setVolume(SSG_CH_B, 8); MMLplayer.setVolume(SSG_CH_C, 8);
void MMLplayer.setEnvelope(int ch, int type, int interval)
チャンネルごとにエンベロープを設定します。(SSG音源のみ)
- ch チャンネル番号。3〜5のSSG音源のみ有効。
- type エンベロープの形状。(下記)
- interval エンベロープの周期 (0〜65535)。周期は interval * 256 usecになります。
エンベロープの形状は、8〜15の値によって下図のように設定されます。表中の波形図のTが、エンベロープの周期です。
エンベロープについての詳細は、FM音源とPSG音源の基礎とYM2203のレジスタ設定(PSG音源編)を参照してください。
※ エンベロープと音量を同時に設定することはできません。MMLplayer.setVolume関数を呼ぶとエンベロープの設定は無効になり、MMLplayer.setEnvelope関数を呼ぶと音量の設定は無効になります。
MMLplayer.setToneNoise(int ch, int mode)
チャンネルごとにトーン/ノイズのミックスを設定します。(SSG音源のみ)
- ch チャンネル番号。3〜5のSSG音源のみ有効。
- mode トーン/ノイズ モードの指定。(下記)
SSGチャンネルの出力は、modeに下記の定数を与えることにより、(1)トーンのみ (2)ノイズのみ (3)トーン+ノイズ のいずれかを設定できます。
TONE_MODE | トーンのみ出力 |
NOISE_MODE | ノイズのみ出力 |
TONE_NOISE_MODE | トーン+ノイズを出力 |
void MMLplayer.setTimbre(int ch, YM2203_Timbre *timbre)
チャンネルごとに音色を設定します。(FM音源のみ)
- ch チャンネル番号。0〜2のFM音源のみ有効。
- timbre 音色データ構造体をポインタで渡します。
音色の設定には、YM2203_Timbre構造体を用います。AR,DR,SR,RR,SL,TL,KS,ML,DTについては、4つのオペレータに対するパラメータをまとめて設定するメソッドを用意しました。
音色の各パラメータについての詳細は、FM音源とPSG音源の基礎とYM2203のレジスタ設定(FM音源編)を参照してください。
// エレキベースの音色定義 YM2203_Timbre tmbEBass; tmbEBass.algorithm = 2; tmbEBass.feedback = 5; tmbEBass.opMask = MASK_ALL; tmbEBass.setAR(31, 31, 31, 31); tmbEBass.setDR( 8, 14, 16, 12); tmbEBass.setSR( 0, 6, 3, 5); tmbEBass.setRR( 0, 9, 0, 8); tmbEBass.setSL( 3, 2, 2, 2); tmbEBass.setTL(34, 42, 20, 0); tmbEBass.setKS( 0, 0, 0, 0); tmbEBass.setML( 0, 8, 0, 1); tmbEBass.setDT( 3, 0, -3, 0); // 音色の設定 MMLplayer.setTimbre(FM_CH1, &tmbEBass);
algorithm
アルゴリズム。0〜7。4つのオペレータのつなぎ方を指定します。
feedback
フィードバック。0〜7。オペレータ1のセルフ・フィードバック量を指定します。
opMask
オペレータマスク。0〜15。4つの各オペレータの有効/無効を指定します。
setAR( op1, op2, op3, op4 )
4つのオペレータに対して、アタック・レイトを指定します。各0〜31。
setDR( op1, op2, op3, op4 )
4つのオペレータに対して、ディケイ・レイトを指定します。各0〜31。
setSR( op1, op2, op3, op4 )
4つのオペレータに対して、サステイン・レイトを指定します。各0〜31。
setRR( op1, op2, op3, op4 )
4つのオペレータに対して、リリース・レイトを指定します。各0〜15。
setSL( op1, op2, op3, op4 )
4つのオペレータに対して、サステイン・レベルを指定します。各0〜15。
setTL( op1, op2, op3, op4 )
4つのオペレータに対して、トータル・レベルを指定します。各0〜127。
setKS( op1, op2, op3, op4 )
4つのオペレータに対して、キース・ケールを指定します。各0〜3。
setML( op1, op2, op3, op4 )
4つのオペレータに対して、マルチプルを指定します。各0〜15。
setDT( op1, op2, op3, op4 )
4つのオペレータに対して、デチューンを指定します。各0〜7
void MMLplayer.setGateTime(int ch, int gateTime)
チャンネルごとにゲートタイムの割合を設定します。
- ch チャンネル番号。0〜2がFM音源、3〜5がSSG音源です。
- gateTime ゲートタイムの長さ。1〜8で設定します。
ステップタイムに対するゲートタイムの長さの比を設定します。ステップタイムとは、音符の長さで指定される時間です。ゲートタイムとは、実際に音が出ている時間です。ステップタイム8に対するゲートタイムの長さで設定します。ステップタイムはつまり1が最も短く、8が最も長くなります。
void MMLplayer.setNote(int ch, char* note)
チャンネルごとに楽譜データをMMLで設定します。
このライブラリで使用できるMMLはN-88BASICのMMLの下位互換であり、下記のコマンドに対応しています。
CDEFGAB | ドレミファソラシを表します。後に音符の長さを指定できます。たとえば8分音符のドならC8。 |
R | 休符。これも長さを指定できます。 |
1,2,4,8,16,32,3,6,12,24 | 音符の長さ。たとえば8は8分音符。 |
. | 付点。たとえばC4.は付点4分音符のドを表します。 |
+(#) | シャープ。たとえばC+またはC#でド#を表します。 |
- | フラット。たとえばC-でド♭を表します。 |
O | オクターブを指定します。1〜8。 |
L | デフォルトの音長を指定します。 |
Q | ゲートタイムの割合を指定します。音符の長さのn/8のあいだ音が出力されます。 |
V | 音量を指定します。0〜15。 |
> | オクターブを上げます。 |
オクターブを下げます。 | |
& | タイまたはスラー。 |
@ | 音色を選びます。 ※データは作成中 |
T | ※未対応 テンポを指定します。BPM。 |
{ } | ※未対応 連符。 |
void MMLplayer.play(void)
設定された楽譜データを演奏します。
このメソッドは非同期型です。演奏を開始すると関数から戻ってきます。以降はバックグランドで演奏を継続します。
void MMLplayer.stop(void)
演奏を強制停止します。
bool MMLplayer.isPlaying(void)
演奏中か否かを返します。
このメソッドは、MMLplayer.play()で演奏を開始した場合に、演奏の終了をチェックするのに用います。
- 戻り値 true:演奏中 / false:停止中
// 楽譜の設定 char *note1,*note2,*note3; note1 = (char*)"L8Q7O4V14" "DBAGD4RDDBAGE4REE>C<BAF+4R>DDDC<AB4RD" "DBAGD4RDDBADE4REE>C<BA>DDDDEDC<AGR>D4"; note2 = (char*)"L8Q4O5V13" "RRRRRD16C+16D16C+16DRRRRRE16D+16E16D+16E" "RRRRRF+16F16F+16F16F+RRRRRD16D+16E16D+16D" "RRRRRD16C+16D16C+16DRRRRRE16D+16E16D+16E" "RRRRRF+16F16F+16F16F+RRRRRRD4"; note3 = (char*)"L8Q8O4V14" "G4D4G4D4G4AB>C4<G4>C4<G4A4D4A4D4GDEF+" "G4D4G4D4G4AB>C4<G4>C4<G4A4D4ADEF+G4D4"; MMLplayer.setNote(FM_CH1, note1); MMLplayer.setNote(FM_CH2, note2); MMLplayer.setNote(FM_CH3, note3); MMLplayer.setNote(SSG_CH_A, ""); // 使用しない場合は空文字列 MMLplayer.setNote(SSG_CH_B, ""); // 使用しない場合は空文字列 MMLplayer.setNote(SSG_CH_C, ""); // 使用しない場合は空文字列 // 演奏を開始する MMLplayer.play(); // 演奏が終わるまでLEDを点滅 int blink = 1; while( MMLplayer.isPlaying() ){ digitalWrite(PIN_LED0, blink); delay(500); blink = 1-blink; // スイッチ押したら停止 if (digitalRead(PIN_SW) == 0){ MMLplayer.stop(); } }
void MMLplayer.playAndWait(void)
設定された楽譜データを演奏します。
このメソッドは同期型です。演奏が終了するまで関数から戻ってきません。
// 演奏する MMLplayer.playAndWait(); // 演奏が終わってからLED点灯 digitalWrite(PIN_LED0, 1);