• R/O
  • HTTP
  • SSH
  • HTTPS

Tags
Aucun tag

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

oga's tools


File Info

Révision d6f35a911265152c0ebe479a6552316ce6024300
Taille 11,958 octets
l'heure 2024-12-28 17:25:55
Auteur hyperoga
Message de Log

change access permission

Content

/*
 *  midic: midi compiler
 *
 *   11/10/01 V0.10 by oga.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <windows.h>
#endif

/* script format */
/*
-------------------------
# comment
SmfFormat=1                             (省略可)
# 480: 1/4
# 240: 1/8
# 120: 1/16
DeltaTime=480                           (省略可)

[Track1]
TrackName=Track1                        (省略可: "")
# 00ff03lldddddd...
Copyright=Copyright (C)2011 oga. Labo.  (省略可: なし)
# 00ff02lldddddd...
Tempo=120                               (省略可: 120)
# 00ff5103tttttt
Beat=4/4                                (省略可: 4/4)  {4/4|3/4|6/8|2/2}
# 00ff5804 0402 1808  

[Track2]
TrackName=Track2
ChannelPrefix=0
# 00ff2101nn
<Delta>:9xnnvv  904364   (Note On ) G5
<Delta>:8xnn00  804300   (Note Off)
MML=t120o4l4cdef#gabc+d+e+f#+
-------------------------
 */

/* globals */
int  vf = 0;           /* verbose */

/* for header */
int   HeadLen    = 6;
short SmfFormat  = 1;
short NumOfTrack = 0;
short DeltaTime  = 480; /* 1/4 time */

/* for track */
int  Tempo         = 120; /* MM=120 : 500000us */
int  ChannelPrefix = 0;   /* Channel Prefix    */
int  Beat1         = 4;   /* Beat1/Beat2       */
int  Beat2         = 4;   /* Beat1/Beat2       */

#define MAX_TRACK   16
#define ALLOC_SIZE  (500*1024)   /* 500KB/track */

char *TrackData[MAX_TRACK];
int  TrackLen[MAX_TRACK];

/* for Midi Header */
#define ID_HEAD        "MThd"

/* for Midi Track  */
#define ID_TRACK       "MTrk"

#define KEY_SMFFMT     "SmfFormat="
#define KEY_DELTA      "DeltaTime="

#define SECT_TRACK     "[Track"
#define KEY_TRACKN     "TrackName="
#define KEY_COPYR      "Copyright="
#define KEY_TEMPO      "Tempo="
#define KEY_BEAT       "Beat="
#define KEY_CHPREFIX   "ChannelPrefix="

/* for Midi Command */
#define CMD_EndOfTrack "ff2f00"

/*
 *  output syntax error & msg
 *
 *   IN : buf  : error script line data
 *        msg  : error message
 */
void SyntaxErr(char *buf, char *msg)
{
	printf("Syntax Error: [%s]\n", buf);
	if (msg != NULL && strlen(msg)) {
		printf("%s\n", msg);
	}
}

/*
 *  read script file
 *
 *   IN : fp   : script file pointer
 */
int ReadMidiScript(FILE *fp)
{
	char buf[2048];
	char msg[256];
	char Copyright[4096];
	char TrackName[4096];
	char CmdStr[4096];
	char *cmd;
	int  CurTrack  = -1;    /* current track number  */
	int  PrevTrack = -1;    /* previous track number */

	while (fgets(buf, sizeof(buf), fp)) {
		/* delete CR/LF */
		if (buf[strlen(buf)-1] == 0x0a) {
			buf[strlen(buf)-1] = '\0';
		}
		if (buf[strlen(buf)-1] == 0x0d) {
			buf[strlen(buf)-1] = '\0';
		}
		if (vf > 1) printf("DEBUG: ReadMidiScript2: buf=[%s]\n", buf);
		if (strlen(buf) == 0) continue;   /* blank line */
		if (buf[0] == '#')    continue;   /* comment    */

		/* Common Info */
		if (!strncmp(buf, KEY_SMFFMT, strlen(KEY_SMFFMT))) {
			SmfFormat = atoi(&buf[strlen(KEY_SMFFMT)]);
			if (vf) printf("SmfFormat=%d\n", SmfFormat);
			continue;
		}
		if (!strncmp(buf, KEY_DELTA, strlen(KEY_DELTA))) {
			DeltaTime = atoi(&buf[strlen(KEY_DELTA)]);
			if (vf) printf("DeltaTime=%d\n", DeltaTime);
			continue;
		}

		/* Track */
		if (!strncmp(buf, SECT_TRACK, strlen(SECT_TRACK))) {
			PrevTrack = CurTrack;
			CurTrack = atoi(&buf[strlen(SECT_TRACK)]) - 1;
			if (CurTrack < 0 || CurTrack >= MAX_TRACK) {
				sprintf(msg, "The track number must be %d or less.", MAX_TRACK);
				SyntaxErr(buf, msg);
				return -1;
			}

			if (TrackData[CurTrack]) {
				SyntaxErr(buf, "Duplicate Track");
				return -1;
			}
			TrackData[CurTrack] = (char *)malloc(ALLOC_SIZE);
			memset(TrackData[CurTrack], 0, ALLOC_SIZE);
			++NumOfTrack;
			if (PrevTrack >= 0) {
				// add EOT
				AddMidiCommand(TrackData[PrevTrack], &TrackLen[PrevTrack], 0, CMD_EndOfTrack);
			}
			if (vf) printf("## Track : %d\n", CurTrack);
			continue;
		}

		if (CurTrack >= 0) {
			/* Track Name (str) */
			if (!strncmp(buf, KEY_TRACKN, strlen(KEY_TRACKN))) {
				strcpy(TrackName, &buf[strlen(KEY_TRACKN)]);
				if (vf) printf("TrackName=[%s]\n", TrackName);
				if (strlen(TrackName) > 255) {
					SyntaxErr(buf, "Track name too long.");
					TrackName[256] = '\0';
				}
				AddMidiCmdStr(TrackData[CurTrack], &TrackLen[CurTrack], "ff03", TrackName);
				continue;
			}

			/* Copyright (str) */
			if (!strncmp(buf, KEY_COPYR, strlen(KEY_COPYR))) {
				strcpy(Copyright, &buf[strlen(KEY_COPYR)]);
				if (vf) printf("Copyright=[%s]\n", Copyright);
				if (strlen(Copyright) > 255) {
					SyntaxErr(buf, "Copyright too long.");
					Copyright[256] = '\0';
				}
				AddMidiCmdStr(TrackData[CurTrack], &TrackLen[CurTrack], "ff02", Copyright);
				continue;
			}

			/* Tempo */
			if (!strncmp(buf, KEY_TEMPO, strlen(KEY_TEMPO))) {
				Tempo = atoi(&buf[strlen(KEY_TEMPO)]);
				if (vf) printf("Tempo=%d\n", Tempo);
				sprintf(CmdStr, "ff5103%06x", 60000000/Tempo);  /* ff5103<ms> */
				AddMidiCommand(TrackData[CurTrack], &TrackLen[CurTrack], 0, CmdStr);
				continue;
			}

			/* Channel Prefix */
			if (!strncmp(buf, KEY_CHPREFIX, strlen(KEY_CHPREFIX))) {
				ChannelPrefix = atoi(&buf[strlen(KEY_CHPREFIX)]);
				if (vf) printf("ChannelPrefix=%d\n", ChannelPrefix);
				sprintf(CmdStr, "ff2101%02x", ChannelPrefix);  /* ff2101<chprefix> */
				AddMidiCommand(TrackData[CurTrack], &TrackLen[CurTrack], 0, CmdStr);
				continue;
			}

			/* Beat */
			if (!strncmp(buf, KEY_BEAT, strlen(KEY_BEAT))) {
				char *pt = strchr(&buf[strlen(KEY_BEAT)], '/');
				if (pt == NULL) {
					sprintf(msg, "ex.)%s4/4\n", KEY_BEAT);
					SyntaxErr(buf, msg);
				} else {
					int k, Beat2val = 0;

					Beat1 = atoi(&buf[strlen(KEY_BEAT)]);
					++pt;  /* skip '/' */
					Beat2 = atoi(pt);
					if (vf) printf("Beat=%d/%d\n", Beat1, Beat2);
					for (k = 0; k < 30; k++) {
						if ((Beat2 >> k) & 1) {
							Beat2val = k;
							break;
						}
					}
					sprintf(CmdStr, "ff5804%02x%02x1808", Beat1, Beat2val); /*ff5804xxyy1808*/
					AddMidiCommand(TrackData[CurTrack], &TrackLen[CurTrack], 0, CmdStr);
				}
				continue;
			}

			/* midi command          */
			/* <delta>:9xnnvv        */
			if (cmd = strchr(buf, ':')) {
				char *delta_pt = &buf[0];
				int  delta;

				while(isspace(delta_pt[0])) ++delta_pt;
				if (isdigit(delta_pt[0])) {
					++cmd;   /* skip ":" */
					delta = atoi(delta_pt);
					AddMidiCommand(TrackData[CurTrack], &TrackLen[CurTrack], delta, cmd);
				} else {
					SyntaxErr(buf, "no delta len");
				}
				continue;
			}
		}
		if (CurTrack < 0) {
			SyntaxErr(buf, "No Track Section.");
		} else {
			SyntaxErr(buf, "unknown command");
		}
	}
	if (CurTrack >= 0) {
		AddMidiCommand(TrackData[CurTrack], &TrackLen[CurTrack], 0, CMD_EndOfTrack);
	}
	return 0;
}

/*
 *  AddDelta()
 *
 *  IN    : delta    : delta value
 *  IN/OUT: trackdat : track data
 *          len      : tracklen
 *
 *  DeltaTime bytes: MSB is continuation flag
 *                   7bit time data
 */
int AddDelta(unsigned char *trackdat, int *len, int delta)
{
	char work_delta[4];
	int  i;
	int  cnt = 0;

	/* delta to bytes */
	if (delta == 0) {
		work_delta[0] = delta;
		++cnt;
	} else {
		while (delta) {
			work_delta[cnt] = delta & 0x7f;
			if (vf > 1) printf("AddDelta: work_delta[%d] = 0x%02x\n", cnt, work_delta[cnt]);
			delta = delta >> 7;
			++cnt;
		}
	}

	for (i = 0; i<cnt; i++) {
		trackdat[*len + i] = work_delta[(cnt-1)-i];
		if (i < cnt-1) {
			trackdat[*len + i] |= 0x80;  /* set continuation flag */
		}
		if (vf > 1) printf("AddDelta: trackdat[%d] = 0x%02x\n", *len+i, trackdat[*len+i]);
	}
	*len += cnt;

	return 0;
}

/*
 *  AddMidiCommand()
 *
 *  IN    : delta    : delta time
 *        : cmd      : midi command (hexadecimal ascii string)
 *  IN/OUT: trackdat : track data
 *          len      : tracklen
 *
 */
int AddMidiCommand(unsigned char *trackdat, int *len, int delta, char *cmd)
{
	char work[128];

	if (vf > 1) printf("AddMidiCommand: cmd=%s [%c] %d\n", cmd, *cmd, !isspace(*cmd));
	/* add delta */
	AddDelta(trackdat, len, delta);

	/* add midi command */
	strcpy(work, "0x");
	while ((*cmd != '\0') && (!isspace(*cmd)) && (*cmd != '#')) {
		strncpy(&work[2], cmd, 2);
		work[4] = '\0';
		trackdat[*len] = (unsigned char)strtoul(work,(char **)NULL,0);
		if (vf > 1) printf("AddMidiCommand: %s\n", work);
		++(*len);
		++cmd;
		++cmd;
	}
	if (vf > 1) printf("AddMidiCommand: len = %d\n", *len);

	return 0;
}

/*
 *  AddMidiCmdStr()  
 *     add MIDI command & string
 *
 *  IN    : delta    : always zero
 *        : cmd      : midi command (hexadecimal ascii string)
 *        : str      : string 
 *  IN/OUT: trackdat : track data
 *          len      : tracklen
 *
 */
int AddMidiCmdStr(unsigned char *trackdat, int *len, char *cmd, char *str)
{
	char CmdStr[4096];

	if (strlen(str) > 255) {
		return -1;
	}

	/* add command */
	sprintf(CmdStr, "%s%02x", cmd, strlen(str));
	AddMidiCommand(trackdat, len, 0, CmdStr);

	/* add string */
	strcpy(&trackdat[*len], str);
	*len += strlen(str);

	return 0;
}

/*
 *  write midi data
 *
 *   IN : fp   : output file pointer
 */
int WriteMidi(FILE *fp)
{
	int   i;
	int   worki;
	short works;
	int   pos  = 0;
	int   size = 0;

	/* header */
	fwrite(ID_HEAD, 1, strlen(ID_HEAD), fp);

	/* convert to big endian and write*/
	worki = htonl(HeadLen);
	fwrite(&worki, 1, sizeof(worki), fp);
	works = htons(SmfFormat);
	fwrite(&works, 1, sizeof(works), fp);
	works = htons(NumOfTrack);
	fwrite(&works, 1, sizeof(works), fp);
	works = htons(DeltaTime);
	fwrite(&works, 1, sizeof(works), fp);

	/* track */
	for (i = 0; i < MAX_TRACK; i++) {
		if (TrackData[i]) {
			fwrite(ID_TRACK, 1, strlen(ID_TRACK), fp);
			worki = htonl(TrackLen[i]);
			fwrite(&worki, 1, sizeof(worki), fp);
			if (vf) printf("Write Track%d len=%d\n", i+1, TrackLen[i]);
			pos = 0;
			while (pos < TrackLen[i]) {
				size = TrackLen[i] - pos;
				if (size > 4096) size = 4096;
				size = fwrite(&TrackData[i][pos], 1, size, fp);
				if (size < 0) {
					perror("fwrite");
					return -1;
				}
				pos += size;
			}
		}
	}
	return 0;
}

/*
 *   Disp Script Sample
 */
void DispScriptSample()
{
	printf("#SmfFormat=1\n");
	printf("#DeltaTime=480      # 480 = 1/4\n");

	printf("[Track1]\n");
	printf("#TrackName=Track1\n");
	printf("#Copyright=Copyright (C)2011 xxx.\n");
	printf("#Beat=4/4  # {4/4 | 3/4 | 6/8 | 2/2}\n");
	printf("#Tempo=120\n");

	printf("#[Track2]\n");
	printf("#TrackName=Track2\n");
	printf("#ChannelPrefix=0\n");
	printf("<DeltaLen>:9xnnvv  #904364   (Note On ) G5\n");
	printf("<DeltaLen>:9xnn00  #904300   (Note Off)\n");
	printf("<DeltaLen>:8xnnvv  #804300   (Note Off)\n");
	printf("<DeltaLen>:Cxnn    #c072     Program Change (SteelDrum)\n");
	printf("# DeltaLen ... 1920:1/1  960:1/2  480:1/4  240:1/8  120:1/16\n");
}

void usage()
{
	printf("usage: midic <script> [-o <output.mid>]\n");
	printf("usage: midic [-sample]\n");
	printf("       -sample: output sample script\n");
	exit(1);
}

int main(int a, char *b[])
{
	int  i;
	char *filename = NULL;
	char ofname[2048];
	FILE *ifp;
	FILE *ofp;


	strcpy(ofname, "");
	for (i = 1; i<a; i++) {
		if (!strcmp(b[i],"-h")) {
			usage();
		}
		if (!strcmp(b[i],"-v")) {
			++vf;
			continue;
		}
		if (!strcmp(b[i],"-sample")) {
			DispScriptSample();
			exit(0);
		}
		if (i+1 < a && !strcmp(b[i],"-o")) {
			strcpy(ofname, b[++i]);
			continue;
		}
   		filename = b[i];
	}

	if (filename) {
		if (!(ifp = fopen(filename, "r"))) {
			perror(filename);
			exit(1);
		}
	} else {
		ifp = stdin;
	}

	if (filename == NULL && strlen(ofname) == 0) {
		usage();
	}
	if (strlen(ofname) == 0) {
		strcpy(ofname, filename);
		strcat(ofname, ".mid");
	}
	printf("output to %s\n", ofname);

	memset(TrackData, 0, sizeof(TrackData));
	memset(TrackLen,  0, sizeof(TrackLen));

	// read script
	ReadMidiScript(ifp);
	fclose(ifp);

	// ouput midi
	if (NumOfTrack == 0) {
		printf("Error: no track.\n");
		exit(1);
	}
	if (!(ofp = fopen(ofname, "w"))) {
		perror(ofname);
		exit(1);
	}
	WriteMidi(ofp);
	fclose(ofp);

	return 0;
}


/* vim:ts=4:sw=4:
 */