This is wav.c in view mode; [Download] [Up]
/*
* April 15, 1992
* Copyright 1992 Rick Richardson
* Copyright 1991 Lance Norskog And Sundry Contributors
* This source code is freely redistributable and may be used for
* any purpose. This copyright notice must be maintained.
* Lance Norskog And Sundry Contributors are not responsible for
* the consequences of using this software.
*/
/*
* Windows 3.0 .wav format driver
*/
/*
* Fixed by various contributors:
* 1) Little-endian handling
* 2) Skip other kinds of file data
* 3) Handle 16-bit formats correctly
* 4) Not go into infinite loop
*/
/*
* User options should overide file
* header :- we assumed user knows what
* they are doing if they specify options.
* Enhancements and clean up
* by Graeme W. Gill, 93/5/17
*/
#include "st.h"
#include "wav.h"
/* Private data for .wav file */
typedef struct wavstuff {
long samples;
int second_header; /* non-zero on second header write */
} *wav_t;
static char *wav_format_str();
/*
* Do anything required before you start reading samples.
* Read file header.
* Find out sampling rate,
* size and style of samples,
* mono/stereo/quad.
*/
wavstartread(ft)
ft_t ft;
{
wav_t wav = (wav_t) ft->priv;
char magic[4];
unsigned len;
int littlendian = 1;
char *endptr;
char c;
/* wave file characteristics */
unsigned short wFormatTag; /* data format */
unsigned short wChannels; /* number of channels */
unsigned long wSamplesPerSecond; /* samples per second per channel */
unsigned long wAvgBytesPerSec; /* estimate of bytes per second needed */
unsigned short wBlockAlign; /* byte alignment of a basic sample block */
unsigned short wBitsPerSample; /* bits per sample */
unsigned long data_length; /* length of sound data in bytes */
unsigned long bytespersample; /* bytes per sample (per channel) */
endptr = (char *) &littlendian;
if (!*endptr) ft->swap = 1;
/* If you need to seek around the input file. */
if (0 && ! ft->seekable)
fail("Sorry, .wav input file must be a file, not a pipe");
if ( fread(magic, 1, 4, ft->fp) != 4
|| strncmp("RIFF", magic, 4))
fail("Sorry, not a RIFF file");
len = rllong(ft);
if ( fread(magic, 1, 4, ft->fp) != 4
|| strncmp("WAVE", magic, 4))
fail("Sorry, not a WAVE file");
/* Now look for the format chunk */
for (;;)
{
if ( fread(magic, 1, 4, ft->fp) != 4 )
fail("Sorry, missing fmt spec");
len = rllong(ft);
if (strncmp("fmt ", magic, 4) == 0)
break; /* Found the format chunk */
while (len > 0 && !feof(ft->fp)) /* skip to next chunk */
{
getc(ft->fp);
len--;
}
}
if ( len < 16 )
fail("Sorry, fmt chunk is too short");
wFormatTag = rlshort(ft);
switch (wFormatTag)
{
case WAVE_FORMAT_UNKNOWN:
fail("Sorry, this WAV file is in Microsoft Official Unknown format.");
case WAVE_FORMAT_PCM: /* this one, at least, I can handle */
if (ft->info.style != -1 && ft->info.style != UNSIGNED && ft->info.style != SIGN2)
warn("User options overiding style read in .wav header");
break;
case WAVE_FORMAT_ADPCM:
fail("Sorry, this WAV file is in Microsoft ADPCM format.");
case WAVE_FORMAT_ALAW: /* Think I can handle this */
if (ft->info.style == -1 || ft->info.style == ALAW)
ft->info.style = ALAW;
else
warn("User options overiding style read in .wav header");
break;
case WAVE_FORMAT_MULAW: /* Think I can handle this */
if (ft->info.style == -1 || ft->info.style == ULAW)
ft->info.style = ULAW;
else
warn("User options overiding style read in .wav header");
break;
case WAVE_FORMAT_OKI_ADPCM:
fail("Sorry, this WAV file is in OKI ADPCM format.");
case WAVE_FORMAT_DIGISTD:
fail("Sorry, this WAV file is in Digistd format.");
case WAVE_FORMAT_DIGIFIX:
fail("Sorry, this WAV file is in Digifix format.");
case IBM_FORMAT_MULAW:
fail("Sorry, this WAV file is in IBM U-law format.");
case IBM_FORMAT_ALAW:
fail("Sorry, this WAV file is in IBM A-law format.");
case IBM_FORMAT_ADPCM:
fail("Sorry, this WAV file is in IBM ADPCM format.");
default: fail("Sorry, don't understand format");
}
wChannels = rlshort(ft);
/* User options take precedence */
if (ft->info.channels == -1 || ft->info.channels == wChannels)
ft->info.channels = wChannels;
else
warn("User options overiding channels read in .wav header");
wSamplesPerSecond = rllong(ft);
if (ft->info.rate == 0 || ft->info.rate == wSamplesPerSecond)
ft->info.rate = wSamplesPerSecond;
else
warn("User options overiding rate read in .wav header");
wAvgBytesPerSec = rllong(ft); /* Average bytes/second */
wBlockAlign = rlshort(ft); /* Block align */
wBitsPerSample = rlshort(ft); /* bits per sample per channel */
bytespersample = (wBitsPerSample + 7)/8;
switch (bytespersample)
{
case 1:
/* User options take precedence */
if (ft->info.size == -1 || ft->info.size == BYTE)
ft->info.size = BYTE;
else
warn("User options overiding size read in .wav header");
if (ft->info.style == -1 || ft->info.style == UNSIGNED)
ft->info.style = UNSIGNED;
else if (ft->info.style != ALAW && ft->info.style != ULAW)
warn("User options overiding style read in .wav header");
break;
case 2:
if (ft->info.size == -1 || ft->info.size == WORD)
ft->info.size = WORD;
else
warn("User options overiding size read in .wav header");
if (ft->info.style == -1 || ft->info.style == SIGN2)
ft->info.style = SIGN2;
else
warn("User options overiding style read in .wav header");
break;
case 4:
if (ft->info.size == -1 || ft->info.size == LONG)
ft->info.size = LONG;
else
warn("User options overiding size read in .wav header");
if (ft->info.style == -1 || ft->info.style == SIGN2)
ft->info.style = SIGN2;
else
warn("User options overiding style read in .wav header");
break;
default:
fail("Sorry, don't understand .wav size");
}
len -= 16;
while (len > 0 && !feof(ft->fp))
{
getc(ft->fp);
len--;
}
/* Now look for the wave data chunk */
for (;;)
{
if ( fread(magic, 1, 4, ft->fp) != 4 )
fail("Sorry, missing data chunk");
len = rllong(ft);
if (strncmp("data", magic, 4) == 0)
break; /* Found the data chunk */
while (len > 0 && !feof(ft->fp)) /* skip to next chunk */
{
getc(ft->fp);
len--;
}
}
data_length = len;
wav->samples = data_length/ft->info.size; /* total samples */
report("Reading Wave file: %s format, %d channel%s, %d samp/sec",
wav_format_str(wFormatTag), wChannels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
report(" %d byte/sec, %d block align, %d bits/samp, %u data bytes\n",
wAvgBytesPerSec, wBlockAlign, wBitsPerSample, data_length);
}
/*
* Read up to len samples from file.
* Convert to signed longs.
* Place in buf[].
* Return number of samples read.
*/
wavread(ft, buf, len)
ft_t ft;
long *buf, len;
{
wav_t wav = (wav_t) ft->priv;
int done;
if (len > wav->samples) len = wav->samples;
if (len == 0)
return 0;
done = rawread(ft, buf, len);
if (done == 0)
warn("Premature EOF on .wav input file");
wav->samples -= done;
return done;
}
/*
* Do anything required when you stop reading samples.
* Don't close input file!
*/
wavstopread(ft)
ft_t ft;
{
}
wavstartwrite(ft)
ft_t ft;
{
wav_t wav = (wav_t) ft->priv;
int littlendian = 1;
char *endptr;
endptr = (char *) &littlendian;
if (!*endptr) ft->swap = 1;
wav->samples = 0;
wav->second_header = 0;
if (! ft->seekable)
warn("Length in output .wav header will wrong since can't seek to fix it");
wavwritehdr(ft);
}
wavwritehdr(ft)
ft_t ft;
{
wav_t wav = (wav_t) ft->priv;
/* wave file characteristics */
unsigned short wFormatTag; /* data format */
unsigned short wChannels; /* number of channels */
unsigned long wSamplesPerSecond; /* samples per second per channel */
unsigned long wAvgBytesPerSec; /* estimate of bytes per second needed */
unsigned short wBlockAlign; /* byte alignment of a basic sample block */
unsigned short wBitsPerSample; /* bits per sample */
unsigned long data_length; /* length of sound data in bytes */
unsigned long bytespersample; /* bytes per sample (per channel) */
switch (ft->info.size)
{
case BYTE:
wBitsPerSample = 8;
if (ft->info.style == -1 || ft->info.style == UNSIGNED)
ft->info.style = UNSIGNED;
else if (!wav->second_header && ft->info.style != ALAW && ft->info.style != ULAW)
warn("User options overiding style written to .wav header");
break;
case WORD:
wBitsPerSample = 16;
if (ft->info.style == -1 || ft->info.style == SIGN2)
ft->info.style = SIGN2;
else if (!wav->second_header)
warn("User options overiding style written to .wav header");
break;
case LONG:
wBitsPerSample = 32;
if (ft->info.style == -1 || ft->info.style == SIGN2)
ft->info.style = SIGN2;
else if (!wav->second_header)
warn("User options overiding style written to .wav header");
break;
default:
wBitsPerSample = 32;
if (ft->info.style == -1)
ft->info.style = SIGN2;
if (!wav->second_header)
warn("Warning - writing bad .wav file using %s",sizes[ft->info.size]);
break;
}
switch (ft->info.style)
{
case UNSIGNED:
wFormatTag = WAVE_FORMAT_PCM;
if (wBitsPerSample != 8 && !wav->second_header)
warn("Warning - writing bad .wav file using unsigned data and %d bits/sample",wBitsPerSample);
break;
case SIGN2:
wFormatTag = WAVE_FORMAT_PCM;
if (wBitsPerSample == 8 && !wav->second_header)
warn("Warning - writing bad .wav file using signed data and %d bits/sample",wBitsPerSample);
break;
case ALAW:
wFormatTag = WAVE_FORMAT_ALAW;
if (wBitsPerSample != 8 && !wav->second_header)
warn("Warning - writing bad .wav file using A-law data and %d bits/sample",wBitsPerSample);
break;
case ULAW:
wFormatTag = WAVE_FORMAT_MULAW;
if (wBitsPerSample != 8 && !wav->second_header)
warn("Warning - writing bad .wav file using U-law data and %d bits/sample",wBitsPerSample);
break;
}
wSamplesPerSecond = ft->info.rate;
bytespersample = (wBitsPerSample + 7)/8;
wAvgBytesPerSec = ft->info.rate * ft->info.channels * bytespersample;
wChannels = ft->info.channels;
wBlockAlign = ft->info.channels * bytespersample;
if (!wav->second_header) /* use max length value first time */
data_length = 0x7fffffff - (8+16+12);
else /* fixup with real length */
data_length = bytespersample * wav->samples;
/* figured out header info, so write it */
fputs("RIFF", ft->fp);
wllong(ft, data_length + 8+16+12); /* Waveform chunk size: FIXUP(4) */
fputs("WAVE", ft->fp);
fputs("fmt ", ft->fp);
wllong(ft, (long)16); /* fmt chunk size */
wlshort(ft, wFormatTag);
wlshort(ft, wChannels);
wllong(ft, wSamplesPerSecond);
wllong(ft, wAvgBytesPerSec);
wlshort(ft, wBlockAlign);
wlshort(ft, wBitsPerSample);
fputs("data", ft->fp);
wllong(ft, data_length); /* data chunk size: FIXUP(40) */
if (!wav->second_header) {
report("Writing Wave file: %s format, %d channel%s, %d samp/sec",
wav_format_str(wFormatTag), wChannels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
report(" %d byte/sec, %d block align, %d bits/samp",
wAvgBytesPerSec, wBlockAlign, wBitsPerSample);
} else
report("Finished writing Wave file, %u data bytes\n",data_length);
}
wavwrite(ft, buf, len)
ft_t ft;
long *buf, len;
{
wav_t wav = (wav_t) ft->priv;
wav->samples += len;
rawwrite(ft, buf, len);
}
void
wavstopwrite(ft)
ft_t ft;
{
/* All samples are already written out. */
/* If file header needs fixing up, for example it needs the */
/* the number of samples in a field, seek back and write them here. */
if (!ft->seekable)
return;
if (fseek(ft->fp, 0L, 0) != 0)
fail("Sorry, can't rewind output file to rewrite .wav header.");
((wav_t) ft->priv)->second_header = 1;
wavwritehdr(ft);
}
/*
* Return a string corresponding to the wave format type.
*/
static char *
wav_format_str(wFormatTag)
unsigned wFormatTag;
{
switch (wFormatTag)
{
case WAVE_FORMAT_UNKNOWN:
return "Microsoft Official Unknown";
case WAVE_FORMAT_PCM:
return "Microsoft PCM";
case WAVE_FORMAT_ADPCM:
return "Microsoft ADPCM";
case WAVE_FORMAT_ALAW:
return "Microsoft A-law";
case WAVE_FORMAT_MULAW:
return "Microsoft U-law";
case WAVE_FORMAT_OKI_ADPCM:
return "OKI ADPCM format.";
case WAVE_FORMAT_DIGISTD:
return "Digistd format.";
case WAVE_FORMAT_DIGIFIX:
return "Digifix format.";
case IBM_FORMAT_MULAW:
return "IBM U-law format.";
case IBM_FORMAT_ALAW:
return "IBM A-law";
case IBM_FORMAT_ADPCM:
return "IBM ADPCM";
default:
return "Unknown";
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.