/*
 *	main.c
 *
 *	dppcmcnv - WAV to PPD コンバータ
 *	Copyright (C) 2002-2011 Naoyuki Sawa
 *
 *	* Sun Jul 21 04:37:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Mon Jul 17 22:43:46 JST 2006 Naoyuki Sawa
 *	- 以下のバグを修正しました。
 *	  - 変換元のwavファイルが大きい場合に、不正なppdファイルを出力することがあった。
 *	    具体的には、変換元のwavファイルが数十秒以上の長さで、出力形式が可変周波数ADPCM形式の場合、
 *	    出力されるppdファイルのPCEWAVEINFO.lenフィールドが、誤ってマイナスになることがありました。
 *	  今回の修正により、このバグは無くなりました。
 *	* Tue Dec 19 18:05:32 JST 2006 Naoyuki Sawa
 *	- 2bitADPCM可変周波数(独自拡張)を追加しました。
 *	  CLiPライブラリで再生します。
 *	* Sun Jun 05 23:48:49 JST 2011 Naoyuki Sawa
 *	- 無音カット機能のオプション(-c[level])を追加しました。
 *	* Sat Jun 11 00:34:52 JST 2011 Naoyuki Sawa
 *	- ノーマライズ機能のオプション(-n)を追加しました。
 */
#include "app.h"

/****************************************************************************
 *
 ****************************************************************************/

/* メッセージ出力 */
#define NULDEV	"NUL"
FILE* msg = stdout;

/* 出力ファイル形式 */
#define TYPE_TEXT	0
#define TYPE_BINARY	1
char* out_type_desc[] = {
	"TEXT",
	"BINARY",
};
int out_type = -1;

/* 出力データ形式 */
#define FMT_PCM		0
#define FMT_ADPCM	1
#define FMT_ADPCM_V	2
#define FMT_ADPCM_X	3	/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)}}*/
char* out_fmt_desc[] = {
	"PCM",
	"ADPCM",
	"ADPCM_V",
	"ADPCM_X",		/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)}}*/
};
int out_fmt = -1;

/* サンプルサイズ(PCM時のみ)
 * -1: 入力ファイルと同じ
 *  8:  8ビット
 * 16: 16ビット
 */
int out_smpl = -1;

/* 変換レート(ADPCM可変周波数時のみ)
 *       -1: 入力ファイルと同じ
 * 1〜16000: 周波数指定
 */
#define RATE_MIN	1
#define RATE_MAX	16000
#define RATE_DEFAULT	RATE_MAX
int out_rate = -1;

/*{{2011/06/11追加: ノーマライズ*/
/* ノーマライズ
 * 0:行わない
 * 1:行う
 */
int out_norm = 0;
/*}}2011/06/11追加: ノーマライズ*/

/*{{2011/06/06追加: 無音カット*/
/* 無音カットレベル
 *    -1: カット無し
 * 0〜99: 先頭と末尾から、指定された音量[%]以下の部分をカットする
 */
int out_cut = -1;
/*}}2011/06/06追加: 無音カット*/

/* 入出力ファイル名 */
char in_path[_MAX_PATH];
char out_path[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];

/* 出力ラベル */
char out_label[32];
char out_prefix[32];

/* テキスト出力時、一行に何バイト出力するか？ */
#define LINEBYTES	32

/****************************************************************************
 *	main
 ****************************************************************************/

int
main(int argc, char* argv[])
{
	int retval, c_fmt;
	WAV *wav1, *wav2;
	FILE* fp;

	/* コマンドラインを解析します。 */
	cmdline(argc, argv);

	/* フォーマットの最大サイズを取得しておきます。 */
	retval = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &c_fmt);
	if(retval != 0) die("acmMetrics()が失敗しました。");

	/* WAVデータを読み込みます。 */
	wav1 = wav_load(in_path);

	/* 一時的なPCM形式に変換します。
	 * ※注意！
	 *   変換元が既にWAVE_FORMAT_PCMの場合、
	 *   WAVE_FORMAT_PCMからWAVE_FORMAT_PCMへの変換（無意味）を指定してacmFormatSuggest()を呼ぶと、
	 *   acmFormatSuggest()の中で落ちるようです。（Win2K+SP2で確認）
	 *   これを防ぐために、変換元がWAVE_FORMAT_PCMでない場合のみ、
	 *   一時的なPCM形式への変換を行うことにします。
	 */
	if(wav1->fmt->wFormatTag != WAVE_FORMAT_PCM) {
		wav2 = wav_new();
		wav2->fmt = (WAVEFORMATEX*)calloc(1, c_fmt);
		if(wav2->fmt == NULL) die("メモリ不足です。");
		wav2->fmt->wFormatTag = WAVE_FORMAT_PCM; /* リニアPCM */
		retval = acmFormatSuggest(NULL, wav1->fmt, wav2->fmt, c_fmt, ACM_FORMATSUGGESTF_WFORMATTAG);
		if(retval != 0) die("acmFormatSuggest()が失敗しました。");
		wav_to_wav(wav1, wav2);
		wav_free(wav1); /* 変換元のWAVを開放             */
		wav1 = wav2;    /* 変換後のWAVを次の変換元に設定 */
	}

	/* 最終的なPCM形式のサンプルサイズ・変換レートを決定します。 */
	switch(out_fmt) {
	case FMT_PCM:
		/* サンプルサイズが指定されていなければ、元のWAVファイルと同じにします。 */
		if(out_smpl == -1) {
			out_smpl = wav1->fmt->wBitsPerSample;
			if(out_smpl != 8 && out_smpl != 16) { /* ありえないとは思うが... */
				/* 8/16bit以外なら、近い方のサンプルサイズに合わせます。 */
				out_smpl = out_smpl < 12 ? 8 : 16;
			}
		}
		/* 変換レートは16000[Hz]固定です。 */
		out_rate = RATE_DEFAULT;
		break;
	case FMT_ADPCM:
		/* サンプルサイズは16ビット固定です。 */
		out_smpl = 16;
		/* 変換レートは16000[Hz]固定です。 */
		out_rate = RATE_DEFAULT;
		break;
	case FMT_ADPCM_V:
	case FMT_ADPCM_X:	/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)}}*/
		/* サンプルサイズは16ビット固定です。 */
		out_smpl = 16;
		/* 変換レートが指定されていなければ、元のWAVファイルと同じにします。 */
		if(out_rate == -1) {
			out_rate = wav1->fmt->nSamplesPerSec;
			if(out_rate < RATE_MIN) out_rate = RATE_MIN; /* 下限切り詰め */
			if(out_rate > RATE_MAX) out_rate = RATE_MAX; /* 上限切り詰め */
		}
		break;
	}
	fprintf(msg, "OUT SMPL : %d\n", out_smpl);
	fprintf(msg, "OUT RATE : %d\n", out_rate);

	/* 最終的なPCM形式に変換します。 */
#define CHANNELS 1
	wav2 = wav_new();
	wav2->fmt = (WAVEFORMATEX*)calloc(1, c_fmt);
	if(wav2->fmt == NULL) die("メモリ不足です。");
	wav2->fmt->wFormatTag = WAVE_FORMAT_PCM;
	wav2->fmt->nChannels = CHANNELS;
	wav2->fmt->nSamplesPerSec = out_rate;
	wav2->fmt->nAvgBytesPerSec = out_rate * (out_smpl / 8);
	wav2->fmt->nBlockAlign = (out_smpl / 8) * CHANNELS;
	wav2->fmt->wBitsPerSample = out_smpl;
	wav_to_wav(wav1, wav2);
	wav_free(wav1); /* 変換元のWAVを開放             */
	wav1 = wav2;    /* 変換後のWAVを次の変換元に設定 */
#undef CHANNELS

	/*{{2011/06/11追加: ノーマライズ*/
	if(out_norm) {
		switch(out_smpl) {
		case 8:
			do_norm8(wav1);
			break;
		case 16:
			do_norm16(wav1);
			break;
		default:
			abort();
		}
	}
	/*}}2011/06/11追加: ノーマライズ*/

	/* ↑ノーマライズしてから↓指定レベル以下を無音カットする。この処理順序、必須です。 */

	/*{{2011/06/06追加: 無音カット*/
	if(out_cut != -1) {
		switch(out_smpl) {
		case 8:
			do_cut8(wav1, out_cut);
			break;
		case 16:
			do_cut16(wav1, out_cut);
			break;
		default:
			abort();
		}
	}
	/*}}2011/06/06追加: 無音カット*/

	/* 出力ファイルを作成します。 */
	switch(out_type) {
	case TYPE_TEXT:
		fp = fopen(out_path, "wt");
		break;
	case TYPE_BINARY:
		fp = fopen(out_path, "wb");
		break;
	default:
		abort();
	}
	if(fp == NULL) die("%sが作成できません。", out_path);

	/* C言語配列の開始。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "const unsigned char %s[] = {\n", out_label);
	}

	/* データを出力します。 */
	switch(out_fmt) {
	case FMT_PCM:
		writepcm(fp, wav1);
		break;
	case FMT_ADPCM:
	case FMT_ADPCM_V:
		writeadpcm(fp, wav1);
		break;
	/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)*/
	case FMT_ADPCM_X:
		writeadpcm2(fp, wav1);
		break;
	/*}}2006/12/19追加: 2bitADPCM可変周波数(独自拡張)*/
	default:
		abort();
	}

	/* C言語配列の終了。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "};\n");
	}

	/* 出力ファイルを閉じます。 */
	fclose(fp);

	/* WAV構造体を開放します。 */
	wav_free(wav1);

	return 0;
}

/****************************************************************************
 *	usage
 ****************************************************************************/

void
usage()
{
	fprintf(stderr, "dppcmcnv - WAV to PPD converter (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2002-2011 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "    dppcmcnv [options] <filename.wav>\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "TYPE OPTIONS:\n");
	fprintf(stderr, "    -t         テキスト\n");
	fprintf(stderr, "  * -b         バイナリ\n");
	fprintf(stderr, "FORMAT OPTIONS:                               --[bit]--  ---[Ｈz]---\n");
	fprintf(stderr, "  * -fp[smpl]  PCM                            16or8        16000固定\n");
	fprintf(stderr, "    -fa        ADPCM                          16->4固定    16000固定\n");
	fprintf(stderr, "    -fv[rate]  ADPCM可変周波数                16->4固定  1-16000\n");
	fprintf(stderr, "    -fx[rate]  2bitADPCM可変周波数(独自拡張)  16->2固定  1-16000\n");	/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)}}*/
	fprintf(stderr, "    smpl・rate省略時は、入力ファイルと同じ値になります。\n");
	fprintf(stderr, "OTHER OPTIONS:\n");
	fprintf(stderr, "    -n         ノーマライズ\n");	/*{{2011/06/11追加: ノーマライズ}}*/
	fprintf(stderr, "    -c[level]  無音カットレベル（0〜99[％]以下をカット。level省略時は0[％]）\n");	/*{{2011/06/05追加: 無音カット}}*/
	fprintf(stderr, "               -c[level]オプションを指定しなければ、無音でもカットしません。\n");	/*{{2011/06/05追加: 無音カット}}*/
	fprintf(stderr, "    -o<fname>  出力ファイル名\n");
	fprintf(stderr, "    -l<label>  ラベル名（テキスト時のみ）\n");
	fprintf(stderr, "    -p<prefix> ラベルプレフィクス（テキスト時のみ）\n");
	fprintf(stderr, "    -q         メッセージ抑制\n");
	fprintf(stderr, "（* 初期値）\n");
	exit(1);
}

/****************************************************************************
 *	die
 ****************************************************************************/

void
die(const char* fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	 fprintf(stderr, "\n### ");
	vfprintf(stderr, fmt, ap);
	 fprintf(stderr, "\n");
	va_end(ap);
	exit(1);
}

/****************************************************************************
 *	cmdline
 ****************************************************************************/

void
cmdline(int argc, char* argv[])
{
	int i;
	char *opt, *p;

	for(i = 1; i < argc; i++) {
		opt = argv[i];
		if(opt[0] != '-') {
			/* 入力ファイル名 */
			if(strlen(in_path) != 0) usage();
			strcpy(in_path, opt);
		} else {
			switch(opt[1]) {
			/* 出力ファイル形式 */
			case 't':
				if(out_type != -1) usage();
				out_type = TYPE_TEXT;
				break;
			case 'b':
				if(out_type != -1) usage();
				out_type = TYPE_BINARY;
				break;
			/* 出力データ形式 */
			case 'f':
				switch(opt[2]) {
				case 'p':
					if(out_fmt != -1) usage();
					out_fmt = FMT_PCM;
					/* 追加パラメータ=サンプルサイズ */
					p = &opt[3];
					if(*p != '\0') {
						if(out_smpl != -1) usage();
						out_smpl = strtol(p, &p, 10);
						if(*p != '\0') usage();
						if(out_smpl != 8 && out_smpl != 16) usage();
					}
					break;
				case 'a':
					if(out_fmt != -1) usage();
					out_fmt = FMT_ADPCM;
					/* 追加パラメータ=なし */
					p = &opt[3];
					if(*p != '\0') usage();
					break;
				case 'v':
					if(out_fmt != -1) usage();
					out_fmt = FMT_ADPCM_V;
					/* 追加パラメータ=周波数 */
					p = &opt[3];
					if(*p != '\0') {
						if(out_rate != -1) usage();
						out_rate = strtol(p, &p, 10);
						if(*p != '\0') usage();
						if(out_rate < RATE_MIN || RATE_MAX < out_rate) usage();
					}
					break;
				/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)*/
				case 'x':
					if(out_fmt != -1) usage();
					out_fmt = FMT_ADPCM_X;
					/* 追加パラメータ=周波数 */
					p = &opt[3];
					if(*p != '\0') {
						if(out_rate != -1) usage();
						out_rate = strtol(p, &p, 10);
						if(*p != '\0') usage();
						if(out_rate < RATE_MIN || RATE_MAX < out_rate) usage();
					}
					break;
				/*}}2006/12/19追加: 2bitADPCM可変周波数(独自拡張)*/
				default:
					usage();
				}
				break;
			/*{{2011/06/11追加: ノーマライズ*/
			/* ノーマライズ */
			case 'n':
				if(out_norm != 0) usage();
				out_norm = 1;
				break;
			/*}}2011/06/11追加: ノーマライズ*/
			/*{{2011/06/06追加: 無音カット*/
			/* 無音カット */
			case 'c':
				if(out_cut != -1) usage();
				/* 追加パラメータ=無音カットレベル */
				p = &opt[2];
				if(*p != '\0') {
					if(out_cut != -1) usage();
					out_cut = strtol(p, &p, 10);
					if(*p != '\0') usage();
					if(out_cut < 0 || 99 < out_cut) usage();
				} else {
					out_cut = 0;	/* 追加パラメータ無しならば、無音カットレベル0%が指定されたものと見なす */
				}
				break;
			/*}}2011/06/06追加: 無音カット*/
			/* 出力ファイル名 */
			case 'o':
				if(strlen(out_path) != 0) usage();
				strcpy(out_path, &opt[2]);
				break;
			/* 出力ラベル */
			case 'l':
				if(strlen(out_label) != 0) usage();
				strcpy(out_label, &opt[2]);
				break;
			case 'p':
				if(strlen(out_prefix) != 0) usage();
				strcpy(out_prefix, &opt[2]);
				break;
			/* メッセージ抑制 */
			case 'q':
				if(msg != stdout) usage();
				msg = fopen(NULDEV, "wt");
				break;
			default:
				usage();
			}
		}
	}

	/* 入力ファイル名は必須です。 */
	if(strlen(in_path) == 0) usage();

	/* 入力ファイル名の拡張子が省略されていたら、「.wav」とします。 */
	_splitpath(in_path, drive, dir, fname, ext);
	if(strlen(ext) == 0) {
		_makepath(in_path, drive, dir, fname, ".wav");
	}

	/* 出力ファイル形式が指定されていなければ、デフォルト値を設定します。 */
	if(out_type == -1) out_type = TYPE_BINARY;

	/* 出力データ形式が指定されていなければ、デフォルト値を設定します。 */
	if(out_fmt == -1) out_fmt = FMT_PCM;

	/* 出力ファイル名が省略されていたら、入力ファイル名から生成します。 */
	if(strlen(out_path) == 0) {
		_splitpath(in_path, drive, dir, fname, ext);
		switch(out_type) {
		case TYPE_TEXT:
			_makepath(out_path, drive, dir, fname, ".c");
			break;
		case TYPE_BINARY:
			_makepath(out_path, drive, dir, fname, ".ppd");
			break;
		default:
			abort();
		}
	}

	/* 出力ラベルが省略されていたら、入力ファイル名から生成します。 */
	if(strlen(out_label) == 0) {
		_splitpath(out_path, drive, dir, fname, ext);
		for(i = 0; fname[i] != '\0'; i++) {
			out_label[i] = toupper(fname[i]);
		}
	}
	if(strlen(out_prefix) != 0) {
		strcat(out_prefix, out_label); /* out_prefixは破壊されます。もう使わないので大丈夫 */
		strcpy(out_label, out_prefix);
	}

	fprintf(msg, "IN  FILE : %s\n", in_path);
	fprintf(msg, "OUT FILE : %s\n", out_path);
	fprintf(msg, "OUT TYPE : %s\n", out_type_desc[out_type]);
	fprintf(msg, "OUT FMT  : %s", out_fmt_desc[out_fmt]);
	if(out_smpl != -1) fprintf(msg, "(%d)", out_smpl);
	if(out_rate != -1) fprintf(msg, "(%d)", out_rate);
	fprintf(msg, "\n");
	/*{{2011/06/11追加: ノーマライズ*/
	fprintf(msg, "NORMALIZE: %s\n", out_norm ? "ON" : "OFF");
	/*}}2011/06/11追加: ノーマライズ*/
	/*{{2011/06/06追加: 無音カット*/
	if(out_cut != -1) {
		fprintf(msg, "CUT LEVEL: %d[%%]\n", out_cut);
	} else {
		fprintf(msg, "CUT LEVEL: NONE\n");
	}
	/*}}2011/06/06追加: 無音カット*/
	fprintf(msg, "OUT LABEL: %s\n", out_label);
	/* サンプルサイズと周波数は入力ファイルを読むまで決まらないので、後で表示します。 */
}

/****************************************************************************
 *	writepcm
 ****************************************************************************/

/* テキスト時は、呼ばれた単位で改行するので、一行ごとに呼び出してください。 */
void
writedata(FILE* fp, void* data, int len)
{
	int i;
	unsigned char* p;

	switch(out_type) {
	case TYPE_TEXT:
		p = (unsigned char*)data;
		for(i = 0; i < len; i++) {
			fprintf(fp, "0x%02x,", *p++);
		}
		fprintf(fp, "\n");
		break;
	case TYPE_BINARY:
		fwrite(data, 1, len, fp);
		break;
	default:
		abort();
	}
}

/****************************************************************************
 *	writepcm
 ****************************************************************************/

void
writepcm(FILE* fp, WAV* wav)
{
	int ofs, len;
	PPDHDR hdr;
	PCEWAVEINFO info;

	/* ヘッダ出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*PPCM*/\n");
	memset(&hdr, 0, sizeof hdr);
	hdr.sig = PPDSIG;
	hdr.len = sizeof(PCEWAVEINFO) + wav->c_data;
	writedata(fp, &hdr, sizeof hdr);

	/* WAVE情報出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*info*/\n");
	memset(&info, 0, sizeof info);
	info.stat = PW_STAT_START;
	switch(out_smpl) {
	case 8:
		info.type = PW_TYPE_8BITPCM;
		info.len = wav->c_data;
		break;
	case 16:
		info.type = PW_TYPE_16BITPCM;
		info.len = wav->c_data / 2;
		break;
	default:
		abort();
	}
	writedata(fp, &info, sizeof info);

	/* データ出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*data*/\n");
	ofs = 0;
	while(ofs < wav->c_data) {
		len = wav->c_data - ofs;
		if(len > LINEBYTES) len = LINEBYTES;
		writedata(fp, &wav->data[ofs], len);
		ofs += len;
	}
}

/****************************************************************************
 *	writeadpcm
 ****************************************************************************/

void
writeadpcm(FILE* fp, WAV* wav)
{
	int c_smpl, ofs, len;
	unsigned char* adp;
	PPDHDR hdr;
	PCEWAVEINFO info;

	/* ADPCMは2サンプル単位で変換するので、サンプル数を2サンプル単位に切り上げます。 */
	c_smpl = wav->c_data / sizeof(short); /* 元のサンプル数 */
	if(c_smpl & 1) { /* サンプル数が奇数なら... */
		c_smpl++; /* サンプル数を偶数単位に切り上げ */
		wav->c_data = sizeof(short) * c_smpl; /* パディング追加 */
		wav->data = (char*)realloc(wav->data, wav->c_data);
		if(wav->data == NULL) die("メモリ不足です。");
		((short*)wav->data)[c_smpl - 1] = 0; /* パディング部分をクリア */
	}

	/* ADPCM変換。 */
	adp = (unsigned char*)malloc(c_smpl / 2);
	if(adp == NULL) die("メモリ不足です。");
	encode(adp, (short*)wav->data, wav->c_data);

	/* ヘッダ出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*PPCM*/\n");
	memset(&hdr, 0, sizeof hdr);
	hdr.sig = PPDSIG;
	hdr.len = sizeof(PCEWAVEINFO) + c_smpl / 2;
	writedata(fp, &hdr, sizeof hdr);

	/* WAVE情報出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*info*/\n");
	memset(&info, 0, sizeof info);
	info.stat = PW_STAT_START;
	switch(out_fmt) {
	case FMT_ADPCM:
		info.type = PW_TYPE_ADPCM;
		info.len = c_smpl;
		break;
	case FMT_ADPCM_V:
		info.type = PW_TYPE_ADPCM_V;
		info.resv = out_rate; /* 可変周波数時のみ設定 */
		/* 超重要！
		 * ADPCM可変周波数時のlen設定は、実サンプル数ではありません！
		 * 16000Hz換算でのサンプル数（＝再生秒数*16000）です！
		 * （C:\usr\PIECE\app\adpcm\hello.c 53行目あたり参照）
		 */
		//info.len = c_smpl * RATE_DEFAULT / out_rate;
		//↓2006/07/17 バグ修正 (オーバーフロー回避)
		info.len = (int)((__int64)c_smpl * RATE_DEFAULT / out_rate);
		break;
	default:
		abort();
	}
	writedata(fp, &info, sizeof info);

	/* データ出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*data*/\n");
	ofs = 0;
	while(ofs < c_smpl / 2) {
		len = c_smpl / 2 - ofs;
		if(len > LINEBYTES) len = LINEBYTES;
		writedata(fp, &adp[ofs], len);
		ofs += len;
	}

	/* ADPCMデータ開放。 */
	free(adp);
}

/*{{2006/12/19追加: 2bitADPCM可変周波数(独自拡張)*/

/****************************************************************************
 *	writeadpcm2
 ****************************************************************************/

void
writeadpcm2(FILE* fp, WAV* wav)
{
	int c_smpl, ofs, len;
	unsigned char* adp;
	PPDHDR hdr;
	PCEWAVEINFO info;

	/* 2bitADPCMは4サンプル単位で変換するので、サンプル数を4サンプル単位に切り上げます。 */
	c_smpl = wav->c_data / sizeof(short); /* 元のサンプル数 */
	while(c_smpl & 3) { /* サンプル数が4の倍数になるまで... */
		c_smpl++;   /* サンプル数を1づつ増加 */
		wav->c_data = sizeof(short) * c_smpl; /* パディング追加 */
		wav->data = (char*)realloc(wav->data, wav->c_data);
		if(wav->data == NULL) die("メモリ不足です。");
		((short*)wav->data)[c_smpl - 1] = 0; /* パディング部分をクリア */
	}

	/* ADPCM変換。 */
	adp = (unsigned char*)malloc(c_smpl / 4);
	if(adp == NULL) die("メモリ不足です。");
	encode2(adp, (short*)wav->data, wav->c_data);

	/* ヘッダ出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*PPCM*/\n");
	memset(&hdr, 0, sizeof hdr);
	hdr.sig = PPDSIG;
	hdr.len = sizeof(PCEWAVEINFO) + c_smpl / 4;
	writedata(fp, &hdr, sizeof hdr);

	/* WAVE情報出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*info*/\n");
	memset(&info, 0, sizeof info);
	info.stat = PW_STAT_START;
	switch(out_fmt) {
	case FMT_ADPCM_X:
		info.type = PW_TYPE_ADPCM_X;
		info.resv = out_rate;
		info.len = (int)((__int64)c_smpl * RATE_DEFAULT / out_rate);
		break;
	default:
		abort();
	}
	writedata(fp, &info, sizeof info);

	/* データ出力。 */
	if(out_type == TYPE_TEXT) fprintf(fp, "/*data*/\n");
	ofs = 0;
	while(ofs < c_smpl / 4) {
		len = c_smpl / 4 - ofs;
		if(len > LINEBYTES) len = LINEBYTES;
		writedata(fp, &adp[ofs], len);
		ofs += len;
	}

	/* ADPCMデータ開放。 */
	free(adp);
}

/*}}2006/12/19追加: 2bitADPCM可変周波数(独自拡張)*/

/*{{2011/06/11追加: ノーマライズ*/

/****************************************************************************
 *	do_norm8
 ****************************************************************************/

void
do_norm8(WAV* wav)
{
	int c_smpl;
	unsigned char* p;
	int i;
	int v;
	int v_max;

	c_smpl = wav->c_data;
	p = (unsigned char*)wav->data;

	/* 最大振幅を検索します。 */
	v_max = 0;
	for(i = 0; i < c_smpl; i++) {
		v = abs(p[i] - 128);
		if(v > v_max) {
			v_max = v;
		}
	}

	/* 完全に無音か、既に最大ならば、ノーマライズ不要です。そうでなければ、ノーマライズを実行します。 */
	if((v_max == 0) || (v_max >= 128)) {
		/** no job **/
	} else {
		for(i = 0; i < c_smpl; i++) {
			v = p[i] - 128;
			v = v * 128 / v_max;
			if(v < -128) v = -128;	/* アルゴリズム上、有り得ないはずだが、念のため */
			if(v >  127) v =  127;	/* アルゴリズム上、有り得ないはずだが、念のため */
			p[i] = v + 128;
		}
	}
}

/****************************************************************************
 *	do_norm16
 ****************************************************************************/

void
do_norm16(WAV* wav)
{
	int c_smpl;
	short* p;
	int i;
	int v;
	int v_max;

	c_smpl = wav->c_data / 2;
	p = (short*)wav->data;

	/* 最大振幅を検索します。 */
	v_max = 0;
	for(i = 0; i < c_smpl; i++) {
		v = abs(p[i]);
		if(v > v_max) {
			v_max = v;
		}
	}

	/* 完全に無音か、既に最大ならば、ノーマライズ不要です。そうでなければ、ノーマライズを実行します。 */
	if((v_max == 0) || (v_max >= 32768)) {
		/** no job **/
	} else {
		for(i = 0; i < c_smpl; i++) {
			v = p[i];
			v = v * 32768 / v_max;
			if(v < -32768) v = -32768;	/* アルゴリズム上、有り得ないはずだが、念のため */
			if(v >  32767) v =  32767;	/* アルゴリズム上、有り得ないはずだが、念のため */
			p[i] = v;
		}
	}
}

/*}}2011/06/11追加: ノーマライズ*/

/*{{2011/06/06追加: 無音カット*/

/****************************************************************************
 *	do_cut8
 ****************************************************************************/

void
do_cut8(WAV* wav, int cut_level)
{
	int c_smpl;
	unsigned char* p1;
	unsigned char* p2;

	/* カットレベルを計算します。
	 * 8bit PCMは、128を中央として0〜255に振れるので、100%振幅は128です。
	 */
	cut_level = (cut_level * 128) / 100;

	/* カットする前のサンプル数、及び、先頭と末尾のサンプルへのポインタを取得します。 */
	c_smpl = wav->c_data;
	p1 = (unsigned char*)wav->data;
	p2 = p1 + c_smpl - 1;

	/* 先頭の無音をカットします。 */
	while(p1 <= p2) {
		if(abs(*p1 - 128) > cut_level) {
			break;
		}
		p1++;
	}

	/* 末尾の無音をカットします。 */
	while(p1 <= p2) {
		if(abs(*p2 - 128) > cut_level) {
			break;
		}
		p2--;
	}

	/* 残ったサンプル数を求めます。 */
	c_smpl = p2 - p1 + 1;

	/* 全部カットしてしまった場合は、無音の1サンプルを残して帰ります。 */
	if(c_smpl == 0) {
		*(unsigned char*)wav->data = 128;	/* 確実に無音とする。元データは、カットレベル以下ではあるが、無音とは限らないから */
		wav->c_data = 1;
		return;	/* ここまで */
	}

	/* 残った部分をデータ先頭へ移動し、サイズを変更します。 */
	wav->c_data = c_smpl;
	memcpy(wav->data, p1, wav->c_data);	/* 前方コピーなので、memmove()でなくても大丈夫 */
}

/****************************************************************************
 *	do_cut16
 ****************************************************************************/

void
do_cut16(WAV* wav, int cut_level)
{
	int c_smpl;
	short* p1;
	short* p2;

	/* カットレベルを計算します。
	 * 16bit PCMは、0を中央として-32768〜+32767に振れるので、100%振幅は32768です。
	 */
	cut_level = (cut_level * 32768) / 100;

	/* カットする前のサンプル数、及び、先頭と末尾のサンプルへのポインタを取得します。 */
	c_smpl = wav->c_data / 2;
	p1 = (short*)wav->data;
	p2 = p1 + c_smpl - 1;

	/* 先頭の無音をカットします。 */
	while(p1 <= p2) {
		if(abs(*p1) > cut_level) {
			break;
		}
		p1++;
	}

	/* 末尾の無音をカットします。 */
	while(p1 <= p2) {
		if(abs(*p2) > cut_level) {
			break;
		}
		p2--;
	}

	/* 残ったサンプル数を求めます。 */
	c_smpl = p2 - p1 + 1;

	/* 全部カットしてしまった場合は、無音の1サンプルを残して帰ります。 */
	if(c_smpl == 0) {
		*(short*)wav->data = 0;	/* 確実に無音とする。元データは、カットレベル以下ではあるが、無音とは限らないから */
		wav->c_data = 2;
		return;	/* ここまで */
	}

	/* 残った部分をデータ先頭へ移動し、サイズを変更します。 */
	wav->c_data = c_smpl * 2;
	memcpy(wav->data, p1, wav->c_data);	/* 前方コピーなので、memmove()でなくても大丈夫 */
}

/*}}2011/06/06追加: 無音カット*/
