他人の空似自作物置場

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);
}