Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions doc/manual/utl.tex
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,160 @@ \subsection{{\tt parallelize\_*\_justified}}
parallel sample buffer.


%-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
\section{WAV file I/O} \label{Utl-wav-io}

The {\tt wav\_io} module provides transparent reading and writing of WAV
files alongside the traditional raw (headerless) PCM format used throughout
the STL. All tools that process audio PCM signals use this module to
auto-detect the input format and to select the output format based on the
output filename extension.

\subsection{Design}

\begin{itemize}
\item {\bf Input auto-detection:} When opening a file for reading, the first
four bytes are checked for the RIFF magic number ({\tt 0x52494646}). If
found, the file is parsed as a WAV file; otherwise it is treated as raw
PCM (legacy behavior).
\item {\bf Output format:} Determined solely by the output filename extension.
If the filename ends in {\tt .wav} (case-insensitive), a WAV file with a
canonical 44-byte header is written. Any other extension produces raw PCM
output, preserving full backward compatibility.
\item {\bf Supported formats:} 8-bit unsigned PCM, 16-bit signed PCM, 24-bit
signed PCM, 32-bit signed PCM, and 32-bit IEEE~754 float (format tags~1
and~3).
\item {\bf Parameter validation:} The caller specifies expected sample rate,
channel count, and bit depth. If any expected value is non-zero and does not
match the WAV header, the file is rejected with an error.
\item {\bf Multi-channel:} When a multi-channel WAV file is opened by a mono
tool, channel~1 is extracted and a warning is printed to {\tt stderr}.
\end{itemize}

\subsection{{\tt audio\_open\_read}}

Opens a file for reading with automatic format detection.

{\small
\begin{verbatim}
AUDIO_FILE *audio_open_read(
const char *filename, /* File to open */
long expected_rate, /* Expected sample rate (0 = don't check) */
int expected_channels, /* Expected channels (0 = don't check) */
int expected_bits /* Expected bits/sample (0 = don't check) */
);
\end{verbatim}
}

Returns a pointer to an {\tt AUDIO\_FILE} structure, or {\tt NULL} on error.
For raw files, the file pointer is positioned at byte~0. For WAV files, it is
positioned at the start of the PCM data chunk.

\subsection{{\tt audio\_open\_write}}

Opens a file for writing. The format is determined by the filename extension.

{\small
\begin{verbatim}
AUDIO_FILE *audio_open_write(
const char *filename, /* File to create */
long sample_rate, /* Sample rate for WAV header */
int channels, /* Channel count for WAV header */
int bits_per_sample /* Bits per sample (8, 16, 24, or 32) */
);
\end{verbatim}
}

For WAV output, a 44-byte placeholder header is written on open and updated
with the correct data size on close.

\subsection{{\tt audio\_read} and {\tt audio\_write}}

Read and write samples in the native bit depth of the file:

{\small
\begin{verbatim}
long audio_read(AUDIO_FILE *af, void *buffer, long nsamples);
long audio_write(AUDIO_FILE *af, void *buffer, long nsamples);
\end{verbatim}
}

The buffer type must match the file's bit depth:

\begin{center}
\begin{tabular}{lll}
\hline
{\bf Bit depth} & {\bf Buffer type} & {\bf Bytes/sample} \\
\hline
8-bit PCM & {\tt unsigned char *} & 1 \\
16-bit PCM & {\tt short *} & 2 \\
24-bit PCM & {\tt long *} (sign-extended) & 3 on disk, 4 in memory \\
32-bit PCM & {\tt long *} & 4 \\
32-bit float & {\tt float *} & 4 \\
\hline
\end{tabular}
\end{center}

For multi-channel WAV input, {\tt audio\_read} de-interleaves and returns only
channel~1. Both functions return the number of samples actually read or
written.

\subsection{{\tt audio\_close}}

Closes the file. For WAV output, seeks back to the header and writes the
final data size and file size fields.

{\small
\begin{verbatim}
void audio_close(AUDIO_FILE *af);
\end{verbatim}
}

\subsection{Accessor functions}

{\small
\begin{verbatim}
int audio_is_wav(AUDIO_FILE *af); /* 1 if WAV, 0 if raw */
long audio_get_sample_rate(AUDIO_FILE *af); /* From WAV header, 0 if raw */
int audio_get_channels(AUDIO_FILE *af); /* From WAV header, 0 if raw */
\end{verbatim}
}

\subsection{Usage example}

The following fragment shows a 16-bit tool reading and writing WAV or raw:

{\small
\begin{verbatim}
#include "wav_io.h"

AUDIO_FILE *in, *out;
short buf[256];
long n;

in = audio_open_read("input.wav", 8000, 1, 16);
out = audio_open_write("output.wav", 8000, 1, 16);

while ((n = audio_read(in, buf, 256)) > 0) {
/* process buf[] */
audio_write(out, buf, n);
}

audio_close(out);
audio_close(in);
\end{verbatim}
}

A 24-bit tool would use {\tt long} buffers and pass 24 for {\tt expected\_bits}:

{\small
\begin{verbatim}
AUDIO_FILE *in = audio_open_read("hires.wav", 48000, 1, 24);
long buf[256];
audio_read(in, buf, 256); /* 24-bit values sign-extended into long */
\end{verbatim}
}

%-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
\section{Portability and compliance} \label{Utl-port}

Expand Down
4 changes: 3 additions & 1 deletion src/bs1770demo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
add_executable(bs1770demo bs1770demo.c)
include_directories(../utl)

add_executable(bs1770demo bs1770demo.c ../utl/wav_io.c)
target_link_libraries(bs1770demo ${M_LIBRARY})


Expand Down
28 changes: 14 additions & 14 deletions src/bs1770demo/bs1770demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "wav_io.h"

#define BLOCK_SIZE 19200 /* 400 ms in 48000 Hz sample rate */
#define STEP_SIZE 4800 /* 100 ms in 48000 Hz sample rate (75% overlap of 400 ms gating blocks) */
Expand Down Expand Up @@ -387,8 +388,8 @@ long parse_conf( /* o: 0:success, -1:fail */

int main(int argc, char **argv )
{
FILE* f_input;
FILE* f_output;
AUDIO_FILE* f_input;
AUDIO_FILE* f_output;
char *input_filename;
char *output_filename;
double *input;
Expand Down Expand Up @@ -477,7 +478,7 @@ int main(int argc, char **argv )
}

input_filename = argv[i++];
if( (f_input = fopen( input_filename, "rb" )) == NULL )
if( (f_input = audio_open_read (input_filename, 48000, 0, 16)) == NULL )
{
fprintf( stderr, "*** Could not open input file %s, exiting..\n\n", input_filename );
usage();
Expand All @@ -489,7 +490,7 @@ int main(int argc, char **argv )
else
{
output_filename = argv[i];
if( (f_output = fopen( output_filename, "wb" )) == NULL )
if( (f_output = audio_open_write( output_filename, audio_get_sample_rate (f_input), 1, 16 )) == NULL )
{
fprintf( stderr, "*** Could not open output file %s, exiting..\n\n", output_filename );
usage();
Expand Down Expand Up @@ -541,9 +542,8 @@ int main(int argc, char **argv )
fprintf( stdout, "nchan: %ld\n", nchan );

/* Find length of input file */
fseek( f_input, 0L, SEEK_END );
length_total = ftell( f_input ) / (2*nchan); /* 2 bytes per sample (16 bits), nchan channels */
if( (ftell( f_input ) % (2 * nchan)) != 0 )
length_total = audio_get_data_size( f_input ) / (2*nchan); /* 2 bytes per sample (16 bits), nchan channels */
if( (audio_get_data_size( f_input ) % (2 * nchan)) != 0 )
{
fprintf( stderr, "*** Number of samples not divisible into number of channels, exiting..\n" );
exit( -1 );
Expand All @@ -553,7 +553,7 @@ int main(int argc, char **argv )
fprintf( stderr, "*** Input file must be longer than 400 ms to use bs1770demo, exiting..\n" );
exit( -1 );
}
rewind( f_input );
audio_seek( f_input, 0L );
n_gating_blocks = 4 * (length_total - BLOCK_SIZE) / (BLOCK_SIZE);

/* Allocate input buffers */
Expand All @@ -572,7 +572,7 @@ int main(int argc, char **argv )
for( n = 0, j = -3; j < n_gating_blocks; n++, j++ )
{
/* Read next sub-block */
fread( input_short, sizeof( short ), STEP_SIZE * nchan, f_input );
fread( input_short, sizeof( short ), STEP_SIZE * nchan, f_input->fp );

deinterleave_short2double( input_short, input, STEP_SIZE * nchan, nchan );

Expand Down Expand Up @@ -610,16 +610,16 @@ int main(int argc, char **argv )
fac = find_scaling_factor( gating_block_energy, n_gating_blocks, lev_target, rms_flag, &lev_input, &lev_obtained );

/* Apply scaling */
rewind( f_input );
audio_seek( f_input, 0L );
length_total = 0;
clip = 0;
while( (length = (long)fread( input_short, sizeof( short ), STEP_SIZE * nchan, f_input ) ) )
while( (length = (long)fread( input_short, sizeof( short ), STEP_SIZE * nchan, f_input->fp ) ) )
{
deinterleave_short2double( input_short, input, STEP_SIZE * nchan, nchan );
scale(input, fac, input, STEP_SIZE * nchan );
clip += interleave_double2short( input, input_short, STEP_SIZE * nchan, nchan );
length_total += length / nchan;
fwrite( input_short, sizeof( short ), length, f_output );
fwrite( input_short, sizeof( short ), length, f_output->fp );
}

fprintf( stdout, "Input level: %.6f\n", lev_input );
Expand All @@ -633,7 +633,7 @@ int main(int argc, char **argv )
fprintf( stderr, "*** Warning: %ld samples clipped\n", clip );
}

fclose( f_output );
audio_close( f_output );
}
else
{
Expand Down Expand Up @@ -666,7 +666,7 @@ int main(int argc, char **argv )
}

/* Close files */
fclose( f_input );
audio_close( f_input );

/* Deallocate memory */
free( input );
Expand Down
2 changes: 1 addition & 1 deletion src/esdru/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include_directories(../utl)

add_executable(esdru esdru.c ../utl/ugst-utl.c)
add_executable(esdru esdru.c ../utl/ugst-utl.c ../utl/wav_io.c)
target_link_libraries(esdru ${M_LIBRARY})


Expand Down
26 changes: 15 additions & 11 deletions src/esdru/esdru.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <math.h>
#include <string.h>
#include "ugst-utl.h" /* for ran16_32c */
#include "wav_io.h"

#define LOCAL_PI 3.14159265358979323846

Expand Down Expand Up @@ -271,8 +272,8 @@ void apply_spatial_dist(

int main(int argc, char **argv )
{
FILE* f_input;
FILE* f_output;
AUDIO_FILE* f_input;
AUDIO_FILE* f_output;
FILE* f_energy;
char *input_filename;
char *output_filename;
Expand All @@ -286,6 +287,7 @@ int main(int argc, char **argv )
long step;
long length;
long fs;
int fs_given = 0;
long clip;
long i;
short energy_input;
Expand Down Expand Up @@ -315,6 +317,7 @@ int main(int argc, char **argv )
fprintf(stderr, "Invalid sampling frequency %s, exiting..\n", argv[i + 1] );
usage();
}
fs_given = 1;
i += 2;
}
else if( strcmp( argv[i], "-e_step" ) == 0 )
Expand Down Expand Up @@ -398,12 +401,14 @@ int main(int argc, char **argv )
}
input_filename = argv[i++];
output_filename = argv[i];
if( (f_input = fopen( input_filename, "rb" )) == NULL )
if( (f_input = audio_open_read (input_filename, fs_given ? fs : 0, 0, 16)) == NULL )
{
fprintf( stderr, "Could not open input file %s, exiting..\n\n", input_filename );
usage();
}
if( (f_output = fopen( output_filename, "wb" )) == NULL )
if (audio_get_sample_rate (f_input) > 0)
fs = audio_get_sample_rate (f_input);
if( (f_output = audio_open_write( output_filename, fs, 1, 16 )) == NULL )
{
fprintf( stderr, "Could not open output file %s, exiting..\n\n", output_filename );
usage();
Expand All @@ -421,13 +426,12 @@ int main(int argc, char **argv )
fseed = (float) intseed;

/* Load input file */
fseek( f_input, 0L, SEEK_END );
length = ftell( f_input ) / 4; /* 2 bytes per sample, 2 channels */
rewind( f_input );
length = audio_get_data_size( f_input ) / 4; /* 2 bytes per sample, 2 channels */
audio_seek( f_input, 0L );
input = malloc(sizeof(double) * length * 2);
input_short = malloc( sizeof( short ) * length * 2 );
m = malloc( sizeof( double ) * length );
fread( input_short, sizeof(short), length * 2, f_input);
fread( input_short, sizeof(short), length * 2, f_input->fp);
convert_short2double( input_short, input, length * 2);

step = (long) (1.5 * fs / 50.0);
Expand All @@ -437,7 +441,7 @@ int main(int argc, char **argv )

clip = convert_double2short(input, input_short, length * 2);

fwrite( input_short, sizeof( short ), length * 2, f_output );
fwrite( input_short, sizeof( short ), length * 2, f_output->fp );

fprintf( stdout, "--> Done processing %ld samples\n", length );
if (clip > 0)
Expand All @@ -449,8 +453,8 @@ int main(int argc, char **argv )
{
fclose( f_energy );
}
fclose( f_input );
fclose( f_output );
audio_close( f_input );
audio_close( f_output );
free( input );
free( input_short );
free( m );
Expand Down
4 changes: 2 additions & 2 deletions src/fir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ include_directories(../iir)
include_directories(../utl)


add_executable(filter filter.c fir-dsm.c fir-flat.c fir-irs.c fir-lib.c fir-pso.c fir-tia.c fir-hirs.c fir-wb.c fir-msin.c fir-LP.c ../iir/iir-lib.c ../iir/iir-g712.c ../iir/iir-dir.c ../iir/iir-flat.c ../utl/ugst-utl.c)
add_executable(filter filter.c fir-dsm.c fir-flat.c fir-irs.c fir-lib.c fir-pso.c fir-tia.c fir-hirs.c fir-wb.c fir-msin.c fir-LP.c ../iir/iir-lib.c ../iir/iir-g712.c ../iir/iir-dir.c ../iir/iir-flat.c ../utl/ugst-utl.c ../utl/wav_io.c)
target_link_libraries(filter ${M_LIBRARY})

add_executable(flt fltresp.c fir-dsm.c fir-flat.c fir-irs.c fir-lib.c fir-pso.c fir-tia.c fir-hirs.c fir-wb.c fir-msin.c fir-LP.c ../iir/iir-lib.c ../iir/iir-g712.c ../iir/iir-dir.c ../iir/iir-flat.c)
target_link_libraries(flt ${M_LIBRARY})

add_executable(firdemo firdemo.c fir-dsm.c fir-flat.c fir-irs.c fir-lib.c fir-pso.c fir-tia.c fir-hirs.c fir-wb.c fir-msin.c fir-LP.c ../iir/iir-lib.c ../iir/iir-g712.c ../iir/iir-dir.c ../iir/iir-flat.c ../utl/ugst-utl.c)
add_executable(firdemo firdemo.c fir-dsm.c fir-flat.c fir-irs.c fir-lib.c fir-pso.c fir-tia.c fir-hirs.c fir-wb.c fir-msin.c fir-LP.c ../iir/iir-lib.c ../iir/iir-g712.c ../iir/iir-dir.c ../iir/iir-flat.c ../utl/ugst-utl.c ../utl/wav_io.c)
target_link_libraries(firdemo ${M_LIBRARY})

#Test: FIR
Expand Down
Loading
Loading