From e6e9744529aff5c588f3357c0cab3321cac5a69e Mon Sep 17 00:00:00 2001 From: DWW Date: Thu, 7 Jul 2022 19:51:37 +0300 Subject: [PATCH] Initial commit - V1.0 --- Makefile | 9 ++ main.c | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++ wav_convert.c | 118 ++++++++++++++++ wav_convert.h | 12 ++ 4 files changed, 502 insertions(+) create mode 100644 Makefile create mode 100644 main.c create mode 100644 wav_convert.c create mode 100644 wav_convert.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7235809 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +all: + make apex_mix + make apex_mix_lfe + +apex_mix: + gcc -o apex_mix main.c wav_convert.c + +apex_mix_lfe: + gcc -o apex_mix_lfe -DLFE_ENABLE main.c wav_convert.c diff --git a/main.c b/main.c new file mode 100644 index 0000000..6296d34 --- /dev/null +++ b/main.c @@ -0,0 +1,363 @@ +#include +#include +#include +#include + +#include "wav_convert.h" + +typedef struct wav_data +{ + uint8_t is_be; // Endianess + uint16_t data_fmt; // Format Type + uint16_t channel_count; // Ammount of Channels + uint32_t sample_rate; // Samples per second + uint16_t bits_per_sample; // Bits Per Sample + uint32_t sample_count; // Samples In File +} wav_data; + +int read_header(FILE* file, wav_data* header) +{ + uint8_t arr[4]; + uint8_t riff[4] = {'R', 'I', 'F', 'F'}; + uint8_t rifx[4] = {'R', 'I', 'F', 'X'}; + uint8_t wave[4] = {'W', 'A', 'V', 'E'}; + uint8_t fmt[4] = {'f', 'm', 't', ' '}; + uint8_t data[4] = {'d', 'a', 't', 'a'}; + uint32_t u32; + uint16_t u16; + + if ((NULL == file) || (NULL == header)) + { + return -1; + } + + fseek(file, 0, SEEK_SET); + fread(arr, sizeof(uint8_t), 4, file); + + // Detect RIFF/RIFX + if (0 == strncmp(arr, riff, 4)) + { + header->is_be = 0; + } + else if (0 == strncmp(arr, rifx, 4)) + { + header->is_be = 1; + } + else + { + printf("RIFF/RIFX\n"); + return -1; + } + + // File Size Dummy Read + fread(arr, sizeof(uint8_t), 4, file); + // WAVE + fread(arr, sizeof(uint8_t), 4, file); + if (0 != strncmp(arr, wave, 4)) + { + printf("WAVE\n"); + return -1; + } + + // fmt + fread(arr, sizeof(uint8_t), 4, file); + if (0 != strncmp(arr, fmt, 4)) + { + printf("fmt\n"); + return -1; + } + + // Subchunk 1 Size + fread(arr, sizeof(uint8_t), 4, file); + wavtoh32(arr, &u32, header->is_be); + if (16 != u32) + { + printf("fmt Size != 16\n"); + } + + // Data Format + fread(arr, sizeof(uint8_t), 2, file); + wavtoh16(arr, &u16, header->is_be); + header->data_fmt = u16; + + // Channel Count + fread(arr, sizeof(uint8_t), 2, file); + wavtoh16(arr, &u16, header->is_be); + header->channel_count = u16; + + // Sample Rate + fread(arr, sizeof(uint8_t), 4, file); + wavtoh32(arr, &u32, header->is_be); + header->sample_rate = u32; + + // Byte Rate Dummy Read + fread(arr, sizeof(uint8_t), 4, file); + + // Block Align Dummy Read + fread(arr, sizeof(uint8_t), 2, file); + + // Bits Per Sample + fread(arr, sizeof(uint8_t), 2, file); + wavtoh16(arr, &u16, header->is_be); + header->bits_per_sample = u16; + + // data + fread(arr, sizeof(uint8_t), 4, file); + if (0 != strncmp(arr, data, 4)) + { + printf("data\n"); + return -1; + } + + // Sample Count Read + fread(arr, sizeof(uint8_t), 4, file); + wavtoh32(arr, &u32, header->is_be); + header->sample_count = u32; + u32 = header->channel_count; + u32 *= header->bits_per_sample; + u32 /= 8; + header->sample_count /= u32; + + return 0; +} + +void write_header(FILE* file, wav_data* header) +{ + uint8_t arr[4]; + uint32_t u32; + uint16_t u16; + + if ((NULL == file) || (NULL == header)) + { + return; + } + + // RIFF + strncpy(arr, "RIFF", 4); + fwrite(arr, sizeof(uint8_t), 4, file); + + // File Size + u32 = header->sample_count; + u32 *= header->channel_count; + u32 *= header->bits_per_sample; + u32 /= 8; + u32 += 36; + htowav32(&u32, arr, 0); + fwrite(arr, sizeof(uint8_t), 4, file); + + // WAVE + strncpy(arr, "WAVE", 4); + fwrite(arr, sizeof(uint8_t), 4, file); + + // fmt + strncpy(arr, "fmt ", 4); + fwrite(arr, sizeof(uint8_t), 4, file); + + // fmt Size + u32 = 16; + htowav32(&u32, arr, 0); + fwrite(arr, sizeof(uint8_t), 4, file); + + // Format Type + u16 = header->data_fmt; + htowav16(&u16, arr, 0); + fwrite(arr, sizeof(uint8_t), 2, file); + + // Channel Count + u16 = header->channel_count; + htowav16(&u16, arr, 0); + fwrite(arr, sizeof(uint8_t), 2, file); + + // Sample Rate + u32 = header->sample_rate; + htowav32(&u32, arr, 0); + fwrite(arr, sizeof(uint8_t), 4, file); + + // Byte Rate + u32 = header->sample_rate; + u32 *= header->channel_count; + u32 *= header->bits_per_sample; + u32 /= 8; + htowav32(&u32, arr, 0); + fwrite(arr, sizeof(uint8_t), 4, file); + + // Block Align + u16 = header->channel_count; + u16 *= header->bits_per_sample; + u16 /= 8; + htowav16(&u16, arr, 0); + fwrite(arr, sizeof(uint8_t), 2, file); + + // Bits Per Sample + u16 = header->bits_per_sample; + htowav16(&u16, arr, 0); + fwrite(arr, sizeof(uint8_t), 2, file); + + // data + strncpy(arr, "data", 4); + fwrite(arr, sizeof(uint8_t), 4, file); + + // data Size + u32 = header->sample_count; + u32 *= header->channel_count; + u32 *= header->bits_per_sample; + u32 /= 8; + htowav32(&u32, arr, 0); + fwrite(arr, sizeof(uint8_t), 4, file); +} + +void print_header(wav_data* header) +{ + if (NULL == header) + { + return; + } + + if (0 == header->is_be) + { + printf("LE\n"); + } + else + { + printf("BE\n"); + } + + printf("Format: %u\n", header->data_fmt); + printf("Channels: %u\n", header->channel_count); + printf("Sample Rate: %lu\n", header->sample_rate); + printf("Bits: %u\n", header->bits_per_sample); + printf("Samples: %lu\n", header->sample_count); +} + +void usage(const char* name) +{ + printf("Usage:\n"); + printf("\t%s [input] [output] \n", name); +} + +void process_sample(FILE* in, FILE* out, wav_data* header, float volume) +{ + union u32tof + { + uint32_t u32; + float f; + } un; + uint8_t u8[4]; + + float src[8]; + float dst[2]; + + for (int i = 0; i < 8; ++i) + { + src[i] = 0; + } + + for (int i = 0; i < header->channel_count; ++i) + { + fread(u8, sizeof(uint8_t), 4, in); + wavtoh32(u8, &un.u32, header->is_be); + src[i] = un.f; + } + +#ifdef LFE_ENABLE + // With LFE + dst[0] = src[0] + 0.7071 * src[2] + 0.7071 * src[3] + 0.7071 * src[4] + 0.7071 * src[6]; + dst[1] = src[1] + 0.7071 * src[2] + 0.7071 * src[3] + 0.7071 * src[5] + 0.7071 * src[7]; +#else + // Without LFE + dst[0] = src[0] + 0.7071 * src[2] + 0.7071 * src[4] + 0.7071 * src[6]; + dst[1] = src[1] + 0.7071 * src[2] + 0.7071 * src[5] + 0.7071 * src[7]; +#endif + + dst[0] *= volume; + dst[1] *= volume; + + for (int i = 0; i < 2; ++i) + { + // CLIPPING + if (dst[i] > 0.975) + { + dst[i] = 0.975; + } + + if (dst[i] < -0.975) + { + dst[i] = -0.975; + } + + un.f = dst[i]; + htowav32(&un.u32, u8, 0); + fwrite(u8, sizeof(uint8_t), 4, out); + } +} + +int main(int argc, char** argv) +{ + FILE *in_file; + FILE *out_file; + wav_data in_header; + wav_data out_header; + float volume = 1.0f; + + if (argc < 3) + { + usage(argv[0]); + return -1; + } + + in_file = fopen(argv[1], "rb"); + if (NULL == in_file) + { + printf("Input File\n"); + return -1; + } + + if (0 != read_header(in_file, &in_header)) + { + fclose(in_file); + printf("Header\n"); + return -1; + } + + // 1 is PCM, 3 is float + if ((in_header.data_fmt != 3) || (in_header.bits_per_sample != 32) || + ((in_header.channel_count != 8) && (in_header.channel_count != 2))) + { + printf("Weird Header\n"); + print_header(&in_header); + fclose(in_file); + return -1; + } + + out_file = fopen(argv[2], "wb"); + if (NULL == out_file) + { + fclose(in_file); + printf("Output File\n"); + return -1; + } + + if (argc > 3) + { + volume = atof(argv[3]); + if (0.0 == volume) + { + volume = 1.0; + } + } + + out_header = in_header; + out_header.channel_count = 2; + out_header.is_be = 0; + write_header(out_file, &out_header); + + // Do processing + for (uint32_t u32 = 0; u32 < in_header.sample_count; ++u32) + { + process_sample(in_file, out_file, &in_header, volume); + } + + fclose(in_file); + fclose(out_file); + return 0; +} diff --git a/wav_convert.c b/wav_convert.c new file mode 100644 index 0000000..9157a06 --- /dev/null +++ b/wav_convert.c @@ -0,0 +1,118 @@ +#include "wav_convert.h" + +typedef union wav_u +{ + uint8_t u8[4]; + uint16_t u16[2]; + uint32_t u32; +} wav_u; + +uint32_t endian_swap32(uint32_t in) +{ + wav_u un; + uint8_t u8[4]; + + un.u32 = in; + for (int i = 0; i < 4; ++i) + { + u8[i] = un.u8[3 - i]; + } + + for (int i = 0; i < 4; ++i) + { + un.u8[i] = u8[i]; + } + + return un.u32; +} + +uint16_t endian_swap16(uint16_t in) +{ + wav_u un; + uint8_t u8[2]; + + un.u16[0] = in; + for (int i = 0; i < 2; ++i) + { + u8[i] = un.u8[1 - i]; + } + + for (int i = 0; i < 2; ++i) + { + un.u8[i] = u8[i]; + } + + return un.u16[0]; +} + +void wavtoh32(uint8_t* in, uint32_t* out, uint8_t is_be) +{ + wav_u un; + for (int i = 0; i < 4; ++i) + { + un.u8[i] = in[i]; + } + + if (0 == is_be) + { + *out = un.u32; + } + else + { + *out = endian_swap32(un.u32); + } +} + +void wavtoh16(uint8_t* in, uint16_t* out, uint8_t is_be) +{ + wav_u un; + for (int i = 0; i < 2; ++i) + { + un.u8[i] = in[i]; + } + + if (0 == is_be) + { + *out = un.u16[0]; + } + else + { + *out = endian_swap16(un.u16[0]); + } +} + +void htowav32(uint32_t* in, uint8_t* out, uint8_t is_be) +{ + wav_u un; + if (0 == is_be) + { + un.u32 = *in; + } + else + { + un.u32 = endian_swap32(*in); + } + + for (int i = 0; i < 4; ++i) + { + out[i] = un.u8[i]; + } +} + +void htowav16(uint16_t* in, uint8_t* out, uint8_t is_be) +{ + wav_u un; + if (0 == is_be) + { + un.u16[0] = *in; + } + else + { + un.u16[0] = endian_swap16(*in); + } + + for (int i = 0; i < 2; ++i) + { + out[i] = un.u8[i]; + } +} diff --git a/wav_convert.h b/wav_convert.h new file mode 100644 index 0000000..4705ba3 --- /dev/null +++ b/wav_convert.h @@ -0,0 +1,12 @@ +#ifndef WAV_CONVERT_H_ +#define WAV_CONVERT_H_ + +#include + +void wavtoh32(uint8_t* in, uint32_t* out, uint8_t is_be); +void wavtoh16(uint8_t* in, uint16_t* out, uint8_t is_be); + +void htowav32(uint32_t* in, uint8_t* out, uint8_t is_be); +void htowav16(uint16_t* in, uint8_t* out, uint8_t is_be); + +#endif // WAV_CONVERT_H_