/*
Audio File Library
Copyright (C) 1998-2000, Michael Pruett <michael@68k.org>
Copyright (C) 2000-2001, Silicon Graphics, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307 USA.
*/
/*
wave.c
This file contains code for parsing RIFF WAVE format sound files.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include "audiofile.h"
#include "util.h"
#include "afinternal.h"
#include "byteorder.h"
#include "wave.h"
#include "track.h"
#include "setup.h"
#include "marker.h"
int _af_wave_compression_types[_AF_WAVE_NUM_COMPTYPES] =
{
AF_COMPRESSION_G711_ULAW,
AF_COMPRESSION_G711_ALAW
};
_InstParamInfo _af_wave_inst_params[_AF_WAVE_NUM_INSTPARAMS] =
{
{ AF_INST_MIDI_BASENOTE, AU_PVTYPE_LONG, "MIDI base note", {60} },
{ AF_INST_NUMCENTS_DETUNE, AU_PVTYPE_LONG, "Detune in cents", {0} },
{ AF_INST_MIDI_LOVELOCITY, AU_PVTYPE_LONG, "Low velocity", {1} },
{ AF_INST_MIDI_HIVELOCITY, AU_PVTYPE_LONG, "High velocity", {127} },
{ AF_INST_MIDI_LONOTE, AU_PVTYPE_LONG, "Low note", {0} },
{ AF_INST_MIDI_HINOTE, AU_PVTYPE_LONG, "High note", {127} },
{ AF_INST_NUMDBS_GAIN, AU_PVTYPE_LONG, "Gain in dB", {0} }
};
_AFfilesetup _af_wave_default_filesetup =
{
_AF_VALID_FILESETUP, /* valid */
AF_FILE_WAVE, /* fileFormat */
AF_TRUE, /* trackSet */
AF_TRUE, /* instrumentSet */
AF_TRUE, /* miscellaneousSet */
1, /* trackCount */
NULL, /* tracks */
0, /* instrumentCount */
NULL, /* instruments */
0, /* miscellaneousCount */
NULL /* miscellaneous */
};
static status ParseFrameCount (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
u_int32_t totalFrames;
_Track *track;
track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);
af_fread(&totalFrames, 1, 4, fp);
track->totalfframes = LENDIAN_TO_HOST_INT32(totalFrames);
return AF_SUCCEED;
}
static status ParseFormat (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
_Track *track;
u_int16_t formatTag, channelCount;
u_int32_t sampleRate, averageBytesPerSecond;
u_int16_t blockAlign;
_WAVEInfo *wave;
assert(filehandle != NULL);
assert(fp != NULL);
assert(!memcmp(&id, "fmt ", 4));
track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);
assert(filehandle->formatSpecific != NULL);
wave = (_WAVEInfo *) filehandle->formatSpecific;
af_fread(&formatTag, 1, 2, fp);
formatTag = LENDIAN_TO_HOST_INT16(formatTag);
af_fread(&channelCount, 1, 2, fp);
channelCount = LENDIAN_TO_HOST_INT16(channelCount);
track->f.channelCount = channelCount;
af_fread(&sampleRate, 1, 4, fp);
sampleRate = LENDIAN_TO_HOST_INT32(sampleRate);
track->f.sampleRate = sampleRate;
af_fread(&averageBytesPerSecond, 1, 4, fp);
averageBytesPerSecond = LENDIAN_TO_HOST_INT32(averageBytesPerSecond);
af_fread(&blockAlign, 1, 2, fp);
blockAlign = LENDIAN_TO_HOST_INT16(blockAlign);
track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;
/* Default to uncompressed audio data. */
track->f.compressionType = AF_COMPRESSION_NONE;
switch (formatTag)
{
case WAVE_FORMAT_PCM:
{
u_int16_t bitsPerSample;
af_fread(&bitsPerSample, 1, 2, fp);
bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);
track->f.sampleWidth = bitsPerSample;
if (bitsPerSample == 0 || bitsPerSample > 32)
{
_af_error(AF_BAD_WIDTH,
"bad sample width of %d bits",
bitsPerSample);
return AF_FAIL;
}
if (bitsPerSample <= 8)
track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
else
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
}
break;
case WAVE_FORMAT_MULAW:
case IBM_FORMAT_MULAW:
track->f.sampleWidth = 16;
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
track->f.compressionType = AF_COMPRESSION_G711_ULAW;
break;
case WAVE_FORMAT_ALAW:
case IBM_FORMAT_ALAW:
track->f.sampleWidth = 16;
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
track->f.compressionType = AF_COMPRESSION_G711_ALAW;
break;
case WAVE_FORMAT_IEEE_FLOAT:
{
u_int16_t bitsPerSample;
af_fread(&bitsPerSample, 1, 2, fp);
bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);
if (bitsPerSample == 64)
{
track->f.sampleWidth = 64;
track->f.sampleFormat = AF_SAMPFMT_DOUBLE;
}
else
{
track->f.sampleWidth = 32;
track->f.sampleFormat = AF_SAMPFMT_FLOAT;
}
}
break;
case WAVE_FORMAT_ADPCM:
{
u_int16_t bitsPerSample, extraByteCount,
samplesPerBlock, numCoefficients;
int i;
AUpvlist pv;
long l;
void *v;
if (track->f.channelCount != 1 &&
track->f.channelCount != 2)
{
_af_error(AF_BAD_CHANNELS,
"WAVE file with MS ADPCM compression "
"must have 1 or 2 channels");
}
af_fread(&bitsPerSample, 1, 2, fp);
bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);
af_fread(&extraByteCount, 1, 2, fp);
extraByteCount = LENDIAN_TO_HOST_INT16(extraByteCount);
af_fread(&samplesPerBlock, 1, 2, fp);
samplesPerBlock = LENDIAN_TO_HOST_INT16(samplesPerBlock);
af_fread(&numCoefficients, 1, 2, fp);
numCoefficients = LENDIAN_TO_HOST_INT16(numCoefficients);
/* numCoefficients should be at least 7. */
assert(numCoefficients >= 7 && numCoefficients <= 255);
for (i=0; i<numCoefficients; i++)
{
int16_t a0, a1;
af_fread(&a0, 1, 2, fp);
af_fread(&a1, 1, 2, fp);
a0 = LENDIAN_TO_HOST_INT16(a0);
a1 = LENDIAN_TO_HOST_INT16(a1);
wave->msadpcmCoefficients[i][0] = a0;
wave->msadpcmCoefficients[i][1] = a1;
}
track->f.sampleWidth = 16;
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
track->f.compressionType = AF_COMPRESSION_MS_ADPCM;
track->f.byteOrder = _AF_BYTEORDER_NATIVE;
/* Create the parameter list. */
pv = AUpvnew(4);
AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS);
AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
l = numCoefficients;
AUpvsetval(pv, 0, &l);
AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS);
AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR);
v = wave->msadpcmCoefficients;
AUpvsetval(pv, 1, &v);
AUpvsetparam(pv, 2, _AF_SAMPLES_PER_BLOCK);
AUpvsetvaltype(pv, 2, AU_PVTYPE_LONG);
l = samplesPerBlock;
AUpvsetval(pv, 2, &l);
AUpvsetparam(pv, 3, _AF_BLOCK_SIZE);
AUpvsetvaltype(pv, 3, AU_PVTYPE_LONG);
l = blockAlign;
AUpvsetval(pv, 3, &l);
track->f.compressionParams = pv;
}
break;
case WAVE_FORMAT_DVI_ADPCM:
{
AUpvlist pv;
long l;
u_int16_t bitsPerSample, extraByteCount,
samplesPerBlock;
af_fread(&bitsPerSample, 1, 2, fp);
bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);
af_fread(&extraByteCount, 1, 2, fp);
extraByteCount = LENDIAN_TO_HOST_INT16(extraByteCount);
af_fread(&samplesPerBlock, 1, 2, fp);
samplesPerBlock = LENDIAN_TO_HOST_INT16(samplesPerBlock);
track->f.sampleWidth = 16;
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
track->f.compressionType = AF_COMPRESSION_IMA;
track->f.byteOrder = _AF_BYTEORDER_NATIVE;
/* Create the parameter list. */
pv = AUpvnew(2);
AUpvsetparam(pv, 0, _AF_SAMPLES_PER_BLOCK);
AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
l = samplesPerBlock;
AUpvsetval(pv, 0, &l);
AUpvsetparam(pv, 1, _AF_BLOCK_SIZE);
AUpvsetvaltype(pv, 1, AU_PVTYPE_LONG);
l = blockAlign;
AUpvsetval(pv, 1, &l);
track->f.compressionParams = pv;
}
break;
case WAVE_FORMAT_YAMAHA_ADPCM:
case WAVE_FORMAT_OKI_ADPCM:
case WAVE_FORMAT_CREATIVE_ADPCM:
case IBM_FORMAT_ADPCM:
_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE ADPCM data format 0x%x is not currently supported", formatTag);
return AF_FAIL;
break;
case WAVE_FORMAT_MPEG:
_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG data format is not supported");
return AF_FAIL;
break;
case WAVE_FORMAT_MPEGLAYER3:
_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG layer 3 data format is not supported");
return AF_FAIL;
break;
default:
_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE file data format 0x%x not currently supported", formatTag);
return AF_FAIL;
break;
}
_af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth);
return AF_SUCCEED;
}
static status ParseData (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
_Track *track;
assert(filehandle != NULL);
assert(fp != NULL);
assert(!memcmp(&id, "data", 4));
track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);
track->fpos_first_frame = af_ftell(fp);
track->data_size = size;
return AF_SUCCEED;
}
static status ParsePlayList (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
_Instrument *instrument;
u_int32_t segmentCount;
int segment;
af_fread(&segmentCount, 4, 1, fp);
segmentCount = LENDIAN_TO_HOST_INT32(segmentCount);
if (segmentCount == 0)
{
filehandle->instrumentCount = 0;
filehandle->instruments = NULL;
return AF_SUCCEED;
}
for (segment=0; segment<segmentCount; segment++)
{
u_int32_t startMarkID, loopLength, loopCount;
af_fread(&startMarkID, 4, 1, fp);
startMarkID = LENDIAN_TO_HOST_INT32(startMarkID);
af_fread(&loopLength, 4, 1, fp);
loopLength = LENDIAN_TO_HOST_INT32(loopLength);
af_fread(&loopCount, 4, 1, fp);
loopCount = LENDIAN_TO_HOST_INT32(loopCount);
}
return AF_SUCCEED;
}
static status ParseCues (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
_Track *track;
u_int32_t markerCount;
int i;
track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);
af_fread(&markerCount, 4, 1, fp);
markerCount = LENDIAN_TO_HOST_INT32(markerCount);
track->markerCount = markerCount;
if (markerCount == 0)
{
track->markers = NULL;
return AF_SUCCEED;
}
if ((track->markers = _af_marker_new(markerCount)) == NULL)
return AF_FAIL;
for (i=0; i<markerCount; i++)
{
u_int32_t id, position, chunkid;
u_int32_t chunkByteOffset, blockByteOffset;
u_int32_t sampleFrameOffset;
_Marker *marker = &track->markers[i];
af_fread(&id, 4, 1, fp);
id = LENDIAN_TO_HOST_INT32(id);
af_fread(&position, 4, 1, fp);
position = LENDIAN_TO_HOST_INT32(position);
af_fread(&chunkid, 4, 1, fp);
chunkid = LENDIAN_TO_HOST_INT32(chunkid);
af_fread(&chunkByteOffset, 4, 1, fp);
chunkByteOffset = LENDIAN_TO_HOST_INT32(chunkByteOffset);
af_fread(&blockByteOffset, 4, 1, fp);
blockByteOffset = LENDIAN_TO_HOST_INT32(blockByteOffset);
/*
sampleFrameOffset represents the position of
the mark in units of frames.
*/
af_fread(&sampleFrameOffset, 4, 1, fp);
sampleFrameOffset = LENDIAN_TO_HOST_INT32(sampleFrameOffset);
marker->id = id;
marker->position = sampleFrameOffset;
marker->name = _af_strdup("");
marker->comment = _af_strdup("");
}
return AF_SUCCEED;
}
/* Parse an adtl sub-chunk within a LIST chunk. */
static status ParseADTLSubChunk (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
_Track *track;
AFfileoffset endPos=af_ftell(fp)+size;
track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);
while (af_ftell(fp) < endPos)
{
char chunkID[4];
u_int32_t chunkSize;
af_fread(chunkID, 4, 1, fp);
af_fread(&chunkSize, 4, 1, fp);
chunkSize = LENDIAN_TO_HOST_INT32(chunkSize);
if (memcmp(chunkID, "labl", 4)==0 || memcmp(chunkID, "note", 4)==0)
{
_Marker *marker=NULL;
u_int32_t id;
long length=chunkSize-4;
char *p=_af_malloc(length);
af_fread(&id, 4, 1, fp);
af_fread(p, length, 1, fp);
id = LENDIAN_TO_HOST_INT32(id);
marker = _af_marker_find_by_id(track, id);
if (marker != NULL)
{
if (memcmp(chunkID, "labl", 4)==0)
{
free(marker->name);
marker->name = p;
}
else if (memcmp(chunkID, "note", 4)==0)
{
free(marker->comment);
marker->comment = p;
}
else
free(p);
}
else
free(p);
/*
If chunkSize is odd, skip an extra byte
at the end of the chunk.
*/
if ((chunkSize % 2) != 0)
af_fseek(fp, 1, SEEK_CUR);
}
else
{
/* If chunkSize is odd, skip an extra byte. */
af_fseek(fp, chunkSize + (chunkSize % 2), SEEK_CUR);
}
}
return AF_SUCCEED;
}
/* Parse an INFO sub-chunk within a LIST chunk. */
static status ParseINFOSubChunk (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
AFfileoffset endPos=af_ftell(fp)+size;
while (af_ftell(fp) < endPos)
{
int misctype = AF_MISC_UNRECOGNIZED;
u_int32_t miscid, miscsize;
af_fread(&miscid, 4, 1, fp);
af_fread(&miscsize, 4, 1, fp);
miscsize = LENDIAN_TO_HOST_INT32(miscsize);
if (memcmp(&miscid, "IART", 4) == 0)
misctype = AF_MISC_AUTH;
else if (memcmp(&miscid, "INAM", 4) == 0)
misctype = AF_MISC_NAME;
else if (memcmp(&miscid, "ICOP", 4) == 0)
misctype = AF_MISC_COPY;
else if (memcmp(&miscid, "ICMT", 4) == 0)
misctype = AF_MISC_ICMT;
else if (memcmp(&miscid, "ICRD", 4) == 0)
misctype = AF_MISC_ICRD;
else if (memcmp(&miscid, "ISFT", 4) == 0)
misctype = AF_MISC_ISFT;
if (misctype != AF_MISC_UNRECOGNIZED)
{
char *string = _af_malloc(miscsize);
af_fread(string, miscsize, 1, fp);
filehandle->miscellaneousCount++;
filehandle->miscellaneous = _af_realloc(filehandle->miscellaneous, sizeof (_Miscellaneous) * filehandle->miscellaneousCount);
filehandle->miscellaneous[filehandle->miscellaneousCount-1].id = filehandle->miscellaneousCount;
filehandle->miscellaneous[filehandle->miscellaneousCount-1].type = misctype;
filehandle->miscellaneous[filehandle->miscellaneousCount-1].size = miscsize;
filehandle->miscellaneous[filehandle->miscellaneousCount-1].position = 0;
filehandle->miscellaneous[filehandle->miscellaneousCount-1].buffer = string;
}
else
{
af_fseek(fp, miscsize, SEEK_CUR);
}
/* Make the current position an even number of bytes. */
if (miscsize % 2 != 0)
af_fseek(fp, 1, SEEK_CUR);
}
return AF_SUCCEED;
}
static status ParseList (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
u_int32_t typeID;
af_fread(&typeID, 4, 1, fp);
size-=4;
if (memcmp(&typeID, "adtl", 4) == 0)
{
/* Handle adtl sub-chunks. */
return ParseADTLSubChunk(filehandle, fp, typeID, size);
}
else if (memcmp(&typeID, "INFO", 4) == 0)
{
/* Handle INFO sub-chunks. */
return ParseINFOSubChunk(filehandle, fp, typeID, size);
}
else
{
/* Skip unhandled sub-chunks. */
af_fseek(fp, size, SEEK_CUR);
return AF_SUCCEED;
}
return AF_SUCCEED;
}
static status ParseInstrument (AFfilehandle filehandle, AFvirtualfile *fp,
u_int32_t id, size_t size)
{
u_int8_t baseNote;
int8_t detune, gain;
u_int8_t lowNote, highNote, lowVelocity, highVelocity;
u_int8_t padByte;
af_fread(&baseNote, 1, 1, fp);
af_fread(&detune, 1, 1, fp);
af_fread(&gain, 1, 1, fp);
af_fread(&lowNote, 1, 1, fp);
af_fread(&highNote, 1, 1, fp);
af_fread(&lowVelocity, 1, 1, fp);
af_fread(&highVelocity, 1, 1, fp);
af_fread(&padByte, 1, 1, fp);
return AF_SUCCEED;
}
bool _af_wave_recognize (AFvirtualfile *fh)
{
u_int8_t buffer[8];
af_fseek(fh, 0, SEEK_SET);
if (af_fread(buffer, 1, 8, fh) != 8 || memcmp(buffer, "RIFF", 4) != 0)
return AF_FALSE;
if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, "WAVE", 4) != 0)
return AF_FALSE;
return AF_TRUE;
}
status _af_wave_read_init (AFfilesetup setup, AFfilehandle filehandle)
{
_Track *track;
u_int32_t type, size, formtype;
u_int32_t index = 0;
bool hasFormat, hasData, hasCue, hasList, hasPlayList, hasFrameCount,
hasINST, hasINFO;
_WAVEInfo *wave = _af_malloc(sizeof (_WAVEInfo));
assert(filehandle != NULL);
assert(filehandle->fh != NULL);
hasFormat = AF_FALSE;
hasData = AF_FALSE;
hasCue = AF_FALSE;
hasList = AF_FALSE;
hasPlayList = AF_FALSE;
hasFrameCount = AF_FALSE;
hasINST = AF_FALSE;
hasINFO = AF_FALSE;
filehandle->formatSpecific = wave;
filehandle->instruments = NULL;
filehandle->instrumentCount = 0;
filehandle->miscellaneous = NULL;
filehandle->miscellaneousCount = 0;
track = _af_track_new();
filehandle->tracks = track;
filehandle->trackCount = 1;
af_fseek(filehandle->fh, 0, SEEK_SET);
af_fread(&type, 4, 1, filehandle->fh);
af_fread(&size, 4, 1, filehandle->fh);
size = LENDIAN_TO_HOST_INT32(size);
af_fread(&formtype, 4, 1, filehandle->fh);
assert(!memcmp(&type, "RIFF", 4));
assert(!memcmp(&formtype, "WAVE", 4));
#ifdef DEBUG
printf("size: %d\n", size);
#endif
/* Include the offset of the form type. */
index += 4;
while (index < size)
{
u_int32_t chunkid = 0, chunksize = 0;
status result;
#ifdef DEBUG
printf("index: %d\n", index);
#endif
af_fread(&chunkid, 4, 1, filehandle->fh);
af_fread(&chunksize, 4, 1, filehandle->fh);
chunksize = LENDIAN_TO_HOST_INT32(chunksize);
#ifdef DEBUG
_af_printid(BENDIAN_TO_HOST_INT32(chunkid));
printf(" size: %d\n", chunksize);
#endif
if (memcmp(&chunkid, "fmt ", 4) == 0)
{
result = ParseFormat(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
hasFormat = AF_TRUE;
}
else if (memcmp(&chunkid, "data", 4) == 0)
{
/* The format chunk must precede the data chunk. */
if (!hasFormat)
{
_af_error(AF_BAD_HEADER, "missing format chunk in WAVE file");
return AF_FAIL;
}
result = ParseData(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
hasData = AF_TRUE;
}
else if (memcmp(&chunkid, "inst", 4) == 0)
{
result = ParseInstrument(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
}
else if (memcmp(&chunkid, "fact", 4) == 0)
{
hasFrameCount = AF_TRUE;
result = ParseFrameCount(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
}
else if (memcmp(&chunkid, "cue ", 4) == 0)
{
hasCue = AF_TRUE;
result = ParseCues(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
}
else if (memcmp(&chunkid, "LIST", 4) == 0 || memcmp(&chunkid, "list", 4) == 0)
{
hasList = AF_TRUE;
result = ParseList(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
}
else if (memcmp(&chunkid, "INST", 4) == 0)
{
hasINST = AF_TRUE;
result = ParseInstrument(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
}
else if (memcmp(&chunkid, "plst", 4) == 0)
{
hasPlayList = AF_TRUE;
result = ParsePlayList(filehandle, filehandle->fh, chunkid, chunksize);
if (result == AF_FAIL)
return AF_FAIL;
}
index += chunksize + 8;
/* All chunks must be aligned on an even number of bytes */
if ((index % 2) != 0)
index++;
af_fseek(filehandle->fh, index + 8, SEEK_SET);
}
/* The format chunk and the data chunk are required. */
if (!hasFormat || !hasData)
{
return AF_FAIL;
}
/*
At this point we know that the file has a format chunk
and a data chunk, so we can assume that track->f and
track->data_size have been initialized.
*/
if (hasFrameCount == AF_FALSE)
{
/*
Perform arithmetic in double-precision so as
to preserve accuracy.
*/
track->totalfframes = ceil((double) track->data_size /
_af_format_frame_size(&track->f, AF_FALSE));
}
if (track->f.compressionType != AF_COMPRESSION_NONE &&
(track->f.compressionType == AF_COMPRESSION_G711_ULAW ||
track->f.compressionType == AF_COMPRESSION_G711_ALAW))
{
track->totalfframes = track->data_size / track->f.channelCount;
}
/*
A return value of AF_SUCCEED indicates successful parsing.
*/
return AF_SUCCEED;
}
AFfilesetup _af_wave_complete_setup (AFfilesetup setup)
{
AFfilesetup newsetup;
_TrackSetup *track;
if (setup->trackSet && setup->trackCount != 1)
{
_af_error(AF_BAD_NUMTRACKS, "WAVE file must have 1 track");
return AF_NULL_FILESETUP;
}
track = _af_filesetup_get_tracksetup(setup, AF_DEFAULT_TRACK);
if (track->sampleFormatSet)
{
switch (track->f.sampleFormat)
{
case AF_SAMPFMT_FLOAT:
if (track->sampleWidthSet &&
track->f.sampleWidth != 32)
{
_af_error(AF_BAD_WIDTH,
"Warning: invalid sample width for floating-point WAVE file: %d (must be 32 bits)\n",
track->f.sampleWidth);
_af_set_sample_format(&track->f, AF_SAMPFMT_FLOAT, 32);
}
break;
case AF_SAMPFMT_DOUBLE:
_af_error(AF_BAD_SAMPFMT, "WAVE format does not support double-precision floating-point data");
return AF_NULL_FILESETUP;
break;
case AF_SAMPFMT_UNSIGNED:
if (track->sampleWidthSet)
{
if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)
{
_af_error(AF_BAD_WIDTH, "invalid sample width for WAVE file: %d (must be 1-32 bits)\n", track->f.sampleWidth);
return AF_NULL_FILESETUP;
}
if (track->f.sampleWidth > 8)
{
_af_error(AF_BAD_SAMPFMT, "WAVE integer data of more than 8 bits must be two's complement signed");
_af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth);
}
}
else
/*
If the sample width is not set but the user requests
unsigned data, set the width to 8 bits.
*/
_af_set_sample_format(&track->f, track->f.sampleFormat, 8);
break;
case AF_SAMPFMT_TWOSCOMP:
if (track->sampleWidthSet)
{
if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)
{
_af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth);
return AF_NULL_FILESETUP;
}
else if (track->f.sampleWidth <= 8)
{
_af_error(AF_BAD_SAMPFMT, "Warning: WAVE format integer data of 1-8 bits must be unsigned; setting sample format to unsigned");
_af_set_sample_format(&track->f, AF_SAMPFMT_UNSIGNED, track->f.sampleWidth);
}
}
else
/*
If no sample width was specified, we default to 16 bits
for signed integer data.
*/
_af_set_sample_format(&track->f, track->f.sampleFormat, 16);
break;
}
}
/*
Otherwise set the sample format depending on the sample
width or set completely to default.
*/
else
{
if (track->sampleWidthSet == AF_FALSE)
{
track->f.sampleWidth = 16;
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
}
else
{
if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)
{
_af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth);
return AF_NULL_FILESETUP;
}
else if (track->f.sampleWidth > 8)
/* Here track->f.sampleWidth is in {1..32}. */
track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
else
/* Here track->f.sampleWidth is in {1..8}. */
track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
}
}
if (track->f.compressionType != AF_COMPRESSION_NONE &&
track->f.compressionType != AF_COMPRESSION_G711_ULAW &&
track->f.compressionType != AF_COMPRESSION_G711_ALAW)
{
_af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not supported in WAVE format");
return AF_NULL_FILESETUP;
}
if (track->byteOrderSet &&
track->f.byteOrder != AF_BYTEORDER_LITTLEENDIAN &&
track->f.compressionType == AF_COMPRESSION_NONE)
{
_af_error(AF_BAD_BYTEORDER, "WAVE format only supports little-endian data");
return AF_NULL_FILESETUP;
}
if (track->f.compressionType == AF_COMPRESSION_NONE)
track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;
else
track->f.byteOrder = AF_BYTEORDER_BIGENDIAN;
if (track->aesDataSet)
{
_af_error(AF_BAD_FILESETUP, "WAVE files cannot have AES data");
return AF_NULL_FILESETUP;
}
if (setup->instrumentSet)
{
if (setup->instrumentCount > 1)
{
_af_error(AF_BAD_NUMINSTS, "WAVE files can have 0 or 1 instrument");
return AF_NULL_FILESETUP;
}
else if (setup->instrumentCount == 1)
{
if (setup->instruments[0].loopSet &&
setup->instruments[0].loopCount > 0 &&
(track->markersSet == AF_FALSE || track->markerCount == 0))
{
_af_error(AF_BAD_NUMMARKS, "WAVE files with loops must contain at least 1 marker");
return AF_NULL_FILESETUP;
}
}
}
/* Make sure the miscellaneous data is of an acceptable type. */
if (setup->miscellaneousSet)
{
int i;
for (i=0; i<setup->miscellaneousCount; i++)
{
switch (setup->miscellaneous[i].type)
{
case AF_MISC_COPY:
case AF_MISC_AUTH:
case AF_MISC_NAME:
case AF_MISC_ICRD:
case AF_MISC_ISFT:
case AF_MISC_ICMT:
break;
default:
_af_error(AF_BAD_MISCTYPE, "illegal miscellaneous type [%d] for WAVE file", setup->miscellaneous[i].type);
return AF_NULL_FILESETUP;
}
}
}
/*
Allocate an AFfilesetup and make all the unset fields correct.
*/
newsetup = _af_filesetup_copy(setup, &_af_wave_default_filesetup, AF_FALSE);
/* Make sure we do not copy loops if they are not specified in setup. */
if (setup->instrumentSet && setup->instrumentCount > 0 &&
setup->instruments[0].loopSet)
{
free(newsetup->instruments[0].loops);
newsetup->instruments[0].loopCount = 0;
}
return newsetup;
}
bool _af_wave_instparam_valid (AFfilehandle filehandle, AUpvlist list, int i)
{
int param, type, lval;
AUpvgetparam(list, i, ¶m);
AUpvgetvaltype(list, i, &type);
if (type != AU_PVTYPE_LONG)
return AF_FALSE;
AUpvgetval(list, i, &lval);
switch (param)
{
case AF_INST_MIDI_BASENOTE:
return ((lval >= 0) && (lval <= 127));
case AF_INST_NUMCENTS_DETUNE:
return ((lval >= -50) && (lval <= 50));
case AF_INST_MIDI_LOVELOCITY:
return ((lval >= 1) && (lval <= 127));
case AF_INST_MIDI_HIVELOCITY:
return ((lval >= 1) && (lval <= 127));
case AF_INST_MIDI_LONOTE:
return ((lval >= 0) && (lval <= 127));
case AF_INST_MIDI_HINOTE:
return ((lval >= 0) && (lval <= 127));
case AF_INST_NUMDBS_GAIN:
return AF_TRUE;
default:
return AF_FALSE;
}
return AF_TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1