#include <windows.h>
#include <math.h>
#include "qlist.h"
#define MIDIMSG(stat , data1 , data2) (DWORD)(stat | (data1 << 8) | (data2 << 16))
dialog(char* s){ MessageBox(NULL, s, "エラー", MB_OK); }
// メモリの並びを逆にする
mem_reverse(void *p, int size){
char tmp, *head_p, *tail_p;
int i, len=size/2;
head_p=(char*)p;
tail_p=((char*)p)-1+size;
for(i=0;i<len;i++){
tmp=*head_p;
*head_p=*tail_p;
*tail_p=tmp;
head_p++; tail_p--;
}
}
//==========================================
// MIDI_MSG
//==========================================
class MIDI_MSG{
public :
int m_delta_time;
unsigned char m_code, m_scale, m_vel;
char *m_ext;
int m_ext_len;
MIDI_MSG(){
m_ext=NULL;
m_code=m_scale=m_vel=0;
m_delta_time=0;
}
~MIDI_MSG(){
delete[] m_ext;
}
reset(){
delete[] m_ext;
m_ext=NULL;
}
};
//==========================================
// MIDI_TRUCK
//==========================================
class MIDI_TRUCK{
public :
int m_data_len, m_play_index, m_last_play_index;
QLIST<MIDI_MSG> m_msgs;
};
//==========================================
// MIDI_PLAYER クラス
//==========================================
#define BASE_TIMER_DIFF 5
#define minmax(a,x,b) (min (max (x, a), b))
class MIDI_PLAYER {
public :
int m_tempo; // テンポ(μsec)
short m_file_format_type, m_truck_chunck_len, m_time_base;
char m_division[2]; // 分解能
char m_rhythm[2]; // [0]/[1] 拍子
char m_clef[2]; /* 調号 [sf:mi]シャープまたはフラット記号の数を表すsf、メジャー/マイナーを示すmi。
sfはフラットの数を表すときはマイナス数値になる。また、miはメジャーのとき0、マイナのとき1になる。*/
PLIST<MIDI_TRUCK> m_trucks; // トラック本体
HMIDIOUT m_hmidi; // MIDIデバイス・ハンドル
UINT m_time_diff; // プロシージャ呼び出し時間誤差
int m_timer_id; // タイマーID
play_music_start(); // 音楽再生
play_music_proc(); // 再生プロシージャ
play_music_end(); // 再生停止
delta_to_ms(int delta); // デルタタイム→ミリ秒
static void CALLBACK midi_timer_proc (UINT id, UINT msg, DWORD user, DWORD dw1, DWORD dw2);
read_file(char *name); // ファイル読込
read_data(FILE *fp);
read_num(int *num, FILE **p_fp); // 数値取得(ファイル読込版)
get_msg_size(int ti){ return (m_trucks[ti])? m_trucks[ti]->m_msgs.size() : 0; }
get_step_size(int ti);
MIDI_PLAYER(); // コンストラクタ
~MIDI_PLAYER(); // デストラクタ
get_format_type(){ return m_file_format_type; } // フォーマット・タイプ返値
get_tempo(){ return m_tempo; }
reset();
};
// static プロシージャ
XLIST<MIDI_PLAYER*> g_midi_player_list;
void CALLBACK MIDI_PLAYER::midi_timer_proc (UINT id, UINT msg, DWORD user_data, DWORD dw1, DWORD dw2){
int i, len=g_midi_player_list.size();
for(i=0;i<len;i++){
if (g_midi_player_list[i]==(MIDI_PLAYER*)user_data) g_midi_player_list[i]->play_music_proc();
return;
}
return;
}
// コンストラクタ
MIDI_PLAYER::MIDI_PLAYER(){
m_file_format_type = m_truck_chunck_len =0;
m_time_base = 480;
m_tempo=500000; // テンポ120=500000μsec
m_division[0] = m_division[1] =0;
m_rhythm[0] = m_rhythm[1] =4;
m_clef[0] = m_clef[1] =0;
g_midi_player_list.push(this);
m_timer_id=0;
m_hmidi=NULL;
m_time_diff=BASE_TIMER_DIFF;
}
// デストラクタ
MIDI_PLAYER::~MIDI_PLAYER(){
int i, len=g_midi_player_list.size();
for(int i=0;i<len;i++){
if (this==g_midi_player_list[i]) {
g_midi_player_list.pop(i);
break;
}
}
}
// ステップ・サイズ取得
MIDI_PLAYER::get_step_size(int ti){
MIDI_TRUCK *ptruck;
ptruck=m_trucks[ti];
if (!ptruck) return -1;
int i, len=get_msg_size(ti), step_len=0;
for(i=0;i<len;i++) if (ptruck->m_msgs[i]->m_delta_time) step_len++;
return step_len;
}
// 数値読込(MIDI特化)
MIDI_PLAYER::read_num(int *num, FILE **p_fp){ // 数値取得(ファイル読込版)
int i, n;
BYTE c;
*num=0;
for(i=0;i<4;i++){
if (fread(&c, 1, 1, *p_fp)!=1) return -1;
n=((*num)<<7) | (c & 0x7f);
*num=n;
if (!(c & 0x80)) return i+1;
}
}
// デルタタイム→ミリ秒
MIDI_PLAYER::delta_to_ms(int delta){
return (m_division[0]>=0)?
delta*m_tempo/(1000*m_time_base) // ディヴィジョン=4分音符
: delta * 1000/(-m_division[0] * m_division[1]); // ディヴィジョン=分解能
}
// ファイル読込
MIDI_PLAYER::read_file(char *name){
int res;
FILE *fp=fopen(name, "rb");
if (!fp) return -1;
res=read_data(fp); // midi ファイル読込
fclose(fp);
return res;
}
// データ消去
MIDI_PLAYER::reset(){
int i, mi, mlen;
for(i=0;i<m_truck_chunck_len;i++){
mlen=m_trucks[i]->m_msgs.size();
for(mi=0;mi<mlen;mi++) m_trucks[i]->m_msgs[mi]->reset();
Sleep(0);
m_trucks[i]->m_msgs.del_all();
}
m_trucks.del_all();
Sleep(0); // メモリの大量解放時はタスクをOSにまわす
return 0;
}
// midi ファイル読込
MIDI_PLAYER::read_data(FILE *fp){
BYTE rs[5];
MIDI_TRUCK *ptruck;
MIDI_MSG *pmsg;
int i, mi, delta, res;
reset();
fread(rs, 1, 4, fp);
if (strncmp(rs, "MThd", 4)) return dialog("MIDIファイルではありません");
// データ長 常にフォーマット、トラック数、時間単位のデータ長の合計6
fread(&i, 1, 4, fp); mem_reverse(&i, 4);
if (i!=6) return dialog("このMIDIファイルは壊れています。 ");
fread(&m_file_format_type, 1, 2, fp); mem_reverse(&m_file_format_type, 2);
fread(&m_truck_chunck_len, 1, 2, fp); mem_reverse(&m_truck_chunck_len, 2);
fread( m_division, 1, 2, fp);
memcpy(&m_time_base, m_division, 2); mem_reverse(&m_time_base, 2);
for(i=0;i<m_truck_chunck_len;i++){
if (fread(rs, 1, 4, fp)!=4) return -1;
if (strncmp(rs, "MTrk", 4)) return dialog("このMIDIファイルは壊れています。");
ptruck = m_trucks.a_push();
if (fread(&(ptruck->m_data_len), 1, 4, fp)!=4) return -1;
mem_reverse(&(ptruck->m_data_len), 4);
mi=ptruck->m_data_len-4;
while(mi>0){
pmsg=ptruck->m_msgs.a_push();
res=read_num(&pmsg->m_delta_time, &fp);
if (res<0) return 0;
mi-=res;
if (fread(&pmsg->m_code, 1, 1, fp)!=1) return -1; mi--;
switch(pmsg->m_code & 0xF0){
case 0x90 :
case 0xA0 :
case 0xB0 :
case 0xE0 :
if (fread(&pmsg->m_scale, 1, 1, fp)!=1) return -1; mi--;
if (fread(&pmsg->m_vel, 1, 1, fp)!=1) return -1; mi--;
break;
case 0x80 :
case 0xC0 :
case 0xD0 :
if (fread(&pmsg->m_scale, 1, 1, fp)!=1) return -1; mi--;
break;
case 0xF0 :
switch(pmsg->m_code){
case 0xF0 :
res=read_num(&pmsg->m_ext_len, &fp);
if (res<0) return -1;
mi-=res;
pmsg->m_ext= new char[pmsg->m_ext_len+1];
if (fread(pmsg->m_ext, 1, pmsg->m_ext_len, fp)!=pmsg->m_ext_len) return -1;
mi-=pmsg->m_ext_len;
break;
case 0xFF : // メタ・イベント
if (fread(&pmsg->m_scale, 1, 1, fp)!=1) return -1; mi--;
res=read_num(&pmsg->m_ext_len, &fp);
if (res<0) return -1;
mi-=res;
pmsg->m_ext= new char[pmsg->m_ext_len+1];
if (fread(pmsg->m_ext, 1, pmsg->m_ext_len, fp)!=pmsg->m_ext_len) return -1;
mi-=pmsg->m_ext_len;
break;
}
break;
default:return -1;
}
if (mi<=0) break;
}
fread(rs, 1, 4, fp);
if (rs[0]!=0x0 || rs[1]!=0xff || rs[2]!=0x2f || rs[3]!=0x00) {
dialog("トラック終端の異常");
return 0;
}
}
return 0;
}
// 音楽再生の開始
MIDI_PLAYER::play_music_start(){
TIMECAPS tc ;
timeGetDevCaps (&tc, sizeof (TIMECAPS)) ;
m_time_diff = minmax ((int)tc.wPeriodMin, (int)BASE_TIMER_DIFF, (int)tc.wPeriodMax) ;
timeBeginPeriod (m_time_diff) ;
midiOutOpen(&m_hmidi , MIDIMAPPER , 0 , 0 , 0);
midiOutReset(m_hmidi); // 出てる音がないように
m_timer_id = timeSetEvent(max ((int)m_time_diff, 100), m_time_diff, midi_timer_proc, (UINT)this, TIME_ONESHOT) ;
if (m_timer_id == 0) {
timeEndPeriod (m_time_diff) ;
midiOutClose (m_hmidi) ;
return FALSE ;
}
int ti;
for(ti=0;ti<m_truck_chunck_len;ti++){
m_trucks[ti]->m_play_index=m_trucks[ti]->m_last_play_index=0;
}
return 0;
}
// 再生停止
MIDI_PLAYER::play_music_end(){
// stop the timer
if (m_timer_id) timeKillEvent (m_timer_id) ;
timeEndPeriod (m_time_diff);
midiOutReset(m_hmidi);
midiOutClose(m_hmidi);
m_hmidi=NULL;
return 0;
}
// 音楽再生プロシ−ジャ
MIDI_PLAYER::play_music_proc(){
int ti, mi;
MIDI_MSG *pmsg;
for(ti=0;ti<m_truck_chunck_len;ti++){
for(mi=0;;mi++){
pmsg = m_trucks[ti]->m_msgs[m_trucks[ti]->m_play_index];
if (!pmsg) { play_music_end(); return 0; }
if (pmsg->m_delta_time>0 && m_trucks[ti]->m_last_play_index!=m_trucks[ti]->m_play_index) {
m_timer_id = timeSetEvent(max((int)m_time_diff, delta_to_ms(pmsg->m_delta_time)),
m_time_diff, midi_timer_proc, (UINT)this, TIME_ONESHOT) ;
m_trucks[ti]->m_last_play_index=m_trucks[ti]->m_play_index;
break;
}
m_trucks[ti]->m_play_index++; // インデックスを進める
switch(pmsg->m_code){
case 0xf0:
break;
case 0xf7:
switch(pmsg->m_scale){
case 0x51:
m_tempo=0; memcpy(&m_tempo, pmsg->m_ext, 3); mem_reverse(&m_tempo, 4);
break;
case 0x58: /* 拍子 [nn:dd:cc:bb]拍子記号の分子nn、2のdd乗で表される分母(ddが2の場合4、3の場合8)、
メトロノーム1カウントあたりのMIDIクロック数cc、4分音符中の32分音符数bb。*/
m_rhythm[0]=pmsg->m_ext[0]; m_rhythm[1]=(char)pow(2, pmsg->m_ext[1]);
break;
case 0x59: /* 調号 [sf:mi]シャープまたはフラット記号の数を表すsf、メジャー/マイナーを示すmi。
sfはフラットの数を表すときはマイナス数値になる。また、miはメジャーのとき0、マイナのとき1になる。*/
m_clef[0]=pmsg->m_ext[0]; m_clef[1]=pmsg->m_ext[1];
break;
}
break;
default :
midiOutShortMsg(m_hmidi , MIDIMSG(pmsg->m_code , pmsg->m_scale , pmsg->m_vel));
}
}
}
return 0;
}
|