💾 Archived View for uscoffings.net › retro-computing › systems › TI994a › x99tape › x99tape_src › wa… captured on 2022-06-04 at 01:07:43.
View Raw
More Information
-=-=-=-=-=-=-
/*
Portable TI99 tape encoder/decoder
wave.c: Code to read and write PCM WAVE files
Raphael Nabet 2002
#include <math.h>
#include <stdio.h>
#include "common.h"
#include "my_int.h"
#include "wave.h"
/* typedef for riff 32-bit tag, intel word (16 bits) and intel dword (32 bits) */
typedef uint_fast32_t riff_tag_t;
typedef uint_fast16_t intel_word_t;
typedef uint_fast32_t intel_dword_t;
/* defines for supported tags in WAVE RIFF file */
enum
{
tag_RIFF = 0x52494646UL, /* 'RIFF' */
tag_WAVE = 0x57415645UL, /* 'WAVE' */
tag_fmt = 0x666D7420UL, /* 'fmt ' */
tag_data = 0x64617461UL /* 'data' */
};
/* static prototypes */
static error_t read_wave_intel_word(FILE *handle, intel_word_t *reply);
static error_t read_wave_intel_dword(FILE *handle, intel_dword_t *reply);
static error_t read_wave_tag(FILE *handle, riff_tag_t *reply);
/* set to 1 to enable a simple robustness test: add random white noise to signal */
#define TEST 0
#if TEST
#include <stdlib.h>
static const double noise_level = .35;
#if 1
static double my_rand(void)
{
return (rand()*2-RAND_MAX)/(double)RAND_MAX;
}
#else
static double my_rand(void)
{
double x, y, norm_2;
do
{
x = (rand()*2-RAND_MAX)/(double)RAND_MAX;
y = (rand()*2-RAND_MAX)/(double)RAND_MAX;
norm_2 = x*x + y*y;
}
while (norm_2 == 0.) && (norm_2 > 1.);
}
#endif
#endif
/*
Parse WAVE header and fill the wave_file structure accordingly, so that
read_wave_sample() and seek_wave_sample() can be called afterwards.
handle: stdc stream pointing to start of WAVE file
wave_file: pointer to the wave_file_t to fill
Returns error code (no_error or incorrect_parameters, bad_wave_format,
unsupported_wave_format)
error_t open_wave_in(FILE *handle, wave_file_in_t *wave_file)
{
error_t error;
riff_tag_t tag;
intel_word_t word;
intel_dword_t dword;
unsigned long field_len;
/* set up handle */
if (! handle)
return invalid_parameters;
wave_file->handle = handle;
/* read RIFF header*/
/* read 'RIFF' tag */
if ((error = read_wave_tag(handle, & tag)) != no_error)
return error;
if (tag != tag_RIFF)
return bad_wave_format;
/* read total len field */
if ((error = read_wave_intel_dword(handle, & dword)) != no_error)
return error;
/*wave_file->len = dword;*/
/* read type tag ('WAVE') */
if ((error = read_wave_tag(handle, & tag)) != no_error)
return error;
if (tag != tag_WAVE)
return bad_wave_format;
/* now read variable number of tagged fields */
do
{
/* read field tag */
if ((error = read_wave_tag(handle, & tag)) != no_error)
return error;
/* read field len */
if ((error = read_wave_intel_dword(handle, & dword)) != no_error)
return error;
field_len = dword;
switch (tag)
{
case tag_fmt: /* fmt field */
/* Check field length once and for all. Length should be at least 14 bytes in
the general case, and 16 in the case of PCM encoding. */
if (field_len < 16)
return (field_len < 14) ? bad_wave_format : unsupported_wave_format;
/***Fields common to all encoding***/
/* read format tag */
if ((error = read_wave_intel_word(handle, & word)) != no_error)
return error;
if (word != 1)
return unsupported_wave_format; /* only PCM is supported */
/*wave_file->formatTag = word;*/
/* read number of channels */
if ((error = read_wave_intel_word(handle, & word)) != no_error)
return error;
wave_file->channels = word;
/* read sampling rate */
if ((error = read_wave_intel_dword(handle, & dword)) != no_error)
return error;
wave_file->samplesPerSec = dword;
/* read byte rate */
if ((error = read_wave_intel_dword(handle, & dword)) != no_error)
return error;
/*wave_file->avgBytesPerSec = dword;*/
/* read block alignment */
if ((error = read_wave_intel_word(handle, & word)) != no_error)
return error;
wave_file->blockAlign = word;
/***Field specific to PCM***/
/* read bits per sample */
if ((error = read_wave_intel_word(handle, & word)) != no_error)
return error;
wave_file->bitsPerSample = word;
/* seek past end of field if necessary */
if (field_len>16)
fseek(handle, field_len-16, SEEK_CUR);
/* Compute a few constants */
if (wave_file->blockAlign % wave_file->channels)
return bad_wave_format; /* This is what Microsoft says */
wave_file->sample_padding = (wave_file->blockAlign/wave_file->channels) - ((wave_file->bitsPerSample+7) >> 3);
wave_file->multiplier = 1. / (pow(2, wave_file->bitsPerSample-1) * wave_file->channels);
break;
case tag_data: /* data field: wave data follows */
/*wave_file->data_len = field_len;*/
wave_file->sample_len = field_len / wave_file->blockAlign;
wave_file->sample_pos = 0; /* currently pointing to first sample in file */
wave_file->data_offset = ftell(handle); /* remember data offset in file */
return no_error; /* only way we can exit OK from function */
default: /* unknown */
/* Well, we don't mind extra fields, let's just skip it */
fseek(handle, field_len, SEEK_CUR);
break;
}
} while (1);
}
/*
read_wave_sample: read a sample from a wave_file_in_t
wave_file: wave_file structure
reply: sample (normalized to fit in [-1,1])
Returns error code (no_error, eof_error or read_error)
error_t read_wave_sample(wave_file_in_t *wave_file, float *reply)
{
int bit;
int channel;
int ch;
intmax_t sample_buf;
float buf;
/* check we have not reached end of data field */
if (wave_file->sample_pos >= wave_file->sample_len)
return eof_error;
buf = 0;
for (channel=0; channel<wave_file->channels; channel++)
{
sample_buf = 0;
/* skip pad bytes */
if (wave_file->sample_padding)
fseek(wave_file->handle, wave_file->sample_padding, SEEK_CUR);
/* read ceil(wave_file->bitsPerSample/8) bits */
for (bit=0; bit<wave_file->bitsPerSample; bit+=8)
{
ch = getc(wave_file->handle);
if (ch == EOF)
return read_error;
sample_buf |= ((uintmax_t) (ch & 0xff)) << bit;
}
/* shift out undefined bits */
if (bit > wave_file->bitsPerSample)
sample_buf >>= bit-wave_file->bitsPerSample;
if (wave_file->bitsPerSample <= 8)
/* convert to signed if unsigned */
sample_buf -= 1 << (wave_file->bitsPerSample-1);
else
{
/* extend sign bit */
if (sample_buf & ((intmax_t)1 << (wave_file->bitsPerSample-1)))
sample_buf |= ~ (((intmax_t)1 << wave_file->bitsPerSample)-1);
}
/* mix with previous channels */
buf += sample_buf;
}
/* normalize and save reply */
*reply = buf*wave_file->multiplier;
#if TEST
/* add white noise (debug test) */
*reply = *reply*(1-noise_level)+noise_level*my_rand();
#endif
wave_file->sample_pos++;
return no_error;
}
/*
seek_wave_sample: seek to a given sample in a wave_file
wave_file: wave_file structure
sample_position: sample index
Returns error code (always no_error)
/*error_t seek_wave_sample(wave_file_in_t *wave_file, uint_fast32_t sample_pos)
{
fseek(wave_file->handle, wave_file->data_offset+sample_pos*wave_file->blockAlign, SEEK_SET);
wave_file->sample_pos = sample_pos;
return no_error;
}*/
/*
Read one INTEL word (little-endian 16-bit word)
static error_t read_wave_intel_word(FILE *handle, intel_word_t *reply)
{
unsigned char buf[2];
if (fread(buf, 1, 2, handle) != 2)
return read_error;
*reply = (buf[0] & 0xff) | ((buf[1] & 0xff) << 8);
return no_error;
}
/*
Read one INTEL double word (little-endian 32-bit word)
static error_t read_wave_intel_dword(FILE *handle, intel_dword_t *reply)
{
unsigned char buf[4];
if (fread(buf, 1, 4, handle) != 4)
return read_error;
*reply = (buf[0] & 0xff) | ((buf[1] & 0xff) << 8) | ((buf[2] & 0xff) << 16) | ((buf[3] & 0xff) << 24);
return no_error;
}
/*
Read one RIFF tag (4 byte-long tag)
static error_t read_wave_tag(FILE *handle, riff_tag_t *reply)
{
unsigned char buf[4];
if (fread(buf, 1, 4, handle) != 4)
return read_error;
*reply = ((buf[0] & 0xff) << 24) | ((buf[1] & 0xff) << 16) | ((buf[2] & 0xff) << 8) | (buf[3] & 0xff);
return no_error;
}
/* static prototypes */
static error_t write_wave_intel_word(FILE *handle, intel_word_t data);
static error_t write_wave_intel_dword(FILE *handle, intel_dword_t data);
static error_t write_wave_tag(FILE *handle, riff_tag_t data);
/*
Parse WAVE header and fill the wave_file structure accordingly, so that
write_wave_sample() can be called afterwards.
handle: stdc stream pointing to start of WAVE file
wave_file: pointer to the wave_file_t to fill
Returns error code (no_error or incorrect_parameters, bad_wave_format,
unsupported_wave_format)
error_t open_wave_out(FILE *handle, uint_fast32_t samplesPerSec, wave_file_out_t *wave_file)
{
error_t error;
/* set up handle */
if (! handle)
return invalid_parameters;
wave_file->handle = handle;
/* write RIFF header*/
/* read 'RIFF' tag */
if ((error = write_wave_tag(handle, tag_RIFF)) != no_error)
return error;
/* skip total len field */
if ((error = write_wave_intel_dword(handle, 0)) != no_error)
return error;
/*wave_file->len = dword;*/
/* write type tag ('WAVE') */
if ((error = write_wave_tag(handle, tag_WAVE)) != no_error)
return error;
/* write fmt field */
/* write field tag */
if ((error = write_wave_tag(handle, tag_fmt)) != no_error)
return error;
/* write field len */
if ((error = write_wave_intel_dword(handle, 16)) != no_error)
return error;
/***Fields common to all encoding***/
/* write format tag */
if ((error = write_wave_intel_word(handle, 1)) != no_error) /* only PCM is supported */
return error;
/*wave_file->formatTag = 1;*/
/* write number of channels */
if ((error = write_wave_intel_word(handle, 1)) != no_error)
return error;
/*wave_file->channels = 1;*/
/* write sampling rate */
if ((error = write_wave_intel_dword(handle, samplesPerSec)) != no_error)
return error;
wave_file->samplesPerSec = samplesPerSec;
/* write byte rate */
if ((error = write_wave_intel_dword(handle, samplesPerSec/**channels*block_size*/)) != no_error)
return error;
/*wave_file->avgBytesPerSec = ...;*/
/* write block alignment */
if ((error = write_wave_intel_word(handle, 1)) != no_error)
return error;
/*wave_file->blockAlign = 1;*/
/***Field specific to PCM***/
/* write bits per sample */
if ((error = write_wave_intel_word(handle, /*2*/8)) != no_error)
return error;
/*wave_file->bitsPerSample = 2;*/
/* write data field */
/* write field tag */
if ((error = write_wave_tag(handle, tag_data)) != no_error)
return error;
/* skip field len */
if ((error = write_wave_intel_dword(handle, 0)) != no_error)
return error;
wave_file->sample_pos = 0;
wave_file->data_offset = ftell(handle);
return no_error;
}
/*
Close WAVE file
We need to write the lenght of the data field and the total lenght of the file.
error_t close_wave_out(wave_file_out_t *wave_file)
{
uint_fast32_t file_len = ftell(wave_file->handle);
/* write len of data field */
fseek(wave_file->handle, wave_file->data_offset-4, SEEK_SET);
write_wave_intel_dword(wave_file->handle, (file_len-wave_file->data_offset));
/* write len of file */
fseek(wave_file->handle, 4, SEEK_SET);
write_wave_intel_dword(wave_file->handle, file_len-8);
return no_error;
}
/*
write_wave_sample: write a sample to a wave_file_out_t
wave_file: wave_file structure
data: sample (should be -1 or +1)
Returns error code (no_error or write_error)
error_t write_wave_sample(wave_file_out_t *wave_file, int data)
{
int ch;
ch = putc(data > 0 ? 0xC0 : 0x40, wave_file->handle);
if (ch == EOF)
return write_error;
return no_error;
}
/*
Write one INTEL word (little-endian 16-bit word)
static error_t write_wave_intel_word(FILE *handle, intel_word_t data)
{
unsigned char buf[2];
buf[0] = data & 0xff;
buf[1] = (data >> 8) & 0xff;
if (fwrite(buf, 1, 2, handle) != 2)
return write_error;
return no_error;
}
/*
Write one INTEL double word (little-endian 32-bit word)
static error_t write_wave_intel_dword(FILE *handle, intel_dword_t data)
{
unsigned char buf[4];
buf[0] = data & 0xff;
buf[1] = (data >> 8) & 0xff;
buf[2] = (data >> 16) & 0xff;
buf[3] = (data >> 24) & 0xff;
if (fwrite(buf, 1, 4, handle) != 4)
return write_error;
return no_error;
}
/*
Write one RIFF tag (4 byte-long tag)
static error_t write_wave_tag(FILE *handle, riff_tag_t data)
{
unsigned char buf[4];
buf[0] = (data >> 24) & 0xff;
buf[1] = (data >> 16) & 0xff;
buf[2] = (data >> 8) & 0xff;
buf[3] = data & 0xff;
if (fwrite(buf, 1, 4, handle) != 4)
return write_error;
return no_error;
}