開発環境 Microsoft Visual C++ 2013
実行環境 Microsoft Windows 8.1 (64bit)
プロジェクトの種類 Win32 コンソール アプリケーション
プロジェクト名 code2smf
アプリケーションの種類 コンソール アプリケーション
追加のオプション 空のプロジェクト, SDLチェック

code2smf.cpp
// マルチバイト文字セット
 
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <map>
 
using namespace std;
 
#define AC(a) a, _countof(a)
 
struct Data {
	BYTE cmd;
	BYTE d1;
	BYTE d2;
};
typedef multimap<DWORD, Data> DataList;
 
// 関数プロトタイプ宣言
int GetNoteLen(const char* p);
int NoteName(const char* p, int tick_a, int tick_n);
int GetValue(const char* p, int* value);
void Output(LPCTSTR path_in);
void WriteBE(int len, DWORD val);
void WriteData(DWORD t, BYTE cmd, BYTE d1, BYTE d2);
 
// 外部変数
int timebase = 480;
int q = 7;		// 音の長さの割合 q/8
char flat[16];
FILE* pfo;
int datalen;		// データ長
DataList list;
 
//==============================================================================
int main(int argc, char* argv[])
{
	if (argc != 2) {
		fprintf(stderr, "usage: code2smf codefile\n");
		return 1;
	}
	char path_in[_MAX_PATH];
	strcpy_s(path_in, argv[1]);
 
	// ヘッダ
	GetPrivateProfileString("head", "b", NULL, AC(flat), path_in);
 
	// 入力ファイル
	FILE* pfi;
	if (fopen_s(&pfi, path_in, "rt") != 0) {
		fprintf(stderr, "error: %s\n", path_in);
		return 1;
	}
 
	// 読み込みループ
	char line[256];
	BOOL track = FALSE;
	int row = 0;
	while (fgets(AC(line), pfi) != NULL) {
		row++;
		int len = strlen(line);
		if (0 < len && line[len - 1] == '\n') line[len - 1] = '\0';	// 改行文字を削除
		char* p = strchr(line, ';');
		if (p != NULL) *p = '\0';	// コメントアウトを削除
		if (line[0] == '\0') continue;
		if (track == FALSE) {
			if (_stricmp(line, "[track1]") == 0) track = TRUE;
			continue;
		}
 
		int bar, numer, denom;	// 小節、分子、分母
		char notelen[16];	// 音長
		char notename[16];	// 音名
		int field = sscanf_s(line, "%d %d/%d %s %s", &bar, &numer, &denom, AC(notelen), AC(notename));
		if (field != 5) {
			fprintf(stderr, "error: %s\n", line);
			break;
		}
 
		// 音長
		int tick_n = GetNoteLen(notelen);
		if (tick_n <= 0) {
			fprintf(stderr, "error: %s\n", line);
			break;
		}
 
		// 音名処理
		int tick_a = (4 * timebase) * (bar - 1) + (4 * timebase) * numer / denom;
		if (NoteName(notename, tick_a, tick_n) != 0) {
			fprintf(stderr, "error: %s\n", line);
			break;
		}
 
		printf("[%s] %d %d %d/%d t=%d\n", line, field, bar, numer, denom, tick_n);
	}
	fclose(pfi);
 
	Output(path_in);
	return 0;
}
 
// 音長の取得
int GetNoteLen(const char* p)
{
	int tick = 0;
	for (int c = 0; p[c]; ) {
		// 音長
		int value;
		c += GetValue(p + c, &value);
		if (value == 0) return 0;
		// 付点
		int dot = 0;
		for ( ; p[c] == '.'; c++) dot++;
 
		int t = timebase * 4 / value;
		t += t * ((1 << dot) - 1) / (1 << dot);
		tick += t;
		// タイ
		if (p[c] == '&') c++;
	}
	return tick;
}
 
// 音名処理
int NoteName(const char* p, int tick_a, int tick_n)
{
	const int scale[] = { 9, 11, 0, 2, 4, 5, 7 };	// 音階
	if (p[0] == 'r') return 0;	// 休符
	int ch = 0;
	for (int c = 0; p[c]; ch++) {
		// 音名
		char name = p[c++];
		int n = name - 'a';
		if (n < 0 || 7 < n) {
			return -1;
		}
		// オクターブ
		int octave;
		c += GetValue(p + c, &octave);
		if (octave < 1 || 7 < octave) {
			return -1;
		}
		// 臨時記号 accidental
		int acci = 0;
		switch (p[c]) {
		case '+': case '#':
			acci++; c++; break;
		case '-':
			acci--; c++; break;
		}
		if (acci == 0) {
			if (strchr(flat, name) != NULL) {
				acci = -1;
			}
		}
		int notenum = (octave + 1) * 12 + scale[n] + acci;
 
		Data data;
		data.cmd = 0x90 | ch;	// note on
		data.d1 = notenum;
		data.d2 = 0x70;
		list.insert(make_pair(tick_a, data));
		data.cmd = 0x80 | ch;	// note off
		data.d1 = notenum;
		data.d2 = 0x00;
		list.insert(make_pair(tick_a + tick_n * q / 8, data));
	}
	return 0;
}
 
// 値の取得
int GetValue(const char* p, int* value)
{
	int c = 0;
	*value = 0;
	for ( ; isdigit(p[c]); c++) {
		*value = *value * 10 + (p[c] - '0');
	}
	return c;
}
 
//------------------------------------------------------------------------------
// SMFファイルへの出力
void Output(LPCTSTR path_in)
{
 
	// 出力ファイル
	char drv[_MAX_DRIVE];
	char dir[_MAX_DIR];
	char fnm[_MAX_FNAME];
	char ext[_MAX_EXT];
	char path_out[_MAX_PATH];
	_splitpath_s(path_in, drv, dir, fnm, ext);
	_makepath_s(path_out, drv, dir, fnm, "mid");
	if (fopen_s(&pfo, path_out, "wb") != 0) {
		fprintf(stderr, "error: %s\n", path_out);
		return;
	}
 
	// ヘッダチャンク
	fwrite("MThd", 1, 4, pfo);
	WriteBE(4, 6);		// データ長
	WriteBE(2, 0);		// フォーマットタイプ
	WriteBE(2, 1);		// トラック数
	WriteBE(2, timebase);	// タイムベース
 
	// トラックチャンク
	fwrite("MTrk", 1, 4, pfo);
	WriteBE(4, 0);	// データ長(仮)
	datalen = 0;
 
	DWORD tick = 0;
	for (DataList::iterator it = list.begin(); it != list.end(); it++) {
		DWORD delta = it->first - tick;
		Data data = it->second;
		WriteData(delta, data.cmd, data.d1, data.d2);
		tick = it->first;
//		printf("%u %x %u %x\n", it->first, data.cmd, data.d1, data.d2);
	}
 
	// トラック終了
	WriteData(0, 0xff, 0x2f, 0x00);
	fseek(pfo, -4 - datalen, SEEK_CUR);
	WriteBE(4, datalen);	// データ長
	fclose(pfo);
}
 
void WriteBE(int len, DWORD val)
{
	BYTE* p = (BYTE*)&val + len;
	for (int n = 0; n < len; n++) {
		fwrite(--p, 1, 1, pfo);
	}
	datalen += len;
}
 
void WriteData(DWORD t, BYTE cmd, BYTE d1, BYTE d2)
{
	// 可変長tick
	DWORD v = ((t & 0xfe00000) << 3) | ((t & 0x1fc000) << 2) | ((t & 0x3f80) << 1) | (t & 0x7f) | 0x80808000;
	BYTE* p = (BYTE*)&v + 4;
	for (int n = 0; n < 4; n++) {
		if (*--p != 0x80) {
			fwrite(p, 1, 1, pfo);
			datalen++;
		}
	}
 
	fwrite(&cmd, 1, 1, pfo);
	fwrite(&d1, 1, 1, pfo);
	fwrite(&d2, 1, 1, pfo);
	datalen += 3;
}
 

ivi.txt
[head]
 
[track1]
; 小節 分子/分母 音長 音名
1 0/8 2 c4e4g4c5
1 4/8 2 g3d4g4b4
2 0/8 2 c4e4g4c5
2 4/8 2 r
 
最終更新:2014年03月16日 22:03