288 lines
6.7 KiB
C
288 lines
6.7 KiB
C
/*
|
|
* ss_wav.c: routines for SpiceStream that handle the '.wav' files
|
|
*
|
|
* include LICENSE
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
// #include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
|
|
#include <spicestream.h>
|
|
#include <ss_wav.h>
|
|
|
|
#ifdef TRACE_MEM
|
|
#include <tracemem.h>
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
* Read spice-type file header - ".wav" format
|
|
*
|
|
* return 1 ok
|
|
* 0 EOF
|
|
* -1 Error
|
|
*/
|
|
int sf_rdhdr_wav( SpiceStream *ss )
|
|
{
|
|
int i;
|
|
char *line;
|
|
WaveHeader *hdr;
|
|
WaveChunkHeader *cf;
|
|
WaveFmtBody *fmt;
|
|
WaveChunkHeader *cd;
|
|
int rate;
|
|
char signam[16];
|
|
size_t length;
|
|
|
|
if ( fdbuf_read(ss->linebuf, sizeof(WaveHeader) ) != sizeof(WaveHeader) ) {
|
|
return 0;
|
|
}
|
|
line = ((DBuf *) ss->linebuf)->s;
|
|
hdr = (WaveHeader *) line;
|
|
if (hdr->magic != WAV_RIFF || hdr->type != WAV_WAVE ) {
|
|
msg_dbg("not a wav file (bad magic number)");
|
|
return -1;
|
|
}
|
|
|
|
if ( fdbuf_read(ss->linebuf, sizeof(WaveChunkHeader) ) != sizeof(WaveChunkHeader) ) {
|
|
return 0;
|
|
}
|
|
line = ((DBuf *) ss->linebuf)->s;
|
|
cf = (WaveChunkHeader *) line;
|
|
|
|
if (cf->type != WAV_FMT ) {
|
|
msg_dbg("not a wav file (expecting a WAV_FMT record)");
|
|
return -1;
|
|
}
|
|
|
|
if ( fdbuf_read(ss->linebuf, sizeof(WaveFmtBody) ) != sizeof(WaveFmtBody) ) {
|
|
return 0;
|
|
}
|
|
line = ((DBuf *) ss->linebuf)->s;
|
|
fmt = (WaveFmtBody *) line;
|
|
|
|
ss->ndv = le16_to_cpu(fmt->channels);
|
|
rate = le32_to_cpu(fmt->sample_fq);
|
|
ss->bps = le16_to_cpu(fmt->bit_p_spl); /* storing bits per sample in bps */
|
|
|
|
/* Data chunk header */
|
|
if ( fdbuf_read(ss->linebuf, sizeof(WaveChunkHeader) ) != sizeof(WaveChunkHeader) ) {
|
|
return 0;
|
|
}
|
|
line = ((DBuf *) ss->linebuf)->s;
|
|
cd = (WaveChunkHeader *) line;
|
|
if (cd->type != WAV_DATA ) {
|
|
msg_dbg("not a wav file (expecting a WAV_DATA record)");
|
|
return -1;
|
|
}
|
|
length = le32_to_cpu(cd->length);
|
|
|
|
/* independant variable */
|
|
dataset_var_add ( ss->wds, "time", TIME, 1 );
|
|
ss->ncols = 1;
|
|
|
|
for (i = 0; i < ss->ndv; i++) {
|
|
sprintf( signam, _("channel_%d"), i );
|
|
dataset_var_add( ss->wds, signam, VOLTAGE, 1 );
|
|
ss->ncols++;
|
|
}
|
|
ss->expected_vals = length * 8 / ss->bps ;
|
|
ss->nrows = ss->expected_vals / ss->ndv;
|
|
ss->read_rows = 0;
|
|
|
|
sprintf( signam, _("wav@%d"), rate );
|
|
dataset_dup_setname(ss->wds, signam);
|
|
msg_dbg("Wav \"%s\" expecting %d vals, %d rows", signam,
|
|
ss->expected_vals, ss->nrows );
|
|
msg_dbg("done with header at offset=0x%lx", (long) fdbuf_tello(ss->linebuf) );
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read row of values from a spice2 rawfile
|
|
*/
|
|
int sf_readrow_wav(SpiceStream *ss )
|
|
{
|
|
int i;
|
|
int ret;
|
|
double val;
|
|
char *line;
|
|
int bits = ss->bps ;
|
|
int bytes = bits / 8 ;
|
|
|
|
/* independent var */
|
|
dataset_val_add( ss->wds, (double) ss->read_rows + 1);
|
|
|
|
/* dependent vars */
|
|
for (i = 0; i < ss->ndv; i++) {
|
|
if ( ( ret = fdbuf_read(ss->linebuf, bytes )) != bytes ) {
|
|
msg_error(_("unexpected EOF at dvar %d"), i);
|
|
return -1;
|
|
}
|
|
line = ((DBuf *) ss->linebuf)->s;
|
|
switch (bits) {
|
|
case 8: {
|
|
u_char *uval = (u_char *) line;
|
|
val = (double) *uval;
|
|
break;
|
|
}
|
|
case 16: {
|
|
short *sval = (short *) line;
|
|
val = (double) le16_to_cpu(*sval);
|
|
break;
|
|
}
|
|
case 32: {
|
|
int *ival = (int *) line;
|
|
val = (double) le32_to_cpu(*ival);
|
|
break;
|
|
}
|
|
default:
|
|
val = 0;
|
|
}
|
|
dataset_val_add( ss->wds, val );
|
|
}
|
|
ss->read_rows++;
|
|
if ( ss->read_rows >= ss->nrows ) {
|
|
return -2;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* write a WAVE-header */
|
|
static int sf_write_wav_header(FILE *fd, WDataSet *wds, int bits, int fmt_float, int rate )
|
|
{
|
|
WaveHeader hdr;
|
|
WaveFmtBody fmt;
|
|
WaveChunkHeader chunkf;
|
|
WaveChunkHeader chunkd;
|
|
u_int tmp;
|
|
u_short tmp2;
|
|
|
|
hdr.magic = WAV_RIFF;
|
|
/* len start after length field of WaveHeader */
|
|
tmp = sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody)
|
|
+ sizeof(WaveChunkHeader) - 8;
|
|
hdr.length = cpu_to_le16(tmp);
|
|
hdr.type = WAV_WAVE;
|
|
|
|
chunkf.type = WAV_FMT;
|
|
chunkf.length = cpu_to_le32(sizeof(WaveFmtBody));
|
|
|
|
if ( fmt_float ) {
|
|
fmt.format = cpu_to_le16(WAV_FMT_IEEE_FLOAT);
|
|
} else {
|
|
fmt.format = cpu_to_le16(WAV_FMT_PCM);
|
|
}
|
|
fmt.channels = cpu_to_le16(wds->ncols - 1);
|
|
fmt.sample_fq = cpu_to_le32(rate);
|
|
|
|
tmp2 = (wds->ncols - 1) * bits / 8;
|
|
fmt.byte_p_spl = cpu_to_le16(tmp2);
|
|
tmp = (u_int) tmp2 * rate;
|
|
|
|
fmt.byte_p_sec = cpu_to_le32(tmp);
|
|
fmt.bit_p_spl = cpu_to_le16(bits);
|
|
|
|
chunkd.type = WAV_DATA;
|
|
chunkd.length = 0;
|
|
|
|
if (fwrite( &hdr, sizeof(WaveHeader), 1, fd) != 1 ||
|
|
fwrite( &chunkf, sizeof(WaveChunkHeader), 1, fd) != 1 ||
|
|
fwrite( &fmt, sizeof(WaveFmtBody), 1, fd) != 1 ||
|
|
fwrite( &chunkd, sizeof(WaveChunkHeader), 1, fd) != 1 ) {
|
|
msg_error(_("Wav write error"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void sf_write_wav_end(FILE *fd, size_t nWritten)
|
|
{
|
|
WaveChunkHeader cd;
|
|
off_t length_seek;
|
|
off_t filelen;
|
|
u_int rifflen;
|
|
|
|
filelen = nWritten + 2 * sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4;
|
|
rifflen = filelen > 0x7fffffff ? cpu_to_le32(0x7fffffff) : cpu_to_le32(filelen);
|
|
fseeko(fd, 4, SEEK_SET) ;
|
|
fwrite( &rifflen, 4, 1, fd);
|
|
|
|
length_seek = sizeof(WaveHeader) + sizeof(WaveChunkHeader) +
|
|
sizeof(WaveFmtBody);
|
|
cd.type = WAV_DATA;
|
|
cd.length = nWritten > 0x7fffffff ? cpu_to_le32(0x7fffffff) : cpu_to_le32(nWritten);
|
|
fseeko(fd, length_seek, SEEK_SET);
|
|
fwrite( &cd, sizeof(WaveChunkHeader), 1, fd);
|
|
}
|
|
|
|
void sf_write_file_wav( FILE *fd, WaveTable *wt, char *fmt)
|
|
{
|
|
int i;
|
|
int j;
|
|
int k = 0;
|
|
WDataSet *wds;
|
|
int fmt_float;
|
|
int bits;
|
|
size_t nWritten;
|
|
int rate;
|
|
|
|
while ( (wds = wavetable_get_dataset( wt, k++)) ){
|
|
fmt_float = 0;
|
|
nWritten = 0;
|
|
|
|
bits = wt->bits;
|
|
rate = wt->rate;
|
|
if ( wt->bits > 32 ) {
|
|
bits &= 0x2f;
|
|
fmt_float = 1;
|
|
}
|
|
if (sf_write_wav_header( fd, wds, bits, fmt_float, rate ) ) {
|
|
return;
|
|
}
|
|
|
|
for ( i = 0 ; i < wds->nrows ; i++ ){
|
|
for ( j = 0 ; j < wds->ncols ; j++ ){
|
|
if ( j == 0 ) {
|
|
continue;
|
|
}
|
|
double val = dataset_val_get( wds, i, j );
|
|
switch (bits) {
|
|
case 8: {
|
|
u_char uval = (u_char) val;
|
|
fwrite( &uval, sizeof(u_char), 1, fd);
|
|
nWritten += sizeof(u_char);
|
|
break;
|
|
}
|
|
case 16: {
|
|
short sval = cpu_to_le16( (short) val);
|
|
fwrite( &sval, sizeof(short), 1, fd);
|
|
nWritten += sizeof(short);
|
|
break;
|
|
}
|
|
case 32: {
|
|
int ival = cpu_to_le32( (int) val);
|
|
fwrite( &ival, sizeof(int), 1, fd);
|
|
nWritten += sizeof(int);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
sf_write_wav_end ( fd, nWritten ) ;
|
|
}
|
|
}
|