/* *** wavfile.c: wav ファイル入出力ライブラリ (2015/10/29-11/12 改 YH) *** */ #include "wavfile.h" // 共通ヘッダ int dumpfileinfo = 0; // 1 なら読み込んだファイル情報を出力する int rangeflag = 0; // 1 なら入力時に警告を出力 static FILE *fin, *fout; // 入力/出力ファイルハンドル static int file_size; // ファイルのバイト数(ヘッダ部を除く) static int format_size; // フォーマットチャンクのバイト数 static int data_size; // データチャンクのバイト数 static int nfile, nchunk; static int xrange; // 値域をはみ出したデータの個数 static fpos_t file_pos, format_pos, data_pos; static char dummy[4] = { 0, 0, 0, 0 }; #define MAX_XRANGE 10 // 値域外警告の最大出力数 /* *** WAVFILE 構造体の割り当て *********************************************************** */ WAVFILE * wavalloc() // デフォールト値のみ設定 { WAVFILE *wf = (WAVFILE *)malloc(sizeof(WAVFILE)); if (wf == NULL) { fprintf(stderr, "can't allocate WAVFILE struct\n"); exit(1); } wf->Filename = NULL; wf->FormatID = F_PCM; wf->Channels = MONAURAL; wf->SamplingRate = CD_SAMPLING; // 44100 Hz wf->BytesPerSample = 2; wf->Blocks = 0; wf->Track = wf->TrackL = wf->TrackR = NULL; return(wf); } /* *** wav ファイルへの出力 *************************************************************** */ static void writestr(char *str, int sz) { // str から始まるバイト列を順に出力する(sz は出力バイト数) int i; for (i=0; i>= 8; } nfile += sz; nchunk += sz; } static double w_range(double x) { // |x|>1 かをチェックし、オーバーなら±1 に丸める。 if (fabs(x) > 1.0) { xrange++; if (xrange <= MAX_XRANGE) { fprintf(stderr, "*** |%10.7f| > 1: truncating\n", x); } if (x > 1.0) x = 1.0; else x = -1.0; } return(x); } static void writedbl1(double x) { // データ値(実数)を 1 byte で出力 int i; x = w_range(x); i = (int)((double)AMP1 * x); writenum(i + 128, 1); } static void writedbl2(double x) { // データ値(実数)を 2 byte で出力 int i; x = w_range(x); i = (int)((double)AMP2 * x); writenum(i, 2); } static int w_check(WAVFILE *wf) { // wf 構造体の内容が正しいかのチェック。エラーの場合には非ゼロ値を返す。 if (wf->Filename == NULL) { fprintf(stderr, "*** No file name\n"); return(1); } if (wf->FormatID != F_PCM) { fprintf(stderr, "*** FormatID = %d (1: PCM file only)\n", wf->FormatID); return(2); } if ((wf->Channels != 1) && (wf->Channels != 2)) { fprintf(stderr, "*** Number of channels : %d (must be 1 or 2)\n", wf->Channels); return(3); } if (wf->SamplingRate <= 0) { fprintf(stderr, "*** Sampling rate: %d (must be non-negative)\n", wf->SamplingRate); return(4); } if ((wf->BytesPerSample != 1) && (wf->BytesPerSample != 2)) { fprintf(stderr, "*** Bytes per sample : %d (must be 1 or 2)\n", wf->BytesPerSample); return(5); } if (wf->Blocks <= 0) { fprintf(stderr, "*** No data\n"); return(6); } if ((wf->Channels == 1) && (wf->Track == NULL)) { fprintf(stderr, "*** No data\n"); return(7); } if ((wf->Channels == 2) && ((wf->TrackL == NULL) || (wf->TrackR == NULL))) { fprintf(stderr, "*** No data\n"); return(7); } return(0); } static void w_preproc(WAVFILE *wf) { // WAV ファイル書き込みのための前処理 // 出力ファイルを開き、ヘッダ部分、format chunk を書き出す。 //  注:ファイル・チャンクのバイト数はこの時点で計算できるが、 //    出力バイト数を数えてあとから書き込む(fgetpos/fsetpos 参照) if ((fout = fopen(wf->Filename, "wb")) == NULL) { fprintf(stderr, "*** \"%s\": can't open\n", wf->Filename); exit(-1); } // ファイルのヘッダ writestr("RIFF", 4); fgetpos(fout, &file_pos); writestr(dummy, 4); nfile = 0; nchunk = 0; writestr("WAVE", 4); // フォーマットチャンク writestr("fmt ", 4); fgetpos(fout, &format_pos); writestr(dummy, 4); nchunk = 0; writenum(wf->FormatID, 2); writenum(wf->Channels, 2); writenum(wf->SamplingRate, 4); writenum(wf->BytesPerSample * wf->Channels * wf->SamplingRate, 4); writenum(wf->BytesPerSample * wf->Channels, 2); writenum(wf->BytesPerSample * 8, 2); // バイト数でなくビット数なので format_size = nchunk; // データチャンクのヘッダ writestr("data", 4); fgetpos(fout, &data_pos); writestr(dummy, 4); nchunk = 0; } static void w_postproc() { // WAV ファイル書き込みのための後処理 // 保留していたファイル・チャンク長を書き込み、ファイルを閉じる。 file_size = nfile; data_size = nchunk; fsetpos(fout, &file_pos); writenum(file_size, 4); fsetpos(fout, &format_pos); writenum(format_size, 4); fsetpos(fout, &data_pos); writenum(data_size, 4); fclose(fout); } WAVFILE * wavwrite(WAVFILE *wf) { // WAV ファイル出力のメイン関数 //  チャネル数、データ当たりバイト数に応じて出力形式を変える。 int i; if (i = w_check(wf)) exit(i); w_preproc(wf); xrange = 0; if ((wf->Channels == 1) && (wf->BytesPerSample == 1)) { // 1 channel 1 byte for (i=0; iBlocks; i++) writedbl1(wf->Track[i]); } else if ((wf->Channels == 1) && (wf->BytesPerSample == 2)) { // 1 channel 2 bytes for (i=0; iBlocks; i++) writedbl2(wf->Track[i]); } else if ((wf->Channels == 2) && (wf->BytesPerSample == 1)) { // 2 channel 1 byte for (i=0; iBlocks; i++) { writedbl1(wf->TrackL[i]); writedbl1(wf->TrackR[i]); } } else if ((wf->Channels == 2) && (wf->BytesPerSample == 2)) { // 2 channel 2 bytes for (i=0; iBlocks; i++) { writedbl2(wf->TrackL[i]); writedbl2(wf->TrackR[i]); } } if (xrange > 0) { // 絶対値が 1 を超えたデータ数報告 fprintf(stderr, "*** truncated data: %d\n", xrange); } w_postproc(); return(wf); } WAVFILE * wavwrite_monaural(char *Filename, int SamplingRate, int Blocks, double *Track) { // モノラルデータの出力。バイト数は 2 に固定 WAVFILE *wf = wavalloc(); wf->Filename = (char *)calloc(strlen(Filename)+1, 1); strcpy(wf->Filename, Filename); wf->SamplingRate = SamplingRate; wf->Channels = MONAURAL; wf->Blocks = Blocks; wf->Track = wf->TrackL = Track; return(wavwrite(wf)); } WAVFILE * wavwrite_stereo(char *Filename, int SamplingRate, int Blocks, double *TrackL, double *TrackR) { // ステレオデータの出力。バイト数は 2 に固定 WAVFILE *wf = wavalloc(); wf->Filename = (char *)calloc(strlen(Filename)+1, 1); strcpy(wf->Filename, Filename); wf->SamplingRate = SamplingRate; wf->Channels = STEREO; wf->Blocks = Blocks; wf->Track = wf->TrackL = TrackL; wf->TrackR = TrackR; return(wavwrite(wf)); } /* *** wav ファイルの入力 *************************************************************** */ static int readbyte() { int c = fgetc(fin); if (c == EOF) { fprintf(stderr, "*** premature end of file\n"); exit(1); } return(c); } static int readstr(char *str, int checkflag) { // 入力が与えられた文字列かをチェック。 // checkflag = 1: 不一致ならエラー終了 // checkflag = 0: 不一致なら結果だけ返す(入力は保存) // checkflag =-1: すでに読み込んだ文字列と照合 // int i; static char buf[10]; if (checkflag >= 0) { for (i=0; str[i]; i++) buf[i] = readbyte(); buf[i] = 0; } if (strcmp(buf, str) == 0) return(1); if (checkflag == 1) { fprintf(stderr, "*** \"%s\" expected\n", str); exit(1); } return(0); } static int readnum(int sz) { // sz バイトのデータを整数値として読み込む。(バイト逆順に注意) int byte[4], i, n = 0; for (i=0; i=0; i--) n = (n<<8) | byte[i]; return(n); } static double readdata1() { int d = readnum(1) - 128; // 単に readbyte でも同じ double x; if (d == XAMP1) { xrange++; if (rangeflag && (xrange <= MAX_XRANGE)) { fprintf(stderr, "*** data = %d, truncating to -1\n", d, x); } d = -AMP1; } x = (double)d/(double)AMP1; return(x); } static double readdata2() { short d = (short)readnum(2); double x; if (d == XAMP2) { xrange++; if (rangeflag && (xrange <= MAX_XRANGE)) { fprintf(stderr, "*** data = %d, truncating to -1\n", d, x); } d = -AMP2; } x = (double)d/(double)AMP2; return(x); } static void r_preproc(WAVFILE *wf) { // WAV ファイル読み込みの前処理 //  データチャンクの先頭までを読み、チェックを行う。 // int BytesPerSec, BlockSize, BitsPerSample; int i; if ((fin = fopen(wf->Filename, "rb")) == NULL) { fprintf(stderr, "*** \"%s\": can't open\n", wf->Filename); } readstr("RIFF", 1); file_size = readnum(4); readstr("WAVE", 1); readstr("fmt ", 1); format_size = readnum(4); wf->FormatID = readnum(2); wf->Channels = readnum(2); wf->SamplingRate = readnum(4); BytesPerSec = readnum(4); BlockSize = readnum(2); BitsPerSample = readnum(2); wf->BytesPerSample = BitsPerSample / 8; AGAIN: i = readstr("data", 0); data_size = readnum(4); if (i == 0) { // fact, LIST チャンク:読み飛ばす if (readstr("fact", -1) || readstr("LIST", -1)) { for (i=0; i < data_size; i++) readbyte(); goto AGAIN; } else { fprintf(stderr, "*** unknown chunk\n"); exit(1); } } // data チャンクは1個のみを想定。 // 以下チェック、値設定 if (wf->FormatID != F_PCM) { fprintf(stderr, "*** Format-ID = %d (only PCM(=1))\n", wf->FormatID); exit(1); } if ((wf->Channels != 1) && (wf->Channels != 2)) { fprintf(stderr, "*** Channels = %d (only 1 or 2)\n", wf->Channels); exit(1); } if ((BitsPerSample != 8) && (BitsPerSample != 16)) { fprintf(stderr, "*** BitsPerSample = %d (only 8 or 16)\n", BitsPerSample); exit(1); } i = wf->BytesPerSample * wf->Channels; if (i != BlockSize) { fprintf(stderr, "*** Warning: Incorrect Blocksize: %d (should be %d)\n", BlockSize, i); } i = i * wf->SamplingRate; if (i != BytesPerSec) { fprintf(stderr, "*** Warning: Incorrect BytesPerSec: %d (should be %d)\n", BytesPerSec, i); } // データサイズとデータバッファの割当て wf->Blocks = data_size / BlockSize; wf->Track = wf->TrackL = (double *)calloc(wf->Blocks, sizeof(double)); if (wf->Track == NULL) { fprintf(stderr, "*** can't allocate %d blocks\n", wf->Blocks); exit(1); } if (wf->Channels == 2) { wf->TrackR = (double *)calloc(wf->Blocks, sizeof(double)); if (wf->Track == NULL) { fprintf(stderr, "*** can't allocate %d blocks\n", wf->Blocks); exit(1); } } // ファイル情報のダンプ(stdout へ) if (dumpfileinfo == 0) return; printf("*** WAV file: \"%s\" ***\n", wf->Filename); printf("\tFormat ID : %d\n", wf->FormatID); printf("\tChannels : %d\n", wf->Channels); printf("\tSamplingRate : %d\n", wf->SamplingRate); printf("\tBytes/Sample : %d\n", wf->SamplingRate); printf("\t# of Blocks : %d\n", wf->Blocks); printf("\n"); // 必要なら追加する } WAVFILE * wavread(char *Filename) { // WAV ファイルの読み込み。 //  ファイル情報やデータを格納した WAVFILE 構造体ポインタを返す。 // int i; WAVFILE *wf = wavalloc(); wf->Filename = (char *)calloc(strlen(Filename)+1, 1); strcpy(wf->Filename, Filename); r_preproc(wf); // データの読み込み xrange = 0; if ((wf->Channels == 1) && (wf->BytesPerSample == 1)) { // 1 channel 1 byte for (i=0; iBlocks; i++) wf->Track[i] = readdata1(); } else if ((wf->Channels == 1) && (wf->BytesPerSample == 2)) { // 1 channel 2 bytes for (i=0; iBlocks; i++) wf->Track[i] = readdata2(); } else if ((wf->Channels == 2) && (wf->BytesPerSample == 1)) { // 2 channel 1 byte for (i=0; iBlocks; i++) { wf->TrackL[i] = readdata1(); wf->TrackR[i] = readdata1(); } } else if ((wf->Channels == 2) && (wf->BytesPerSample == 2)) { // 2 channel 2 bytes for (i=0; iBlocks; i++) { wf->TrackL[i] = readdata2(); wf->TrackR[i] = readdata2(); } } // ここでまだファイルが続いているか(データチャンクが複数あるか等々) // のチェックが本来は必要だが、ここでは省略。 if (rangeflag && (xrange > 0)) { // 絶対値が 1 を超えたデータ数報告 fprintf(stderr, "*** truncated data: %d\n", xrange); } fclose(fin); return(wf); }