/* conv2wav * * Convert 24-bit text output of l3dec to .wav format * * written by ff123 * http://fastforward.iwarp.com/ * * Version History * 1.01 08 Jun 2001 Added switches to include option to use different * samplerate and number of channels besides the * default 44.1 kHz and 2 (stereo) * 1.00 27 Jan 2001 Original * * Uses nessie.h to increase portability between systems * http://www.cosic.esat.kuleuven.ac.be/nessie/call/portableC.html * **********************************************************************/ #include #include #include #include "nessie.h" /* portable definitions */ /* function prototypes */ void init(void); int write_riff_header(u32 data_size); int write_format_header(void); int write_data(u32 data_size); int process_options(int argnum, char *argv[]); void usage_error(void); int set_sample_rate(char *sr_string); int set_num_channels(char *ch_string); int parse_command_line(int argc, char *argv[]); u32 get_wav_size(void); #define VERSION "1.01 (Jun 8, 2001) http://fastforward.iwarp.com/" #define FORMAT 1 #define DEFAULT_SAMPLERATE 44100 #define DEFAULT_CHANNELS 2 #define BITS 24 #define MAXCHAR 255 enum {FALSE, TRUE}; enum {NO_ERROR, ERROR}; enum {NO_INCR, INCR}; /* static variables */ static char *infilename_s = NULL; /* input filename */ static char *outfilename_s = NULL; /* output filename */ static FILE *ifp_s = NULL; /* pointer to input file */ static FILE *ofp_s = NULL; /* pointer to output file */ static u32 nSamplesPerSec_s; static u16 nChannels_s; /**********************************************************************/ /* init static variables */ void init(void) { nSamplesPerSec_s = DEFAULT_SAMPLERATE; nChannels_s = DEFAULT_CHANNELS; return; } /**********************************************************************/ /* write 12-byte riff-chunk header to file. rchunksize is * the size of whole file minus 8 bytes; equivalent to the size * of the data portion plus 36 bytes of riff/format/data header info */ int write_riff_header(u32 data_size) { u8 rckID[4] = { /* riff chunk ID */ 'R','I','F','F' }; u8 formatID[4] = { /* riff format ID */ 'W','A','V','E' }; u32 rchunksize; /* riff chunk size */ u8 buf4bytes[4]; /* buffer to hold 4 byte integer */ rchunksize = data_size + 36; ofp_s = fopen(outfilename_s, "wb"); if (ofp_s == NULL) { fprintf(stderr, "Can't open output file %s\n", outfilename_s); exit(1); } fwrite(&rckID, 1, sizeof(rckID), ofp_s); U32TO8_LITTLE(buf4bytes, rchunksize); fwrite(&buf4bytes, 1, sizeof(buf4bytes), ofp_s); fwrite(&formatID, 1, sizeof(formatID), ofp_s); return 0; } /**********************************************************************/ /* write 24-byte format-chunk header to file. nChunkSize is 16 bytes */ int write_format_header(void) { u8 ckID[4] = { /* format chunk ID */ 'f','m','t',' ' }; u32 nChunkSize = 0x10; /* format chunk size */ u16 wFormatTag = FORMAT; u32 nAvgBytesPerSec; u16 nBlockAlign; u16 nBitsPerSample = BITS; u8 buf4bytes[4]; /* buffer to hold 4 byte integer */ u8 buf2bytes[2]; /* buffer to hold 2 byte integer */ fprintf(stdout, "Sample rate = %d Hz\n", nSamplesPerSec_s); fprintf(stdout, "Number of channels = %d\n", nChannels_s); nAvgBytesPerSec = nSamplesPerSec_s * nChannels_s * (nBitsPerSample / 8); nBlockAlign = nBitsPerSample/8 * nChannels_s; fwrite(&ckID, 1, sizeof(ckID), ofp_s); U32TO8_LITTLE(buf4bytes, nChunkSize); fwrite(&buf4bytes, 1, sizeof(buf4bytes), ofp_s); U16TO8_LITTLE(buf2bytes, wFormatTag); fwrite(&buf2bytes, 1, sizeof(buf2bytes), ofp_s); U16TO8_LITTLE(buf2bytes, nChannels_s); fwrite(&buf2bytes, 1, sizeof(buf2bytes), ofp_s); U32TO8_LITTLE(buf4bytes, nSamplesPerSec_s); fwrite(&buf4bytes, 1, sizeof(buf4bytes), ofp_s); U32TO8_LITTLE(buf4bytes, nAvgBytesPerSec); fwrite(&buf4bytes, 1, sizeof(buf4bytes), ofp_s); U16TO8_LITTLE(buf2bytes, nBlockAlign); fwrite(&buf2bytes, 1, sizeof(buf2bytes), ofp_s); U16TO8_LITTLE(buf2bytes, nBitsPerSample); fwrite(&buf2bytes, 1, sizeof(buf2bytes), ofp_s); return 0; } /**********************************************************************/ /* write data to file. */ int write_data(u32 data_size) { u8 dckID[4] = { /* data chunk ID */ 'd','a','t','a' }; u32 nDataSize = data_size; /* data chunk size */ u32 entry; /* entry in input file */ char line[MAXCHAR]; /* line buffer */ char *p; /* pointer to token */ char *endptr; /* end pointer for strtol */ u8 buf4bytes[4]; /* buffer to hold 4 byte integer */ u8 buf3bytes[3]; /* buffer to hold 3 byte integer */ fwrite(&dckID, 1, sizeof(dckID), ofp_s); U32TO8_LITTLE(&buf4bytes[0], nDataSize); fwrite(&buf4bytes, 4, 1, ofp_s); rewind(ifp_s); while (fgets(line, MAXCHAR, ifp_s) != NULL) { p = strtok(line, " ,\t\r\n"); entry = strtol(p, &endptr, 16); U24TO8_LITTLE(buf3bytes, entry); fwrite(&buf3bytes, 1, sizeof(buf3bytes), ofp_s); while ((p = strtok(NULL, " ,\t\r\n")) != NULL) { entry = strtol(p, &endptr, 16); U24TO8_LITTLE(buf3bytes, entry); fwrite(&buf3bytes, 1, sizeof(buf3bytes), ofp_s); } } return 0; } /**********************************************************************/ /* process command line options * Returns INCR if switch option is followed by another argument; * Otherwise returns NO_INCR */ int process_options(int argnum, char *argv[]) { char option[MAXCHAR]; /* option */ char optionvalue[MAXCHAR]; /* value of option */ sprintf(option, "%s", *(argv + argnum)); if (strcmp(option, "-h") == 0) usage_error(); else { if (strcmp(option, "-s") == 0) { sprintf(optionvalue, "%s", *(argv + argnum + 1)); if (set_sample_rate(optionvalue) == ERROR) { fprintf(stdout, "Invalid sample rate specified\n"); usage_error(); } return INCR; } else { if (strcmp(option, "-c") == 0) { sprintf(optionvalue, "%s", *(argv + argnum + 1)); if (set_num_channels(optionvalue) == ERROR) { fprintf(stdout, "Invalid number of channels specified\n"); usage_error(); } } return INCR; } } return NO_INCR; } /**********************************************************************/ void usage_error(void) { fprintf(stdout, "Usage: conv2wav [options] \n"); fprintf(stdout, " -h usage\n"); fprintf(stdout, " -s samplerate of input file (Hz). Choose from\n" " one of the following: 8000, 11025, 12000,\n" " 16000, 22050, 24000, 32000, 44100, 48000.\n" " Default is 44100.\n" " -c number of channels. Default is 2 (stereo)\n"); exit(1); } /**********************************************************************/ /* set the sample-rate to other than the default 44100 Hz. Error checks * against valid sample rates (Hz): * MPEG-1: 32000, 48000, 44100 * MPEG-2: 16000, 24000, 22050 * MPEG-2.5: 8000, 12000, 11025 * * Sets static variable nSamplesPerSec_s if valid samplerate specified. * Returns ERROR or NO_ERROR */ int set_sample_rate(char *sr_string) { int i; int switch_valid = FALSE; u32 sample_rate; u32 valid_sample_rates[9] = {32000, 48000, 44100, 16000, 24000, 22050, 8000, 12000, 11025}; for (i=0; i<9; i++) { sample_rate = atoi(sr_string); if (sample_rate == valid_sample_rates[i]) { switch_valid = TRUE; break; } } if (switch_valid == FALSE) return ERROR; nSamplesPerSec_s = sample_rate; return NO_ERROR; } /**********************************************************************/ /* set number of channels to other than the default 2 (stereo). Error * checks against valid number of channels: either 1 or 2. * * Sets static variable nChannels_s if valid number of channels is * specified. Returns ERROR or NO_ERROR */ int set_num_channels(char *ch_string) { int i; int switch_valid = FALSE; u32 num_channels; u32 valid_num_channels[2] = {1, 2}; for (i=0; i<2; i++) { num_channels = atoi(ch_string); if (num_channels == valid_num_channels[i]) { switch_valid = TRUE; break; } } if (switch_valid == FALSE) return ERROR; nChannels_s = num_channels; return NO_ERROR; } /**********************************************************************/ /* user must enter both input and output filenames */ int parse_command_line(int argc, char *argv[]) { int i; int increment = FALSE; /* increment argument counter */ if (argc < 2) { fprintf(stdout, "Not enough arguments specified\n"); usage_error(); } for (i=1; i