libmid.zip/libmid.c
#include "libmid.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#pragma comment (lib, "winmm.lib")
void midi_head(FILE *fp){
char s[22];
strcpy(s,"MThd");
*(int *)&s[4] = 0x06000000;
*(int *)&s[8] = 0x01000000;
*(short *)&s[12] = 0xE001;
strcpy(&s[14],"MTrk");
*(int *)&s[18] = 0x00000000;
fwrite(s,1,22,fp);
}
void rev(void *a,int len){ //バイト反転(バイトオーダー問題)
char t,*s,*b;
s = (char *)a;
b = s+len-1;
while(s < b){
t = *s;
*s = *b;
*b = t;
s++;
b--;
}
}
MIDI *midi_open(char *fn){
MIDI *midi;
midi = (MIDI *)malloc(sizeof(MIDI));
if(midi == NULL)return NULL;
midi->fp = fopen(fn,"wb");
if(midi->fp==NULL){
printf("ファイルオープンに失敗しました\n");
exit(1);
}
midi->time = 0;
midi->type = 1;
midi_head(midi->fp);
midi->data = NULL;
midi->tempo = 500000;
midi->delta = 480;
return midi;
}
int midi_close(MIDI* midi){
int count;
char s[3];
DATA *data;
if(midi->type == 1){
s[0] = 0xFF;
s[1] = 0x2F;
s[2] = 0x00;
midievent(midi,s,3);
count = ftell(midi->fp)-22;
fseek(midi->fp,18,SEEK_SET);
rev(&count,4);
fwrite(&count,4,1,midi->fp);
}
if(0 != fclose(midi->fp))return 1;
free(midi);
return 0;
}
int write_delta(unsigned char *s,int time){
int i = 1;
if(time>>21 > 0){
*s = 0x80 | (time>>21);
s++;
i = 4;
}
if(time>>14 > 0){
*s = 0x80 | ((time>>14)&0x7F);
s++;
i = 3;
}
if(time>>7 > 0){
*s = 0x80 | ((time>>7)&0x7F);
s++;
i = 2;
}
*s = time&0x7F;
return i;
}
int midievent(MIDI *midi,char *data,int num){
unsigned char s[10];
int count;
DATA *a;
if(num > 6 || num < 0)return 1;
count = write_delta(s,midi->time);
memcpy(&s[count],data,num);
if(count+num != fwrite(s,1,count+num,midi->fp)){
return 2;
}
a = (DATA *)malloc(sizeof(DATA));
if(midi->data == NULL){
midi->data = a;
} else {
midi->tail->next = a;
}
midi->tail = a;
a->next = NULL;
a->time = midi->time;
a->stat = data[0];
a->param1 = data[1];
if(num > 2){
a->param2 = data[2];
if(num > 3){
a->param3 = (unsigned char *)malloc(num-3);
memcpy(a->param3,&data[3],num-3);
}
}
midi->time = 0;
return 0;
}
int event_b3(MIDI *midi,unsigned char n,unsigned char a,unsigned char b,unsigned char c){
unsigned char s[3];
if(n&0x0F != 0)return 1;
if(a > 15)return 2;
if(b > 127)return 4;
if(c > 127)return 5;
s[0] = n | a;
s[1] = b;
s[2] = c;
if(0 != midievent(midi,(char *)s,3)){
return 3;
}
return 0;
}
int event_b2(MIDI *midi,unsigned char n,unsigned char a,unsigned char b){
unsigned char s[2];
if(n&0x0F != 0)return 1;
if(a > 15)return 2;
if(b > 127)return 4;
s[0] = n | a;
s[1] = b;
if(0 != midievent(midi,(char *)s,2)){
return 3;
}
return 0;
}
void next(MIDI *midi,int time){
midi->time += time;
}
int set_tempo(MIDI *midi,int tempo){
unsigned char s[6];
rev(&tempo,4);
*(int *)&s[2] = tempo; //3byteしか使わないがエンディアンの関係で面倒なので4byteで出して後で上書き
s[0] = 0xFF;
s[1] = 0x51;
s[2] = 0x03;
if(0 != midievent(midi,(char *)s,6)){
return 1;
}
return 0;
}
int change_tempo(MIDI *midi,int tempo){
midi->tempo = tempo;
tempo = tempo/midi->delta*480;//数字が大きすぎて場合によりオーバーフローするので割り算が先
return set_tempo(midi,tempo);
}
int change_delta(MIDI *midi,int delta){
int tempo;
midi->delta = delta;
tempo = midi->tempo*480/delta;
return set_tempo(midi,tempo);
}
void MidiPlay(MIDI *midi,double start,int dev){
MIDIHDR mh;
HMIDIOUT hmo;
DATA *a;
int tempo,delta,msg,len,b;
char *str;
double time,t;
unsigned int t1,t2;
timeBeginPeriod(1);
printf("再生を開始します\n");
if(MMSYSERR_NOERROR != midiOutOpen(&hmo, dev, 0, 0, CALLBACK_NULL)){
printf("デバイスのオープンに失敗しました\n");
return;
}
memset(&mh,0,sizeof(MIDIHDR));
t1 = timeGetTime();
// while(1){
midiOutReset(hmo);
tempo = 500000;
delta = 480;
a = midi->data;
time = 0.0;
while(a != NULL){
t = a->time*tempo/1000.0/delta;
t2 = timeGetTime()-t1;
t1 += t;
if(t2>0 && t2<1000)t -= t2;
if(time >= start){
if(t>2)Sleep(t-2);
while(t1 > timeGetTime()){}
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%d:%02d:%02d",(int)floor(time/60/60),(int)floor(time/60)%60,(int)floor(time)%60);
}
time += t/1000;
if (a->stat == 0xFF) { //メタイベント
if(a->param1 == 0x2F){ //エンドオブトラック
break;
} else if(a->param1 == 0x51){ // セットテンポ
tempo = a->param3[2] + (a->param3[1] << 8) + (a->param3[0] << 16);
}
} else if (a->stat >= 0xF0) { //SysExイベント
switch(a->stat){
case 0xF0:
len = a->param1+1;
str = (char *)malloc(len);
str[0] = a->stat;
memcpy(&str[1],a->param3,a->param1);
break;
case 0xF7:
len = a->param1;
str = (char *)malloc(len);
memcpy(str,a->param3,a->param1);
break;
default:
printf("未対応のSysExイベントに遭遇しました\n");
return;
}
mh.lpData = str;
mh.dwFlags = 0;
mh.dwBufferLength = len;
mh.dwBytesRecorded = len;
if(MMSYSERR_NOERROR != midiOutPrepareHeader(hmo, &mh, sizeof(MIDIHDR))){
printf("\n排他バッファを取得できませんでした。\nSysExイベントを実行できません。\n");
return;
} else {
midiOutLongMsg(hmo,&mh,sizeof(MIDIHDR));
while ((mh.dwFlags & MHDR_DONE) == 0);
while(MMSYSERR_NOERROR != (b = midiOutUnprepareHeader(hmo,&mh,sizeof(MIDIHDR))) ){
if(b != MIDIERR_STILLPLAYING){
printf("SysEx命令実行中にエラーが発生しました。\n");
return;
}
}
}
} else { //MIDIイベント
b = a->stat&0xF0; //Bx Cx Dx系のイベントは通しておかないと楽器情報がおかしくなる
if(b==0xB0 || b==0xC0 || b==0xE0 ||time >= start){
msg = a->stat + (a->param1 << 8) | (a->param2 << 16);
midiOutShortMsg(hmo, msg);
}
}
a = a->next;
}
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%d:%02d:%02d",(int)floor(time/60/60),(int)floor(time/60)%60,(int)floor(time)%60);
// }
midiOutReset(hmo); //念のため
midiOutClose(hmo);
printf("\n再生を終了します\n");
timeEndPeriod(1);
}