441 lines
8.5 KiB
C
441 lines
8.5 KiB
C
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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] <volume>\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];
|
|
int16_t i16;
|
|
int32_t i32;
|
|
|
|
float src[8];
|
|
float dst[2];
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
src[i] = 0;
|
|
}
|
|
|
|
if (3 == header->data_fmt)
|
|
{
|
|
// Read float
|
|
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;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Read PCM
|
|
for (int i = 0; i < header->channel_count; ++i)
|
|
{
|
|
fread(u8, sizeof(uint8_t), header->bits_per_sample/8, in);
|
|
switch (header->bits_per_sample)
|
|
{
|
|
// 8 bit is unsigned
|
|
case 8:
|
|
src[i] = u8[0];
|
|
src[i] -= 128;
|
|
if (src[i] < 0)
|
|
{
|
|
src[i] /= 128.0;
|
|
}
|
|
else
|
|
{
|
|
src[i] /= 127.0;
|
|
}
|
|
break;
|
|
|
|
case 16:
|
|
wavtoh16(u8, (uint16_t*)&i16, header->is_be);
|
|
src[i] = i16;
|
|
if (src[i] < 0)
|
|
{
|
|
src[i] /= 32768.0;
|
|
}
|
|
else
|
|
{
|
|
src[i] /= 32767.0;
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
wavtoh24(u8, (uint32_t*)&i32, header->is_be);
|
|
src[i] = i32;
|
|
if (src[i] < 0)
|
|
{
|
|
src[i] /= 8388608.0;
|
|
}
|
|
else
|
|
{
|
|
src[i] /= 8388607.0;
|
|
}
|
|
break;
|
|
|
|
case 32:
|
|
wavtoh32(u8, (uint32_t*)&i32, header->is_be);
|
|
src[i] = i32;
|
|
if (src[i] < 0)
|
|
{
|
|
src[i] /= 2147483648.0;
|
|
}
|
|
else
|
|
{
|
|
src[i] /= 2147483647.0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf("BITS PER SAMPLE WTF - %u\n", header->bits_per_sample);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#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.data_fmt != 1)) ||
|
|
((in_header.bits_per_sample != 8) && (in_header.bits_per_sample != 16) && (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.data_fmt = 3;
|
|
out_header.bits_per_sample=32;
|
|
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;
|
|
}
|