| /* |
| * wav_chan_splitter.c |
| * |
| * Utility for splitting a multichannel wav file to multiple mono wav files. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include "wav_chan_splitter.h" |
| |
| #define MAX_FILENAME_LEN 256 |
| #define MAX_CHANNELS 8 |
| #define MAX_BYTES 4 // Up to 32-bit samples |
| |
| /* Reuse tinyplay/tinycap code for header parsing */ |
| static int parse_header (char * filename, wav_header_t * wav_header) { |
| FILE* file; |
| file = fopen(filename, "rb"); |
| if (!file) { |
| fprintf(stderr, "Unable to open file '%s'\n", filename); |
| return 1; |
| } |
| fread(wav_header, sizeof(wav_header_t), 1, file); |
| if ((wav_header->riff_id != ID_RIFF) || |
| (wav_header->riff_fmt != ID_WAVE) || |
| (wav_header->data_id != ID_DATA)) { |
| fprintf(stderr, "Error: '%s' is not a PCM Wav file\n", filename); |
| fclose(file); |
| return 1; |
| } |
| fclose(file); |
| return 0; |
| } |
| |
| /* Create mono header based on multichannel header */ |
| static int create_new_header (wav_header_t * wav_header_out, wav_header_t * wav_header) { |
| if (!wav_header_out || !wav_header) { |
| fprintf(stderr, "Invalid argument for %s\n", __func__); |
| return 1; |
| } |
| memcpy(wav_header_out, wav_header, sizeof(wav_header_t)); |
| wav_header_out->num_channels = 1; |
| wav_header_out->byte_rate = (wav_header->byte_rate)/(wav_header->num_channels); |
| wav_header_out->block_align = (wav_header->block_align)/(wav_header->num_channels); |
| wav_header_out->data_sz = (wav_header->data_sz)/(wav_header->num_channels); |
| /* Tinycap doesn't set riff_sz field, fill it up for the split files */ |
| /* Definition: size of the rest of header (36 bytes) + datachunk size */ |
| wav_header_out->riff_sz = wav_header_out->data_sz + 36; |
| return 0; |
| } |
| |
| static int split (char *filename, const wav_header_t * wav_header, const wav_header_t * wav_header_out) { |
| char outname[MAX_FILENAME_LEN]; |
| uint8_t i = 0; |
| char *extension_pos; |
| FILE *fp; /* input file pointer */ |
| FILE *fplist[MAX_CHANNELS]; /* output file pointers */ |
| uint8_t data[MAX_CHANNELS*MAX_BYTES]; |
| size_t num_bytes_per_sample; |
| |
| fp = fopen(filename, "rb"); |
| /* Jump to data block */ |
| fseek(fp, sizeof(wav_header_t), SEEK_SET); |
| |
| strncpy(outname, filename, sizeof(outname)); |
| /* Get extension index of filename */ |
| extension_pos = strrchr(outname, '.'); |
| /* Point to '\0' instead if no extension is found */ |
| if (!extension_pos) |
| extension_pos = strrchr(outname, '\0'); |
| if (!extension_pos) { |
| fprintf(stderr, "Filename is too long!\n"); |
| return 1; |
| } |
| |
| printf("splitting...\n"); |
| |
| /* Create files <filename_[01 - N].wav> and copy headers */ |
| for (i = 0; i < wav_header->num_channels; i++) { |
| snprintf(extension_pos, sizeof(outname) - (extension_pos - outname), "_%02d.wav", i + 1); |
| fplist[i] = fopen(outname, "wb"); |
| fwrite(wav_header_out, sizeof(wav_header_t), 1, fplist[i]); |
| } |
| |
| num_bytes_per_sample = wav_header_out->bits_per_sample >> 3; |
| |
| /* Split data block */ |
| while(fread(data, wav_header->block_align, 1, fp) == 1) { |
| for (i = 0; i < wav_header->num_channels; i++) |
| fwrite(&data[i*num_bytes_per_sample], num_bytes_per_sample, 1, fplist[i]); |
| } |
| |
| for (i = 0; i < wav_header->num_channels; i++) { |
| fclose(fplist[i]); |
| } |
| fclose(fp); |
| return 0; |
| } |
| |
| int split_main (int argc, char* argv[]) { |
| char *filename; |
| wav_header_t wav_header, wav_header_out; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: audio-tool split file.wav\n"); |
| return 1; |
| } |
| filename = argv[1]; |
| |
| if (parse_header(filename, &wav_header)) |
| return 1; |
| |
| printf("filename: %s\nformat: %d\nnum_channels: %d\nsample_rate: %d\nbyte_rate: %d\nblock_align: %d\nbits_per_sample: %d\n\n", |
| filename, wav_header.audio_format, wav_header.num_channels, wav_header.sample_rate, |
| wav_header.byte_rate, wav_header.block_align, wav_header.bits_per_sample); |
| |
| if (wav_header.num_channels > MAX_CHANNELS) { |
| fprintf(stderr, "Error: only support %d or less channels\n", MAX_CHANNELS); |
| return 1; |
| } |
| |
| if (wav_header.num_channels <= 1) { |
| fprintf(stderr, "Error: audio channel is already mono or invalid\n"); |
| return 1; |
| } |
| |
| if (create_new_header(&wav_header_out, &wav_header)) |
| return 1; |
| |
| if(split(filename, &wav_header, &wav_header_out)) |
| return 1; |
| |
| printf("Done!\n"); |
| return 0; |
| } |