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.
464 lines
12 KiB
C
464 lines
12 KiB
C
#include "libwaveformat.h"
|
|
|
|
typedef struct {
|
|
t_wav_uint8 m_data[4];
|
|
} t_riff_header;
|
|
|
|
static t_wav_uint32 riff_compare(t_riff_header p_chunk,t_wav_uint8 a,t_wav_uint8 b,t_wav_uint8 c,t_wav_uint8 d)
|
|
{
|
|
return (p_chunk.m_data[0] == a && p_chunk.m_data[1] == b && p_chunk.m_data[2] == c && p_chunk.m_data[3] == d) ? 1 : 0;
|
|
}
|
|
|
|
|
|
static void g_convert_float32_to_float32(t_wav_uint8 const * p_input,t_wav_float32 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_conv temp;
|
|
|
|
temp.n = (t_wav_uint32) (
|
|
( (t_wav_uint32)p_input[0] ) |
|
|
( (t_wav_uint32)p_input[1] << 8) |
|
|
( (t_wav_uint32)p_input[2] << 16) |
|
|
( (t_wav_uint32)p_input[3] << 24)
|
|
);
|
|
|
|
p_input += 4;
|
|
|
|
p_sample_buffer[n] = temp.f;
|
|
}
|
|
}
|
|
|
|
static void g_convert_float32_to_int16(t_wav_uint8 const * p_input,t_wav_int16 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_conv temp;
|
|
t_wav_int32 tempi;
|
|
temp.n = (t_wav_uint32) (
|
|
( (t_wav_uint32)p_input[0] ) |
|
|
( (t_wav_uint32)p_input[1] << 8) |
|
|
( (t_wav_uint32)p_input[2] << 16) |
|
|
( (t_wav_uint32)p_input[3] << 24)
|
|
);
|
|
|
|
p_input += 4;
|
|
|
|
tempi = (t_wav_int32)(temp.f * 0x8000);
|
|
if (tempi < -0x8000) tempi = -0x8000;
|
|
else if (tempi > 0x7FFF) tempi = 0x7FFF;
|
|
|
|
p_sample_buffer[n] = (t_wav_int16) tempi;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int32_to_float32(t_wav_uint8 const * p_input,t_wav_float32 * p_sample_buffer,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) (
|
|
( (t_wav_uint32)p_input[0] ) |
|
|
( (t_wav_uint32)p_input[1] << 8) |
|
|
( (t_wav_uint32)p_input[2] << 16) |
|
|
( (t_wav_uint32)p_input[3] << 24)
|
|
);
|
|
|
|
p_input += 4;
|
|
|
|
p_sample_buffer[n] = (t_wav_float32) temp / (t_wav_float32) 0x80000000 ;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int32_to_int16(t_wav_uint8 const * p_input,t_wav_int16 * p_sample_buffer,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) (
|
|
( (t_wav_uint32)p_input[0] ) |
|
|
( (t_wav_uint32)p_input[1] << 8) |
|
|
( (t_wav_uint32)p_input[2] << 16) |
|
|
( (t_wav_uint32)p_input[3] << 24)
|
|
);
|
|
|
|
p_input += 4;
|
|
|
|
p_sample_buffer[n] = (t_wav_int16) ( temp >> 16 );
|
|
}
|
|
}
|
|
|
|
static void g_convert_int24_to_float32(t_wav_uint8 const * p_input,t_wav_float32 * p_sample_buffer,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) (
|
|
( (t_wav_uint32)p_input[0] ) |
|
|
( (t_wav_uint32)p_input[1] << 8) |
|
|
( (t_wav_int32)(t_wav_int8)p_input[2] << 16)
|
|
);
|
|
|
|
p_input += 3;
|
|
|
|
p_sample_buffer[n] = (t_wav_float32) temp / (t_wav_float32) 0x800000 ;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int24_to_int16(t_wav_uint8 const * p_input,t_wav_int16 * p_sample_buffer,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) (
|
|
( (t_wav_uint32)p_input[0] ) |
|
|
( (t_wav_uint32)p_input[1] << 8) |
|
|
( (t_wav_int32)(t_wav_int8)p_input[2] << 16)
|
|
);
|
|
|
|
p_input += 3;
|
|
|
|
p_sample_buffer[n] = (t_wav_int16) ( temp >> 8 );
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_float32(t_wav_uint8 const * p_input,t_wav_float32 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int16 temp = (t_wav_int16)
|
|
(
|
|
(t_wav_uint16)p_input[0] |
|
|
( (t_wav_uint16)p_input[1] << 8 )
|
|
);
|
|
|
|
p_input += 2;
|
|
|
|
p_sample_buffer[n] = (t_wav_float32) temp / (t_wav_float32) 0x8000 ;
|
|
}
|
|
}
|
|
|
|
static void g_convert_int16_to_int16(t_wav_uint8 const * p_input,t_wav_int16 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int16 temp = (t_wav_int16)
|
|
(
|
|
(t_wav_uint16)p_input[0] |
|
|
( (t_wav_uint16)p_input[1] << 8 )
|
|
);
|
|
|
|
p_input += 2;
|
|
|
|
p_sample_buffer[n] = temp;
|
|
}
|
|
}
|
|
|
|
static void g_convert_uint8_to_float32(t_wav_uint8 const * p_input,t_wav_float32 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int8 temp = (t_wav_int8)( *(p_input++) ^ 0x80 );
|
|
p_sample_buffer[n] = (t_wav_float32) temp / (t_wav_float32) 0x80;
|
|
}
|
|
}
|
|
|
|
static void g_convert_uint8_to_int16(t_wav_uint8 const * p_input,t_wav_int16 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 n;
|
|
for(n=0;n<p_sample_count;n++)
|
|
{
|
|
t_wav_int8 temp = (t_wav_int8)( *(p_input++) ^ 0x80 );
|
|
p_sample_buffer[n] = (t_wav_int16) temp << 8;
|
|
}
|
|
}
|
|
|
|
static t_wav_input_handler g_input_handler_float32 = {g_convert_float32_to_float32,g_convert_float32_to_int16};
|
|
static t_wav_input_handler g_input_handler_int32 = {g_convert_int32_to_float32,g_convert_int32_to_int16};
|
|
static t_wav_input_handler g_input_handler_int24 = {g_convert_int24_to_float32,g_convert_int24_to_int16};
|
|
static t_wav_input_handler g_input_handler_int16 = {g_convert_int16_to_float32,g_convert_int16_to_int16};
|
|
static t_wav_input_handler g_input_handler_uint8 = {g_convert_uint8_to_float32,g_convert_uint8_to_int16};
|
|
|
|
static t_wav_uint32 waveformat_read(t_wav_input_file * p_file,void * p_buffer,t_wav_uint32 p_bytes)
|
|
{
|
|
return p_file->m_callback.m_read(p_file->m_callback.m_user_data,p_buffer,p_bytes);
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_skip(t_wav_input_file * p_file,t_wav_uint32 p_bytes)
|
|
{
|
|
t_wav_uint8 dummy[256];
|
|
t_wav_uint32 delta,done,delta_done;
|
|
|
|
done = 0;
|
|
|
|
while(done < p_bytes)
|
|
{
|
|
delta = p_bytes - done;
|
|
if (delta > sizeof(dummy)) delta = sizeof(dummy);
|
|
|
|
delta_done = waveformat_read(p_file,dummy,delta);
|
|
|
|
done += delta_done;
|
|
|
|
if (delta_done != delta) break;
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
|
|
static t_wav_uint32 waveformat_read_riff(t_wav_input_file * p_file,t_riff_header * p_header)
|
|
{
|
|
return waveformat_read(p_file,p_header,sizeof(t_riff_header)) / sizeof(t_riff_header);
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_read_uint32(t_wav_input_file * p_file,t_wav_uint32 * p_value)
|
|
{
|
|
t_wav_uint8 temp[4];
|
|
if (waveformat_read(p_file,&temp,sizeof(temp)) != sizeof(temp)) return 0;
|
|
* p_value =
|
|
((t_wav_uint32)temp[0]) |
|
|
((t_wav_uint32)temp[1] << 8) |
|
|
((t_wav_uint32)temp[2] << 16) |
|
|
((t_wav_uint32)temp[3] << 24);
|
|
return 1;
|
|
}
|
|
|
|
static t_wav_uint32 waveformat_read_uint16(t_wav_input_file * p_file,t_wav_uint16 * p_value)
|
|
{
|
|
t_wav_uint8 temp[2];
|
|
if (waveformat_read(p_file,&temp,sizeof(temp)) != sizeof(temp)) return 0;
|
|
* p_value =
|
|
((t_wav_uint16)temp[0]) |
|
|
((t_wav_uint16)temp[1] << 8);
|
|
return 1;
|
|
}
|
|
|
|
t_wav_uint32 waveformat_input_open(t_wav_input_file * p_file,t_wav_input_file_callback p_callback)
|
|
{
|
|
t_riff_header header;
|
|
t_wav_uint32 main_size,main_offset,chunk_size;
|
|
t_wav_uint8 found_fmt;
|
|
|
|
found_fmt = 0;
|
|
|
|
p_file->m_callback = p_callback;
|
|
|
|
if (waveformat_read_riff(p_file,&header) != 1) return 0;
|
|
|
|
if (!riff_compare(header,'R','I','F','F')) return 0;
|
|
|
|
if (waveformat_read_uint32(p_file,&main_size) != 1) return 0;
|
|
|
|
if (main_size < 4) return 0;
|
|
|
|
|
|
if (waveformat_read_riff(p_file,&header) != 1) return 0;
|
|
|
|
if (!riff_compare(header,'W','A','V','E')) return 0;
|
|
|
|
main_offset = 4;
|
|
|
|
for(;;)
|
|
{
|
|
if (main_size - main_offset < 8) return 0;
|
|
|
|
if (waveformat_read_riff(p_file,&header) != 1) return 0;
|
|
if (waveformat_read_uint32(p_file,&chunk_size) != 1) return 0;
|
|
|
|
main_offset += 8;
|
|
|
|
if (main_size - main_offset < chunk_size) return 0;
|
|
|
|
|
|
|
|
if (riff_compare(header,'f','m','t',' '))
|
|
{
|
|
t_wav_uint32 fmt_remaining;
|
|
if (found_fmt) return 0;//duplicate fmt chunk
|
|
|
|
fmt_remaining = chunk_size;
|
|
|
|
if (fmt_remaining < 2+2+4+4+2+2) return 0;
|
|
|
|
if (waveformat_read_uint16(p_file,&p_file->m_format_tag ) != 1) return 0;
|
|
if (waveformat_read_uint16(p_file,&p_file->m_channels ) != 1) return 0;
|
|
if (waveformat_read_uint32(p_file,&p_file->m_samples_per_sec ) != 1) return 0;
|
|
if (waveformat_read_uint32(p_file,&p_file->m_avg_bytes_per_sec ) != 1) return 0;
|
|
if (waveformat_read_uint16(p_file,&p_file->m_block_align ) != 1) return 0;
|
|
if (waveformat_read_uint16(p_file,&p_file->m_bits_per_sample ) != 1) return 0;
|
|
|
|
p_file->m_bytes_per_sample = p_file->m_bits_per_sample / 8;
|
|
|
|
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;
|
|
|
|
fmt_remaining -= 2+2+4+4+2+2;
|
|
|
|
switch(p_file->m_format_tag)
|
|
{
|
|
case waveformat_tag_int:
|
|
switch(p_file->m_bits_per_sample)
|
|
{
|
|
case 8:
|
|
p_file->m_input_handler = g_input_handler_uint8;
|
|
break;
|
|
case 16:
|
|
p_file->m_input_handler = g_input_handler_int16;
|
|
break;
|
|
case 24:
|
|
p_file->m_input_handler = g_input_handler_int24;
|
|
break;
|
|
case 32:
|
|
p_file->m_input_handler = g_input_handler_int32;
|
|
break;
|
|
default:
|
|
//unsupported format
|
|
return 0;
|
|
}
|
|
break;
|
|
case waveformat_tag_float:
|
|
switch(p_file->m_bits_per_sample)
|
|
{
|
|
case 32:
|
|
p_file->m_input_handler = g_input_handler_float32;
|
|
break;
|
|
#if 0
|
|
case 64:
|
|
|
|
break;
|
|
#endif
|
|
default:
|
|
//unsupported format
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
//unsupported format
|
|
return 0;
|
|
}
|
|
|
|
if (chunk_size & 1) fmt_remaining++;
|
|
|
|
if (fmt_remaining > 0)
|
|
{
|
|
if (waveformat_skip(p_file,fmt_remaining) != fmt_remaining) return 0;
|
|
}
|
|
|
|
main_offset += chunk_size;
|
|
if (chunk_size & 1) main_offset++;
|
|
|
|
found_fmt = 1;
|
|
}
|
|
else if (riff_compare(header,'d','a','t','a'))
|
|
{
|
|
if (!found_fmt) return 0;//found data before fmt, don't know how to handle data
|
|
//found parsable data chunk, ok to proceed
|
|
p_file->m_data_size = chunk_size / p_file->m_bytes_per_sample;
|
|
p_file->m_data_position = 0;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{//unknown chunk, let's skip over
|
|
t_wav_uint32 toskip = chunk_size;
|
|
if (toskip & 1) toskip++;
|
|
|
|
if (waveformat_skip(p_file,toskip) != toskip) return 0;
|
|
|
|
main_offset += toskip;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
t_wav_uint32 waveformat_input_process_float32(t_wav_input_file * p_file,t_wav_float32 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 samples_read;
|
|
|
|
samples_read = 0;
|
|
|
|
if (p_file->m_data_position + p_sample_count > p_file->m_data_size)
|
|
p_sample_count = p_file->m_data_size - p_file->m_data_position;
|
|
|
|
while(samples_read < p_sample_count)
|
|
{
|
|
t_wav_uint32 delta, deltaread;
|
|
|
|
delta = p_sample_count - samples_read;
|
|
if (delta > p_file->m_buffer_size) delta = p_file->m_buffer_size;
|
|
|
|
deltaread = waveformat_read(p_file,p_file->m_workbuffer,delta * p_file->m_bytes_per_sample) / p_file->m_bytes_per_sample;
|
|
|
|
if (deltaread > 0)
|
|
{
|
|
p_file->m_input_handler.m_convert_float32(p_file->m_workbuffer,p_sample_buffer + samples_read,deltaread);
|
|
|
|
samples_read += deltaread;
|
|
}
|
|
|
|
if (deltaread != delta) break;
|
|
}
|
|
|
|
p_file->m_data_position += samples_read;
|
|
|
|
return samples_read;
|
|
}
|
|
|
|
t_wav_uint32 waveformat_input_process_int16(t_wav_input_file * p_file,t_wav_int16 * p_sample_buffer,t_wav_uint32 p_sample_count)
|
|
{
|
|
t_wav_uint32 samples_read;
|
|
|
|
samples_read = 0;
|
|
|
|
if (p_file->m_data_position + p_sample_count > p_file->m_data_size)
|
|
p_sample_count = p_file->m_data_size - p_file->m_data_position;
|
|
|
|
while(samples_read < p_sample_count)
|
|
{
|
|
t_wav_uint32 delta, deltaread;
|
|
|
|
delta = p_sample_count - samples_read;
|
|
if (delta > p_file->m_buffer_size) delta = p_file->m_buffer_size;
|
|
|
|
deltaread = waveformat_read(p_file,p_file->m_workbuffer,delta * p_file->m_bytes_per_sample) / p_file->m_bytes_per_sample;
|
|
|
|
if (deltaread > 0)
|
|
{
|
|
p_file->m_input_handler.m_convert_int16(p_file->m_workbuffer,p_sample_buffer + samples_read,deltaread);
|
|
|
|
samples_read += deltaread;
|
|
}
|
|
|
|
if (deltaread != delta) break;
|
|
}
|
|
|
|
p_file->m_data_position += samples_read;
|
|
|
|
return samples_read;
|
|
}
|
|
|
|
void waveformat_input_close(t_wav_input_file * p_file)
|
|
{
|
|
}
|
|
|
|
t_wav_uint32 waveformat_input_query_sample_rate(t_wav_input_file * p_file)
|
|
{
|
|
return p_file->m_samples_per_sec;
|
|
}
|
|
t_wav_uint32 waveformat_input_query_channels(t_wav_input_file * p_file)
|
|
{
|
|
return p_file->m_channels;
|
|
}
|
|
t_wav_uint32 waveformat_input_query_length(t_wav_input_file * p_file)
|
|
{
|
|
return p_file->m_data_size;
|
|
}
|