/***************************************************************/ /* WAV ファイル連結プログラム */ /* オプション */ /* -l : length --- wavファイル1の後に wavファイル2を連結する */ /* -c : channel --- wavファイル1を左チャンネル, */ /* wavファイル2 を右チャンネルとして連結する */ /* kondo@kk.iij4u.or.jp */ /* 2000.12.15 */ /***************************************************************/ #include #include #include #ifndef _MAX_PATH #define _MAX_PATH (255) #endif static char cmdname[] = "combine"; void usage(void) { fprintf(stderr, "wav ファイル連結プログラム...\n" "Usage: %s [option] \n" "[option]:\n" " -l, length の後に を連結して\n" " に出力します\n" " -c, channel を左チャンネル" " を右チャンネルとして\n" " に出力します\n" "\n" " wav_file1 と wav_file2 は, 形式が一致している必要があります\n" " (PCM, channels, samples/sec, bits/sample)\n" "\n", cmdname); exit(EXIT_FAILURE); } void openError(char *filename) { fprintf(stderr, "cannot open file : %s\n", filename); exit(EXIT_FAILURE); } void readError(FILE *fp) { fprintf(stderr, "read error : %ld\n", ftell(fp)); fclose(fp); exit(EXIT_FAILURE); } void writeError(FILE *fp) { fprintf(stderr, "write error : %ld\n", ftell(fp)); fclose(fp); exit(EXIT_FAILURE); } int optind = 1; // オプションインデックス int optopt; // オプション文字 char *optarg; // オプション引数 int getopt(int argc, char *argv[], char *optstring) { static char *place = "", *lastoptstring = (char *) 0; register char *optlist; // オプション文字リスト if (optstring != lastoptstring) { lastoptstring = optstring; place = ""; } if (!*place) { if ((optind >= argc) || (*(place = argv[optind]) != '-') || !*++place) { place = ""; return EOF; } // found "--" if (*place == '-') { ++optind; return EOF; } } // オプション文字チェック if ((optopt = (int) *place++) == (int) ':' || !(optlist = strchr(optstring, optopt))) { if (!*place) { ++optind; } fprintf(stderr, "%s : illegal option -- %c\n", *argv, optopt); return (int) '?'; } // オプション引数不要の場合 if (*++optlist != ':') { optarg = NULL; if (!*place) { ++optind; } } // オプション引数必要の場合 else { if (*place) { optarg = place; } // 引数無し else if (argc <= ++optind) { place = ""; fprintf(stderr, "%s : option requires an argument -- %c\n", *argv, optopt); return (int) '?'; } else { optarg = argv[optind]; } place = ""; ++optind; } // オプション文字 return optopt; } /*----------------*/ /* ファイル名取得 */ /*----------------*/ int getBasename(char *dest, char *src) { int i, start, end, ret; i = -1; start = 0; end = 0; // ファイル名のはじめと終わりを検出 while (src[++i]) { if (src[i] == '\\' || src[i] == ':') { start = i + 1; end = 0; } if (src[i] == '.') { end = i; } } if (end == 0) { end = i; } // ファイル名が有る場合 if (start < end) { for (i = 0; i < end; i++) { dest[i] = src[i]; } dest[i] = '\0'; ret = 1; } else { dest[0] = '\0'; ret = 0; } return ret; } /*-----------------------------*/ /* 4Byte 読み込み 10進数に変換 */ /*-----------------------------*/ unsigned long get_ulong(FILE *fp) { unsigned char s[4]; if (fread(s, 4, 1, fp) != 1) { readError(fp); } return (s[0] + 256L * (s[1] + 256L * (s[2] + 256L * s[3]))); } /*-----------------------------*/ /* 2Byte 読み込み 10進数に変換 */ /*-----------------------------*/ unsigned short get_ushort(FILE *fp) { unsigned char s[2]; if (fread(s, 2, 1, fp) != 1) { readError(fp); } return (s[0] + 256U * s[1]); } /*------------------------*/ /* fmt チャンクのチェック */ /*------------------------*/ void fmt_chunkRead(char *wavefile, FILE *fp, unsigned short *channels, unsigned long *sr, unsigned short *bits) { short tag; // 2Byte データ形式 PCM ならば 01 00 tag = get_ushort(fp); fprintf(stderr, " format tag = %u (1 = PCM)\n", tag); // 2Byte チャンネル数 モノラルならば 01 00 ステレオならば 02 00 *channels = get_ushort(fp); fprintf(stderr, " channels = %u\n", *channels); // 4Byte サンプリング周波数(Hz) *sr = get_ulong(fp); fprintf(stderr, " samples/sec = %lu\n", *sr); // 4Byte 1秒あたりのバイト数 fprintf(stderr, " avg. bytes/sec = %lu\n", get_ulong(fp)); // 2Byte バイト数×チャンネル数 fprintf(stderr, " block align = %u\n", get_ushort(fp)); // 2Byte 1サンプルあたりのバイト数 *bits = get_ushort(fp); fprintf(stderr, " bits/sample = %u\n", *bits); if (tag != 1) { fprintf(stderr, "\nThis program supported \"PCM format\" wav file\n"); fprintf(stderr, "%s : format tag = %d\n", wavefile, tag); exit(EXIT_FAILURE); } if (*channels != 2 && *channels != 1) { fprintf(stderr, "\nThis program supported monoral/stereo wav file\n"); fprintf(stderr, "%s : channels = %d\n", wavefile, *channels); exit(EXIT_FAILURE); } if (*bits != 8 && *bits != 16) { fprintf(stderr, "\nThis program supported 16bit or 8bit wav file\n"); fprintf(stderr, "%s : bits/sample = %d\n", wavefile, *bits); exit(EXIT_FAILURE); } } /*----------------------*/ /* ファイル情報読み出し */ /*----------------------*/ void wavRead(char *wavefile, unsigned short *ch, unsigned long *sr, unsigned short *bits, long *data_from, long *data_size) { long cursor, len; unsigned char s[5]; FILE *fp; if ((fp = fopen(wavefile, "rb")) == NULL) { openError(wavefile); } fprintf(stderr, "\n%s :\n", wavefile); // RIFF ヘッダ情報 // 4Byte 'R''I''F''F' if (fread(s, 4, 1, fp) != 1) { readError(fp); } if (memcmp(s, "RIFF", 4) != 0) { fprintf(stderr, "Not a 'RIFF' format\n"); fclose(fp); exit(EXIT_FAILURE); } // 4Byte これ以降のバイト数 = (ファイルサイズ - 8)(Byte) len = get_ulong(fp); fprintf(stderr, "[RIFF] (%lu bytes)\n", len); // WAVE ヘッダ情報 // 4Byte 'W''A''V''E' if (fread(s, 4, 1, fp) != 1) { readError(fp); } if (memcmp(s, "WAVE", 4) != 0) { fprintf(stderr, "Not a 'WAVE' format\n"); fclose(fp); exit(EXIT_FAILURE); } fprintf(stderr, "[WAVE]\n"); // チャンク情報 while (fread(s, 4, 1, fp) == 1) { if (memcmp(s, "fmt ", 4) == 0) { len = get_ulong(fp); fprintf(stderr, " (%ld bytes)\n", len); cursor = ftell(fp); fmt_chunkRead(wavefile, fp, ch, sr, bits); fseek(fp, cursor + len, SEEK_SET); } else if (memcmp(s, "data", 4) == 0) { *data_size = get_ulong(fp); fprintf(stderr, " (%ld bytes)\n", *data_size); *data_from = ftell(fp); fseek(fp, *data_size + *data_from, SEEK_SET); } else { len = get_ulong(fp); s[4] = '\0'; fprintf(stderr, " <%s> (%ld bytes)\n", s, len); cursor = ftell(fp); fseek(fp, cursor + len, SEEK_SET); } } fclose(fp); } /*---------------------*/ /* wav ヘッダ 書き込み */ /*---------------------*/ long wavHeaderWrite(FILE *fp, long data_size, unsigned short ch, unsigned long sr, unsigned short bits) { unsigned long var_long; unsigned short var_short, bytes; char s[4]; // RIFF ヘッダ s[0] = 'R'; s[1] = 'I'; s[2] = 'F'; s[3] = 'F'; fwrite(s, 1, 4, fp); // ファイルサイズ var_long = data_size + 36; fwrite(&var_long, sizeof(long), 1, fp); // WAVE ヘッダ s[0] = 'W'; s[1] = 'A'; s[2] = 'V'; s[3] = 'E'; fwrite(s, 1, 4, fp); // chunkID (fmt チャンク) s[0] = 'f'; s[1] = 'm'; s[2] = 't'; s[3] = ' '; fwrite(s, 1, 4, fp); // chunkSize (fmt チャンクのバイト数 無圧縮 wav は 16) var_long = 16; fwrite(&var_long, sizeof(long), 1, fp); // wFromatTag (無圧縮 PCM = 1) var_short = 1; fwrite(&var_short, sizeof(short), 1, fp); // dwChannels (モノラル = 1, ステレオ = 2) fwrite(&ch, 2, 1, fp); // dwSamplesPerSec (サンプリングレート(Hz)) fwrite(&sr, sizeof(long), 1, fp); // wdAvgBytesPerSec (Byte/秒) bytes = bits / 8; var_long = bytes * ch * sr; fwrite(&var_long, sizeof(long), 1, fp); // wBlockAlign (Byte/サンプル*チャンネル) var_short = bytes * ch; fwrite(&var_short, sizeof(short), 1, fp); // wBitsPerSample (bit/サンプル) var_short = bits; fwrite(&var_short, sizeof(short), 1, fp); // chunkID (data チャンク) s[0] = 'd'; s[1] = 'a'; s[2] = 't'; s[3] = 'a'; fwrite(s, 1, 4, fp); // chunkSize (データ長 Byte) fwrite(&data_size, 4, 1, fp); return ftell(fp); } /*--------------------*/ /* 1Byte(8bit) コピー */ /*--------------------*/ void bytecopy(FILE *fp1, FILE *fp2) { char data; if (fread(&data, sizeof(char), 1, fp1) != 1) { readError(fp1); } if (fwrite(&data, sizeof(char), 1, fp2) != 1) { writeError(fp2); } } /*------------------*/ /* wav ファイル連結 */ /*------------------*/ void wavWriteCascade(char *wave1, long from1, long size1, char *wave2, long from2, long size2, char *result, unsigned long sr, unsigned short ch, unsigned short bits) { long i, data_size; FILE *fp1, *fp2, *fp3; if ((fp3 = fopen(result, "wb")) == NULL) { openError(result); } data_size = size1 + size2; // wav ヘッダ書き込み if (wavHeaderWrite(fp3, data_size, ch, sr, bits) != 44) { fprintf(stderr, "%s : wav header write error\n", result); exit(EXIT_FAILURE); } if ((fp1 = fopen(wave1, "rb")) == NULL) { openError(wave1); } if ((fp2 = fopen(wave2, "rb")) == NULL) { openError(wave2); } fseek(fp1, from1, SEEK_SET); fseek(fp2, from2, SEEK_SET); // wav データ書き込み for (i = 0; i < size1; i++) { bytecopy(fp1, fp3); } for (i = 0; i < size2; i++) { bytecopy(fp2, fp3); } } /*--------------------------------------*/ /* 8 bit データ 0〜255 (無音 128) */ /* 16 bit データ -32768〜32767 (無音 0) */ /*--------------------------------------*/ void wavDataWrite(FILE *wave1, long from1, long length1, FILE *wave2, long from2, long length2, FILE *result, short bytes) { long i; short j; char data; // 元ファイルのデータ開始部分 fseek(wave1, from1, SEEK_SET); fseek(wave2, from2, SEEK_SET); // ファイル1の方が大きい場合 if (length1 > length2) { for (i = 0; i < length2 / 2; i++) { for (j = 0; j < bytes; j++) { bytecopy(wave1, result); } for (j = 0; j < bytes; j++) { bytecopy(wave2, result); } } for (i = 0; i < (length1 - length2) / 2; i++) { for (j = 0; j < bytes; j++) { bytecopy(wave1, result); } for (j = 0; j < bytes; j++) { if (bytes == 1) { data = 128; } else { data = 0; } if (fwrite(&data, sizeof(char), 1, result) != 1) { writeError(result); } } } } // ファイル2の方が大きい場合 else { for (i = 0; i < length1 / 2; i++) { for (j = 0; j < bytes; j++) { bytecopy(wave1, result); } for (j = 0; j < bytes; j++) { bytecopy(wave2, result); } } for (i = 0; i < (length2 - length1) / 2; i++) { for (j = 0; j < bytes; j++) { if (bytes == 1) { data = 128; } else { data = 0; } if ((fwrite(&data, sizeof(char), 1, result)) != 1) { writeError(result); } } for (j = 0; j < bytes; j++) { bytecopy(wave2, result); } } } } /*----------------------------*/ /* wav ファイルチャンネル連結 */ /*----------------------------*/ void wavWriteChannel(char *wave1, long from1, long size1, char *wave2, long from2, long size2, char *result, unsigned long sr, unsigned short ch, unsigned short bits) { unsigned long data_size; FILE *fp1, *fp2, *fp3; if ((fp3 = fopen(result, "wb")) == NULL) { openError(result); } // ファイルサイズ if (size1 < size2) { data_size = size2; } else { data_size = size1; } // チャンネル数 if (ch != 1) { fprintf(stderr, "channel setting error\n"); exit(EXIT_FAILURE); } ch *= 2; data_size *= ch; // wav ヘッダ書き込み if (wavHeaderWrite(fp3, data_size, ch, sr, bits) != 44) { fprintf(stderr, "%s : wav header write error\n", result); exit(EXIT_FAILURE); } if ((fp1 = fopen(wave1, "rb")) == NULL) { openError(wave1); } if ((fp2 = fopen(wave2, "rb")) == NULL) { openError(wave2); } fseek(fp1, from1, SEEK_SET); fseek(fp2, from2, SEEK_SET); wavDataWrite(fp1, from1, size1, fp2, from2, size2, fp3, (short) (bits / 8)); } /**************/ /* メイン関数 */ /**************/ int main(int argc, char *argv[]) { int c, check = 0; unsigned long sampling1, sampling2; unsigned short ch1, ch2, bits1, bits2; long from1, from2, size1, size2; char wavfile1[_MAX_PATH], wavfile2[_MAX_PATH], basename[_MAX_PATH], resultfile[_MAX_PATH]; // オプションチェック while ((c = getopt(argc, argv, "cl")) != EOF) { switch (c) { case 'l': check = 1; break; case 'c': check = 2; break; case '?': usage(); break; default: usage(); break; } } if ((argc - optind) < 3) { usage(); } strcpy(wavfile1, argv[optind]); strcpy(wavfile2, argv[optind + 1]); getBasename(basename, argv[optind + 2]); sprintf(resultfile, "%s.wav", basename); // パラメータ読み込み wavRead(wavfile1, &ch1, &sampling1, &bits1, &from1, &size1); wavRead(wavfile2, &ch2, &sampling2, &bits2, &from2, &size2); // パラメータチェック // チャンネル数 if (ch1 != ch2) { fprintf(stderr, "invalid channels : %s - %d, %s - %d\n", wavfile1, ch1, wavfile2, ch2); exit(EXIT_FAILURE); } // サンプリング周波数 if (sampling1 != sampling2) { fprintf(stderr, "invalid samples/sec : %s - %ld, %s - %ld\n", wavfile1, sampling1, wavfile2, sampling2); exit(EXIT_FAILURE); } // 量子化ビット数 if (bits1 != bits2) { fprintf(stderr, "invalid bits/sample : %s - %d, %s - %d\n", wavfile1, bits1, wavfile2, bits2); exit(EXIT_FAILURE); } if (check == 1) { wavWriteCascade(wavfile1, from1, size1, wavfile2, from2, size2, resultfile, sampling1, ch1, bits1); } else if (check == 2) { wavWriteChannel(wavfile1, from1, size1, wavfile2, from2, size2, resultfile, sampling1, ch1, bits1); } else { usage(); } fprintf(stderr, "\n%s + %s -> %s\n", wavfile1, wavfile2, resultfile); return EXIT_SUCCESS; }