home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * Copyright (C) 1994 Charles P. Peterson *
- * 4007 Enchanted Sun, San Antonio, Texas 78244-1254 *
- * Email: Charles_P_Peterson@fcircus.sat.tx.us *
- * *
- * This is free software with NO WARRANTY. *
- * See gfft.c, or run program itself, for details. *
- * Support is available for a fee. *
- ***************************************************************************
- *
- * Program: gfft--General FFT analysis
- * File: format.c
- * Purpose: parse formatted files
- * Author: Charles Peterson (CPP)
- * History: 18-October-1993 CPP; Created.
- * Comment: Thanks to Guido van Rossum for SOX and David Champion
- * for OmniPlay, from which much was learned.
- * Thanks to Malcolm Slaney and Ken Turkowski for
- * ConvertFromIeeeExtended, from which much was learned.
- * However, this is an original implementation, containing
- * no previously copyrighted code.
- */
-
- #include <math.h>
- #include <stdio.h>
- #include "gfft.h"
- #include "settings.h"
- #include "format.h"
-
- #define NO_ERROR FALSE
- #define CORRUPT TRUE
- #define UNAVAIL_COMPRESSION 2
-
- static unsigned long offset = 0; /* Running offset pointer used here */
-
- /*
- * Static function declarations
- */
- static void read_form_format (void); /* Includes 8SVX, AIFF, AIFC types */
- static void read_8svx_format (long form_cksize);
- static int read_vhdr (ULONG form_cksize, struct ChunkHeader chunk_h);
- static void read_aiff_format (long form_cksize);
- static int read_comm_aiff (ULONG form_cksize, struct ChunkHeader chunk_h);
- static void read_aifc_format (long form_cksize);
- static int read_comm_aifc (ULONG form_cksize, struct ChunkHeader chunk_h);
- static double double_from_extended (UBYTE *extended);
- static void read_avr_format (void);
-
- void reset_format (void)
- {
- /*
- * Reset Global Format Info
- */
- FileFormat = UNFORMATTED;
- FileFormatOK = FALSE;
- FileRate = AUTO_RATE;
- FileDataOffset = 0;
- FileFrames = 0;
- FileVolume = 1.0L;
- FileOctaves = 1;
- FileChannels = 1;
- FileOneShotHiFrames = 0;
- FileRepeatHiFrames = 0;
- }
-
- void read_format (void)
- {
- ID format_id;
- int format = UNFORMATTED;
-
- reset_format();
-
- if (fread (&format_id, sizeof format_id, 1, ReadPtr))
- {
- offset = sizeof format_id; /* This is where offset count begins */
- switch (format_id)
- {
- case ID_FORM: /* IFF 8SVX, AIFF, and AIFC */
- format = ID_FORM; /* Clarified later */
- break;
- case ID_RIFF:
- format = ID_RIFF;
- break;
- case ID_VOCH:
- format = ID_VOCH;
- break;
- case ID_AVR:
- format = ID_AVR;
- break;
- }
- }
- if (format != UNFORMATTED)
- {
- if (IgnoreFormat)
- {
- error_message (FILE_FORMAT_IGNORED);
- return;
- }
- Rate = AUTO_RATE; /* override must be forced afterwards */
- }
- FileFormat = format;
- switch (format)
- {
- case ID_FORM:
- read_form_format ();
- break;
- case ID_AVR:
- read_avr_format ();
- break;
- case UNFORMATTED:
- break;
- default:
- error_message (UNSUPPORTED_FORMAT);
- FileFormatOK = FALSE;
- }
- }
-
- static void read_form_format (void) /* Includes 8SVX, AIFF, AIFC types */
- {
- ULONG form_cksize;
- ID form_type;
-
- if (fread (&form_cksize, sizeof form_cksize, 1, ReadPtr) &&
- fread (&form_type, sizeof form_type, 1, ReadPtr))
- {
- offset += sizeof form_cksize + sizeof form_type;
- switch (form_type)
- {
- case ID_8SVX:
- FileFormat = ID_8SVX;
- read_8svx_format (form_cksize);
- break;
- case ID_AIFF:
- FileFormat = ID_AIFF;
- read_aiff_format (form_cksize);
- break;
- case ID_AIFC:
- FileFormat = ID_AIFC;
- read_aifc_format (form_cksize);
- break;
- default:
- error_message (UNSUPPORTED_FORMAT);
- break;
- }
- }
- else
- {
- error_message (CORRUPT_IFF); /* Missing cksize and/or type! */
- }
- }
-
- static void read_8svx_format (long form_cksize)
- /*
- * Chunks are allowed to be in any order.
- * But, a fast and lazy approach is taken to validation:
- * Chunks looked for: VHDR and BODY
- * (Once those two have been found, and VHDR read, I exit.)
- * BODY chunk isn't read (so premature file end must be detected later)
- * BODY chunk is fseek'd over only if VHDR hasn't been found yet.
- * form_cksize isn't actually checked
- */
- {
- int error = NO_ERROR;
- struct ChunkHeader chunk_h;
- ULONG skip_bytes;
- BOOLEAN vhdr_found = FALSE;
- BOOLEAN body_found = FALSE;
-
- InputFormat.bits = 8; /* True of all 8SVX */
- InputFormat.zero = 0; /* Ditto */
-
- while (fread (&chunk_h, sizeof chunk_h, 1, ReadPtr))
- {
- offset += sizeof chunk_h;
- switch (chunk_h.ckID)
- {
- case ID_VHDR:
- vhdr_found = TRUE;
- error = read_vhdr (form_cksize, chunk_h);
- offset += WordAlign (chunk_h.ckSize);
- break;
- case ID_BODY:
- body_found = TRUE;
- FileDataOffset = offset;
- if (!vhdr_found && !error) /* If still looking for VHDR */
- {
- skip_bytes = WordAlign (chunk_h.ckSize);
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- }
- break;
- default:
- skip_bytes = WordAlign (chunk_h.ckSize);
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- break;
- }
- if (error || (vhdr_found && body_found))
- {
- break;
- }
- }
- if (!vhdr_found || !body_found || error)
- {
- if (error <= CORRUPT) /* Higher errors reported elsewhere */
- {
- error_message (CORRUPT_IFF);
- }
- }
- else
- {
- FileFormatOK = TRUE;
- }
- }
-
- static int read_vhdr (ULONG form_cksize, struct ChunkHeader chunk_h)
- {
- int error = CORRUPT; /* defaulted if chunk not completely read */
- struct VHDR vhdr;
- ULONG skip_bytes;
-
- if (chunk_h.ckSize < sizeof vhdr)
- {
- return CORRUPT; /* Error! Pre '85 or corrupt! */
- }
- if (fread (&vhdr, sizeof vhdr, 1, ReadPtr))
- {
- FileRate = vhdr.samplesPerSec;
- if (vhdr.sCompression)
- {
- error_message (COMPRESSION_NOT_SUPPORTED);
- return UNAVAIL_COMPRESSION;
- }
- FileVolume = vhdr.volume / 65536.0L;
- FileOctaves = vhdr.ctOctave;
- FileOneShotHiFrames = vhdr.oneShotHiSamples;
- FileRepeatHiFrames = vhdr.repeatHiSamples;
- FileFrames = FileOneShotHiFrames + FileRepeatHiFrames;
-
- error = NO_ERROR;
- if (skip_bytes = WordAlign (chunk_h.ckSize - sizeof vhdr)) /* = */
- {
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- }
- }
- return error;
- }
-
- static void read_aiff_format (long form_cksize)
- /*
- * Chunks are allowed to be in any order.
- * But, a fast and lazy approach is taken to validation:
- * Chunks looked for: Common (COMM) and Sound Data (SSND)
- * (Note: COMM here is different from the AIFC version.)
- * (Once those two have been found, and COMM read, I exit.)
- * SSND chunk isn't read (so premature file end must be detected later)
- * SSND chunk is fseek'd over only if VHDR hasn't been found yet.
- * form_cksize isn't actually checked
- */
- {
- ULONG skip_bytes;
- int error = NO_ERROR;
- struct ChunkHeader chunk_h;
- struct SoundDataChunkInfo sdci;
- BOOLEAN ssnd_found = FALSE;
- BOOLEAN comm_found = FALSE;
-
- while (fread (&chunk_h, sizeof chunk_h, 1, ReadPtr))
- {
- offset += sizeof chunk_h;
- switch (chunk_h.ckID)
- {
- case ID_COMM:
- comm_found = TRUE;
- error = read_comm_aiff (form_cksize, chunk_h);
- offset += WordAlign (chunk_h.ckSize);
- break;
- case ID_SSND:
- ssnd_found = TRUE;
- error = !fread (&sdci, sizeof sdci, 1, ReadPtr);
- offset += sizeof sdci;
- FileDataOffset = offset + sdci.offset;
- if (!comm_found && !error)
- {
- skip_bytes = WordAlign (chunk_h.ckSize - sizeof sdci);
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- }
- break;
- default:
- skip_bytes = WordAlign (chunk_h.ckSize);
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- break;
- }
- if (error || (comm_found && ssnd_found))
- {
- break;
- }
- }
- if (!comm_found || !ssnd_found || error)
- {
- if (error <= CORRUPT) /* Higher errors reported elsewhere */
- {
- error_message (CORRUPT_AIFF);
- }
- }
- else
- {
- FileFormatOK = TRUE;
- }
- }
-
- static int read_comm_aiff (ULONG form_cksize, struct ChunkHeader chunk_h)
- {
- int error = CORRUPT; /* defaulted if chunk not completely read */
- int skip_bytes;
- struct CommAiff comm;
-
- if (chunk_h.ckSize < sizeof comm)
- {
- return CORRUPT; /* Error...corrupt or unsupported AIFF format */
- }
- if (fread (&comm, sizeof comm, 1, ReadPtr))
- {
- FileChannels = comm.numChannels;
- FileFrames = comm.numSampleFrames;
- InputFormat.bits = comm.sampleSize;
- InputFormat.zero = 0;
- FileRate = double_from_extended (comm.sampleRate);
-
- error = NO_ERROR;
- if (skip_bytes = WordAlign (chunk_h.ckSize - sizeof comm)) /* = */
- {
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- }
- }
- return error;
- }
-
- static void read_aifc_format (long form_cksize)
- /*
- * Chunks are allowed to be in any order.
- * But, a fast and lazy approach is taken to validation:
- * Chunks looked for: COMM and SSND
- * (Once those two have been found, and COMM read, I exit.)
- * SSND chunk isn't read (so premature file end must be detected later)
- * SSND chunk is fseek'd over only if VHDR hasn't been found yet.
- * form_cksize isn't actually checked
- */
- {
- ULONG skip_bytes;
- int error = NO_ERROR;
- struct ChunkHeader chunk_h;
- struct SoundDataChunkInfo sdci;
- BOOLEAN ssnd_found = FALSE;
- BOOLEAN comm_found = FALSE;
-
- while (fread (&chunk_h, sizeof chunk_h, 1, ReadPtr))
- {
- offset += sizeof chunk_h;
- switch (chunk_h.ckID)
- {
- case ID_COMM:
- comm_found = TRUE;
- error = read_comm_aifc (form_cksize, chunk_h);
- offset += WordAlign (chunk_h.ckSize);
- break;
- case ID_SSND:
- ssnd_found = TRUE;
- error = !fread (&sdci, sizeof sdci, 1, ReadPtr);
- offset += sizeof sdci;
- FileDataOffset = offset + sdci.offset;
- if (!comm_found && !error)
- {
- skip_bytes = WordAlign (chunk_h.ckSize - sizeof sdci);
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- }
- break;
- default:
- skip_bytes = WordAlign (chunk_h.ckSize);
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- break;
- }
- if (error || (comm_found && ssnd_found))
- {
- break;
- }
- }
- if (!comm_found || !ssnd_found || error)
- {
- if (error <= CORRUPT)
- {
- error_message (CORRUPT_AIFC);
- }
- }
- else
- {
- FileFormatOK = TRUE;
- }
- }
-
- static int read_comm_aifc (ULONG form_cksize, struct ChunkHeader chunk_h)
- {
- int error = CORRUPT; /* Defaulted if chunk not read */
- int skip_bytes;
- struct CommAifc comm;
-
- if (chunk_h.ckSize < sizeof comm)
- {
- return CORRUPT; /* Error...corrupt or unsupported AIFF format */
- }
- if (fread (&comm, sizeof comm, 1, ReadPtr))
- {
- FileChannels = comm.numChannels;
- FileFrames = comm.numSampleFrames;
- InputFormat.bits = comm.sampleSize;
- InputFormat.zero = 0;
- FileRate = double_from_extended (comm.sampleRate);
- if (comm.compressionType != ID_NONE)
- {
- error_message (COMPRESSION_NOT_SUPPORTED);
- return UNAVAIL_COMPRESSION;
- }
-
- error = NO_ERROR;
- if (skip_bytes = WordAlign (chunk_h.ckSize - sizeof comm)) /* = */
- {
- error = !!fseek (ReadPtr, skip_bytes, SEEK_CUR);
- offset += skip_bytes;
- }
- }
- return error;
- }
-
-
- /*
- * The following is based on ideas from ConvertFromIeeeExtended
- * by Malcolm Slaney and Ken Turkowski. However, since that was
- * copyright by Apple Computer, Inc., I have prepared an original
- * implementation here.
- *
- * Note: NaN's and infinities are converted to HUGE_VAL. This is
- * not very nice for NaN's.
- *
- * WARNING! THIS HASN'T BEEN WELL TESTED, SO I WOULDN'T USE THIS FOR
- * OTHER OR SERIOUS PURPOSES OR ON OTHER ARCHITECTURES.
- */
-
- #define UPPER_EXP_MASK 0x7F
- #define SIGN_MASK 0x80
-
- #define INFINITE_EXPONENT 0x7FFF
-
- #define HI_E_OFF 16414 /* (16383 + 31) */
- #define LO_E_OFF 16446 /* (16383 + 31 + 32) */
- #define U_TO_F(u) (((double) ((long) ((u)-2147483647L-1))) + 2147483648.0)
-
- static double double_from_extended (UBYTE *extended)
- {
- double dval;
- ULONG high_mantissa;
- ULONG low_mantissa;
- int exponent;
-
- exponent = ((extended[0] & UPPER_EXP_MASK) << 8) | extended[1];
-
- high_mantissa = ((ULONG) (extended[2] << 24)) |
- ((ULONG) (extended[3] << 16)) |
- ((ULONG) (extended[4] << 8)) |
- (ULONG) extended[5];
-
- low_mantissa = ((ULONG) (extended[6] << 24)) |
- ((ULONG) (extended[7] << 16)) |
- ((ULONG) (extended[8] << 8)) |
- (ULONG) extended[9];
-
- if (exponent == 0 && high_mantissa == 0 && low_mantissa == 0)
- {
- dval = 0;
- }
- else
- {
- if (exponent == INFINITE_EXPONENT) /* Infinity or NaN */
- {
- dval = HUGE_VAL; /* from ANSI C math.h */
- }
- else
- {
- dval = ldexp ( U_TO_F (high_mantissa), exponent - HI_E_OFF) +
- ldexp ( U_TO_F (low_mantissa), exponent - LO_E_OFF);
- }
- if (extended[0] & SIGN_MASK)
- {
- dval = (-dval);
- }
- }
- return dval;
- }
-
- static void read_avr_format (void)
- {
- struct AVRH avrh;
-
- if (!fread (&avrh, sizeof avrh, 1, ReadPtr))
- {
- error_message (CORRUPT_AVR);
- }
- else
- {
- FileFormatOK = TRUE;
- FileDataOffset = sizeof (ID) + sizeof (struct AVRH);
-
- if (avrh.mono == 0)
- {
- FileChannels = 1;
- }
- else if (avrh.mono == -1)
- {
- FileChannels = 2;
- }
- else
- {
- error_message (CORRUPT_AVR);
- FileFormatOK = FALSE;
- }
-
- InputFormat.bits = avrh.rez;
- if (InputFormat.bits > 16 || InputFormat.bits < 0)
- {
- error_message (CORRUPT_AVR);
- FileFormatOK = FALSE;
- }
-
- if (avrh.sign == -1)
- {
- InputFormat.zero = 0;
- }
- else if (avrh.sign == 0)
- {
- int sample_width = (InputFormat.bits > 8) ? 16 : 8;
- InputFormat.zero = ((unsigned long) 0xffffffff >>
- (33 - sample_width)) + (unsigned long) 1;
- }
- else
- {
- error_message (CORRUPT_AVR);
- FileFormatOK = FALSE;
- }
-
- FileRate = avrh.rate & 0xFFFFFF; /* I don't understand why */
- /*
- * Maybe FF in upper bits MEANS number is an integer, no FF means float
- * But, my docs didn't say this
- */
- FileFrames = avrh.size;
- if (FileFrames < 0)
- {
- error_message (CORRUPT_AVR);
- FileFormatOK = FALSE;
- }
-
- if (avrh.res2)
- {
- error_message (COMPRESSION_NOT_SUPPORTED);
- FileFormatOK = FALSE;
- }
- }
- }
-