You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
11 KiB
C
339 lines
11 KiB
C
#include "libwaveformat.h"
|
|
|
|
__inline static void write_int24(t_wav_uint8 * p_output,t_wav_int32 p_value)
|
|
{
|
|
p_output[0] = (t_wav_uint8)(p_value);
|
|
p_output[1] = (t_wav_uint8)(p_value>>8);
|
|
p_output[2] = (t_wav_uint8)(p_value>>16);
|
|
}
|
|
|
|
__inline static void write_int32(t_wav_uint8 * p_output,t_wav_int32 p_value)
|
|
{
|
|
p_output[0] = (t_wav_uint8)(p_value);
|
|
p_output[1] = (t_wav_uint8)(p_value>>8);
|
|
p_output[2] = (t_wav_uint8)(p_value>>16);
|
|
p_output[3] = (t_wav_uint8)(p_value>>24);
|
|
}
|
|
|
|
__inline static void write_float(t_wav_uint8 * p_output,t_wav_float32 p_value)
|
|
{
|
|
t_wav_conv bah;
|
|
bah.f = p_value;
|
|
p_output[0] = (t_wav_uint8)bah.n;
|
|
p_output[1] = (t_wav_uint8)(bah.n >> 8);
|
|
p_output[2] = (t_wav_uint8)(bah.n >> 16);
|
|
p_output[3] = (t_wav_uint8)(bah.n >> 24);
|
|
}
|
|
|
|
static void g_convert_float32_to_uint8(t_wav_float32 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int32 temp = (t_wav_int32) ( p_sample_buffer[n] * 0x80 );
|
|
if (temp < -0x80) temp = -0x80;
|
|
else if (temp > 0x7F) temp = 0x7F;
|
|
*(p_output++) = (t_wav_uint8)temp ^ 0x80;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_uint8(t_wav_int16 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
*(p_output++) = (t_wav_uint8)(p_sample_buffer[n] >> 8) ^ 0x80;
|
|
}
|
|
}
|
|
|
|
static void g_convert_float32_to_int16(t_wav_float32 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int32 temp = (t_wav_int32) ( p_sample_buffer[n] * 0x8000 );
|
|
if (temp < -0x8000) temp = -0x8000;
|
|
else if (temp > 0x7FFF) temp = 0x7FFF;
|
|
*(p_output++) = (t_wav_uint8)((t_wav_uint32)temp);
|
|
*(p_output++) = (t_wav_uint8)((t_wav_uint32)temp >> 8);
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_int16(t_wav_int16 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int16 temp = p_sample_buffer[n];
|
|
*(p_output++) = (t_wav_uint8)((t_wav_uint16)temp);
|
|
*(p_output++) = (t_wav_uint8)((t_wav_uint16)temp >> 8);
|
|
}
|
|
}
|
|
|
|
static void g_convert_float32_to_int24(t_wav_float32 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
write_int24(p_output,(t_wav_int32) ( p_sample_buffer[n] * 0x800000 ));
|
|
p_output += 3;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_int24(t_wav_int16 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
write_int24(p_output,(t_wav_int32) p_sample_buffer[n] << 8 );
|
|
p_output += 3;
|
|
}
|
|
}
|
|
|
|
static void g_convert_float32_to_int32(t_wav_float32 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
write_int32(p_output,(t_wav_int32) ( p_sample_buffer[n] * 0x80000000 ));
|
|
p_output += 4;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_int32(t_wav_int16 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
write_int32(p_output,(t_wav_int32) p_sample_buffer[n] << 16 );
|
|
p_output += 4;
|
|
}
|
|
}
|
|
|
|
static void g_convert_float32_to_float32(t_wav_float32 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
write_float(p_output,p_sample_buffer[n]);
|
|
p_output += 4;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_float32(t_wav_int16 const * p_sample_buffer,t_wav_uint8 * p_output,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
write_float(p_output,(t_wav_float32)p_sample_buffer[n] / (t_wav_float32) 0x8000);
|
|
p_output += 4;
|
|
}
|
|
}
|
|
|
|
static const t_wav_output_handler g_wav_output_handler_uint8 = {g_convert_float32_to_uint8,g_convert_int16_to_uint8};
|
|
static const t_wav_output_handler g_wav_output_handler_int16 = {g_convert_float32_to_int16,g_convert_int16_to_int16};
|
|
static const t_wav_output_handler g_wav_output_handler_int24 = {g_convert_float32_to_int24,g_convert_int16_to_int24};
|
|
static const t_wav_output_handler g_wav_output_handler_int32 = {g_convert_float32_to_int32,g_convert_int16_to_int32};
|
|
static const t_wav_output_handler g_wav_output_handler_float32 = {g_convert_float32_to_float32,g_convert_int16_to_float32};
|
|
|
|
typedef struct {
|
|
t_wav_uint8 m_data[4];
|
|
} t_riff_header;
|
|
|
|
static const t_riff_header g_header_riff = {{'R','I','F','F'}};
|
|
static const t_riff_header g_header_wave = {{'W','A','V','E'}};
|
|
static const t_riff_header g_header_data = {{'d','a','t','a'}};
|
|
static const t_riff_header g_header_fmt = {{'f','m','t',' '}};
|
|
|
|
static t_wav_uint32 waveformat_write(t_wav_output_file * p_file,void const * p_buffer,t_wav_uint32 p_buffer_size)
|
|
{
|
|
return p_file->m_callback.m_write(p_file->m_callback.m_user_data,p_buffer,p_buffer_size);
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_seek(t_wav_output_file * p_file,t_wav_uint32 p_position)
|
|
{
|
|
return p_file->m_callback.m_seek(p_file->m_callback.m_user_data,p_position);
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_write_riff(t_wav_output_file * p_file,t_riff_header p_riff)
|
|
{
|
|
return waveformat_write(p_file,&p_riff,sizeof(p_riff)) / sizeof(p_riff);
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_write_uint32(t_wav_output_file * p_file,t_wav_uint32 p_val)
|
|
{
|
|
t_wav_uint8 temp[4] = {
|
|
(t_wav_uint8)(p_val & 0xFF),
|
|
(t_wav_uint8)((p_val >> 8) & 0xFF),
|
|
(t_wav_uint8)((p_val >> 16) & 0xFF),
|
|
(t_wav_uint8)((p_val >> 24) & 0xFF)
|
|
};
|
|
|
|
return waveformat_write(p_file,temp,4) / 4;
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_write_uint16(t_wav_output_file * p_file,t_wav_uint16 p_val)
|
|
{
|
|
t_wav_uint8 temp[2] = {
|
|
(t_wav_uint8)(p_val & 0xFF),
|
|
(t_wav_uint8)((p_val >> 8) & 0xFF)
|
|
};
|
|
|
|
return waveformat_write(p_file,temp,2) / 2;
|
|
}
|
|
|
|
static t_wav_uint32 calculate_riff_size(t_wav_output_file * p_file,t_wav_uint32 p_samples_written)
|
|
{
|
|
t_wav_uint32 bytes_written = p_samples_written * p_file->m_bytes_per_sample, bytes_written_padded = bytes_written;
|
|
if (bytes_written & 1)//padding
|
|
bytes_written_padded++;
|
|
|
|
return bytes_written_padded + 4 + 8 + 2+2+4+4+2+2 + 8;
|
|
}
|
|
|
|
static t_wav_uint32 calculate_data_size(t_wav_output_file * p_file,t_wav_uint32 p_samples_written)
|
|
{
|
|
return p_samples_written * p_file->m_bytes_per_sample;
|
|
}
|
|
|
|
t_wav_uint32 waveformat_output_open(t_wav_output_file * p_file,t_wav_output_file_callback p_callback,t_wav_uint32 p_channels,t_wav_uint32 p_bits_per_sample,t_wav_uint32 p_float,t_wav_uint32 p_sample_rate,t_wav_uint32 p_samples_written_expected)
|
|
{
|
|
p_file->m_callback = p_callback;
|
|
p_file->m_channels = p_channels;
|
|
p_file->m_bits_per_sample = p_bits_per_sample;
|
|
p_file->m_float = p_float;
|
|
p_file->m_sample_rate = p_sample_rate;
|
|
p_file->m_bytes_per_sample = p_bits_per_sample >> 3;
|
|
if (p_file->m_bytes_per_sample == 0) return 0;
|
|
p_file->m_buffer_size = sizeof(p_file->m_workbuffer) / p_file->m_bytes_per_sample;
|
|
p_file->m_samples_written_expected = p_samples_written_expected;
|
|
|
|
if (p_float)
|
|
{
|
|
switch(p_bits_per_sample)
|
|
{
|
|
case 32:
|
|
p_file->m_output_handler = g_wav_output_handler_float32;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(p_bits_per_sample)
|
|
{
|
|
case 8:
|
|
p_file->m_output_handler = g_wav_output_handler_uint8;
|
|
break;
|
|
case 16:
|
|
p_file->m_output_handler = g_wav_output_handler_int16;
|
|
break;
|
|
case 24:
|
|
p_file->m_output_handler = g_wav_output_handler_int24;
|
|
break;
|
|
case 32:
|
|
p_file->m_output_handler = g_wav_output_handler_int32;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (waveformat_write_riff(p_file,g_header_riff) != 1) return 0;
|
|
if (waveformat_write_uint32(p_file,calculate_riff_size(p_file,p_file->m_samples_written_expected)) != 1) return 0; //to be possibly rewritten later, offset : 4
|
|
if (waveformat_write_riff(p_file,g_header_wave) != 1) return 0;
|
|
//offset: 12
|
|
if (waveformat_write_riff(p_file,g_header_fmt) != 1) return 0;
|
|
if (waveformat_write_uint32(p_file,2+2+4+4+2+2) != 1) return 0;
|
|
//offset: 12 + 8
|
|
if (waveformat_write_uint16(p_file,p_float ? waveformat_tag_float : waveformat_tag_int) != 1) return 0;
|
|
if (waveformat_write_uint16(p_file,(t_wav_uint16) p_channels) != 1) return 0;
|
|
if (waveformat_write_uint32(p_file,p_sample_rate) != 1) return 0;
|
|
if (waveformat_write_uint32(p_file,(t_wav_uint32)(p_sample_rate * p_file->m_bytes_per_sample * p_channels)) != 1) return 0;
|
|
if (waveformat_write_uint16(p_file,(t_wav_uint16)(p_file->m_bytes_per_sample * p_channels)) != 1) return 0;
|
|
if (waveformat_write_uint16(p_file,(t_wav_uint16)p_bits_per_sample) != 1) return 0;
|
|
//offset: 12 + 8 + 2+2+4+4+2+2
|
|
if (waveformat_write_riff(p_file,g_header_data) != 1) return 0;
|
|
if (waveformat_write_uint32(p_file,calculate_data_size(p_file,p_file->m_samples_written_expected)) != 1) return 0; //to be possibly rewritten later, offset : 12 + 8 + 2+2+4+4+2+2 + 4
|
|
//total header size: 12 + 8 + 2+2+4+4+2+2 + 8
|
|
p_file->m_samples_written = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
t_wav_uint32 waveformat_output_process_float32(t_wav_output_file * p_file,t_wav_float32 const * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 samples_done;
|
|
|
|
samples_done = 0;
|
|
|
|
while(samples_done < p_sample_count)
|
|
{
|
|
t_wav_uint32 delta = p_sample_count - samples_done, delta_written;
|
|
if (delta > p_file->m_buffer_size) delta = p_file->m_buffer_size;
|
|
|
|
p_file->m_output_handler.m_convert_float32(p_sample_buffer + samples_done,p_file->m_workbuffer,delta);
|
|
|
|
delta_written = waveformat_write(p_file,p_file->m_workbuffer,delta * p_file->m_bytes_per_sample) / p_file->m_bytes_per_sample;
|
|
|
|
if (delta_written > 0)
|
|
{
|
|
samples_done += delta_written;
|
|
}
|
|
|
|
if (delta_written != delta) break;
|
|
}
|
|
|
|
p_file->m_samples_written += samples_done;
|
|
|
|
return samples_done;
|
|
}
|
|
|
|
t_wav_uint32 waveformat_output_process_int16(t_wav_output_file * p_file,t_wav_int16 const * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 samples_done;
|
|
|
|
samples_done = 0;
|
|
|
|
while(samples_done < p_sample_count)
|
|
{
|
|
t_wav_uint32 delta = p_sample_count - samples_done, delta_written;
|
|
if (delta > p_file->m_buffer_size) delta = p_file->m_buffer_size;
|
|
|
|
p_file->m_output_handler.m_convert_int16(p_sample_buffer + samples_done,p_file->m_workbuffer,delta);
|
|
|
|
delta_written = waveformat_write(p_file,p_file->m_workbuffer,delta * p_file->m_bytes_per_sample) / p_file->m_bytes_per_sample;
|
|
|
|
if (delta_written > 0)
|
|
{
|
|
samples_done += delta_written;
|
|
}
|
|
|
|
if (delta_written != delta) break;
|
|
}
|
|
|
|
p_file->m_samples_written += samples_done;
|
|
|
|
return samples_done;
|
|
}
|
|
|
|
t_wav_uint32 waveformat_output_close(t_wav_output_file * p_file)
|
|
{
|
|
if ((p_file->m_samples_written * p_file->m_bytes_per_sample) & 1)//padding
|
|
{
|
|
t_wav_uint8 meh = 0;
|
|
if (waveformat_write(p_file,&meh,1) != 1) return 0;
|
|
}
|
|
|
|
if (p_file->m_samples_written != p_file->m_samples_written_expected)
|
|
{
|
|
if (!waveformat_seek(p_file,4)) return 0;
|
|
if (waveformat_write_uint32(p_file,calculate_riff_size(p_file,p_file->m_samples_written)) != 1) return 0;
|
|
if (!waveformat_seek(p_file,12 + 8 + 2+2+4+4+2+2 + 4)) return 0;
|
|
if (waveformat_write_uint32(p_file,calculate_data_size(p_file,p_file->m_samples_written)) != 1) return 0;
|
|
}
|
|
return 1;
|
|
}
|