#include <stdio.h>
#include <limits.h>
#include <string.h>
#define IS_WAV_LITTLE_44100_SAMPLED(t) \
(t.sample_rate == 44100)
#define IS_WAV_16BIT_CODED(t) \
(t.bits_per_sample == 16)
#define NUMBER_OF_SAMPLES 64
typedef unsigned int doubleword;
typedef unsigned short word;
typedef struct {
doubleword riff;
doubleword file_length;
doubleword wave;
doubleword fmt;
doubleword fmt_length;
word fmt_tag;
word channels;
doubleword sample_rate;
doubleword bytes_per_second;
word block_align;
word bits_per_sample;
doubleword header_sig;
doubleword data_length;
} wave_header_t;
static void print_wave_info(wave_header_t* wave_header, char *name)
{
printf("Wave header for file: %s:\n", name);
printf("File length in bytes:\t\t%d\n", wave_header->file_length);
printf("Sample rate:\t\t\t%d\n", wave_header->sample_rate);
printf("Bits per sample:\t\t%d\n", wave_header->bits_per_sample);
printf("Data block length in bytes:\t%d\n", wave_header->data_length);
printf("---------------------------------------\n");
}
static void mix_wave(FILE **wave_fds, int greater_idx)
{
short sample1[NUMBER_OF_SAMPLES];
short sample2[NUMBER_OF_SAMPLES];
short real_result[NUMBER_OF_SAMPLES];
int result[NUMBER_OF_SAMPLES];
int i;
while (fread(sample1, sizeof(sample1), 1, wave_fds[greater_idx])) {
if (fread(sample2, sizeof(sample2), 1, wave_fds[!greater_idx])) {
for (i = 0; i < NUMBER_OF_SAMPLES; i++) {
result[i] = sample1[i] + sample2[i];
if (result[i] > SHRT_MAX)
real_result[i] = SHRT_MAX;
else if (result[i] < SHRT_MIN)
real_result[i] = SHRT_MIN;
else
real_result[i] = (short) result[i];
}
}
else {
for (i = 0; i < NUMBER_OF_SAMPLES; i++)
real_result[i] = sample1[i];
}
fwrite(real_result, sizeof(real_result), 1, wave_fds[2]);
}
}
static int write_wave_header(FILE *out_wave_fd, wave_header_t *wave_headers, char *name)
{
wave_header_t mix_wave_header;
memcpy(&mix_wave_header, &wave_headers[0], sizeof(wave_header_t));
mix_wave_header.data_length
= wave_headers[0].data_length > wave_headers[1].data_length ?
wave_headers[0].data_length :
wave_headers[1].data_length;
mix_wave_header.file_length = mix_wave_header.data_length +
sizeof(wave_header_t) - 8;
print_wave_info(&mix_wave_header, name);
return fwrite(&mix_wave_header, sizeof(wave_header_t), 1, out_wave_fd);
}
static int get_greater_wav(wave_header_t *wave_headers)
{
return wave_headers[0].data_length > wave_headers[1].data_length ?
0 :
1;
}
static void cleanup(FILE **wave_fds, int size)
{
int i;
for (i = 0; i < size; i++)
if(wave_fds[i])
fclose(wave_fds[i]);
}
static int check_wave_files(wave_header_t *wave_headers, int size)
{
int i;
for (i = 0; i < size; i++) {
if (!IS_WAV_16BIT_CODED(wave_headers[i])) {
fprintf(stderr, "Error: Only support 16 bit coded waves\n");
return -1;
}
if (!IS_WAV_LITTLE_44100_SAMPLED(wave_headers[i])) {
fprintf(stderr, "Error: Only support 44100 HZ sampled waves\n");
return -1;
}
}
return 0;
}
int main(int argc, char **argv)
{
FILE *wave_fds[3];
int i;
wave_header_t wave_headers[2];
bzero(wave_fds, sizeof(wave_fds));
if (argc != 4) {
fprintf(stderr,
"Usage: mix <in_file1.wav> <in_file2.wav> <out_file.wav>\n");
goto err;
}
for (i = 1; i < argc; i++) {
char *mode;
if (i == argc - 1)
mode = "w";
else
mode = "r";
if (!(wave_fds[i - 1] = fopen(argv[i], mode))) {
perror("Error in fopen()");
goto err;
}
if (i < argc - 1) {
if (!fread(&wave_headers[i - 1], sizeof(wave_header_t), 1, wave_fds[i - 1])) {
perror("Error read()");
goto err;
}
print_wave_info(&wave_headers[i - 1], argv[i]);
}
}
if (!write_wave_header(wave_fds[2], wave_headers, argv[3])) {
perror("Error in fwrite()");
goto err;
}
if (check_wave_files (wave_headers, 2) < 0)
goto err;
mix_wave(wave_fds, get_greater_wav(wave_headers));
cleanup(wave_fds, 3);
return 0;
err:
cleanup(wave_fds, 3);
return 1;
}