blob: 90ee4060931ddc3003c770f601cb68856d3961cb [file] [log] [blame]
/*
* 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;
}