/* Spectroscopic Toolkit version 1.94 by Pieter Suurmond, april 16, 2011. See API specification in headerfile 'ST_aiff.h'. Copyright (c) 2000-2011 - Pieter Suurmond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is requested to send the modifications to the original developer so that they can be incorporated into the canonical version. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include "ST_integers.h" /* We need ST's integer types. */ #include "ST_aiff.h" /* Just prototypes and a forward declaration. */ #define kMAX_frames (79380000UL) /* Default maximum length (30 min */ /* @44.1kHz 16 bit stereo, 303 MB) */ #define kMIN_channels (1) #define kMAX_channels (16) #define kMIN_samplerate (64L) /* Tolerant regarding samplerate. */ #define kMAX_samplerate (144000L) #define kMAX_bytes (4) /* Never more than 32 bits per output sample. */ struct AUDIOFILE /* Private structure, forward declaration in headerfile. */ { char* filename; /* Dynamically allocated copy. */ FILE *fp; /* File stream pointer. */ UNSIGNED32 frames; /* Total number of frames (4 byte). */ UNSIGNED16 channels; /* Number of sound channels (2 byte). */ UNSIGNED16 bits; /* Number of bits (2 byte). */ SIGNED32 rate; /* Sampling rate in cycles per second. */ UNSIGNED16 bytes; /* Derived constants. */ UNSIGNED16 shiftRight; /* Scaling. */ SIGNED64 fractMask; /* Rounding. */ SIGNED64 half; SIGNED64 underflow; /* Minimum and maximum sample values. */ SIGNED64 overflow; SIGNED64 peak; /* Remember highest (lowest) output value. */ SIGNED64 peak_neg; /* Absolute value * -1, used for comparison. */ UNSIGNED8 buffer[kMAX_bytes]; }; /*----------------------------------------------------------------------------*/ /* Writes 2 byte short, MSB first. Works ok on both big and little endians. */ static short aiffWBshort (unsigned short u0, FILE* fp) { if (EOF == putc(u0 >> 8, fp)) return -1; if (EOF == putc(u0 & 0xff, fp)) return -2; return 0; } /*------------------------------------------------*/ /* Writes 4 byte unsigned long to file, MSB first. */ static short aiffWBlong (unsigned long u0, FILE* fp) { unsigned long u1 = u0 >> 8; unsigned long u2 = u1 >> 8; if (EOF == putc((int)(u2 >> 8), fp)) return -1; if (EOF == putc((int)(u2 & 0xff), fp)) return -2; if (EOF == putc((int)(u1 & 0xff), fp)) return -3; if (EOF == putc((int)(u0 & 0xff), fp)) return -4; return 0; } /*----------------------------------------------------------------------- C O N V E R T T O I E E E E X T E N D E D Machine-independent I/O routines for IEEE floating-point numbers. NaN's and infinities are converted to HUGE_VAL or HUGE, which happens to be infinity on IEEE machines. Unfortunately, it is impossible to preserve NaN's in a machine-independent way. Infinities are, however, preserved on IEEE machines. These routines have been tested on the following machines: Apple Macintosh, MPW 3.1 C compiler Apple Macintosh, THINK C compiler Silicon Graphics IRIS, MIPS compiler Cray X/MP and Y/MP Digital Equipment VAX Implemented by Malcolm Slaney and Ken Turkowski. Malcolm Slaney contributions during 1988-1990 include big- and little- endian file I/O, conversion to and from Motorola's extended 80-bit floating-point format, and conversions to and from IEEE single- precision floating-point format. In 1991, Ken Turkowski implemented the conversions to and from IEEE double-precision format, added more precision to the extended conversions, and accommodated conversions involving +/- infinity, NaN's, and denormalized numbers. -----------------------------------------------------------------------*/ #ifndef HUGE_VAL # define HUGE_VAL HUGE #endif /*HUGE_VAL*/ #define FloatToUnsigned(f) ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1) static void ConvertToIeeeExtended(double num, char *bytes) { int sign, expon; double fMant, fsMant; unsigned long hiMant, loMant; if (num < 0) { sign = 0x8000; num *= -1; } else { sign = 0; } if (num == 0) { expon = 0; hiMant = 0; loMant = 0; } else { fMant = frexp(num, &expon); if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */ } else { /* Finite */ expon += 16382; if (expon < 0) { /* denormalized */ fMant = ldexp(fMant, expon); expon = 0; } expon |= sign; fMant = ldexp(fMant, 32); fsMant = floor(fMant); hiMant = FloatToUnsigned(fsMant); fMant = ldexp(fMant - fsMant, 32); fsMant = floor(fMant); loMant = FloatToUnsigned(fsMant); } } bytes[0] = expon >> 8; bytes[1] = expon; bytes[2] = (char)(hiMant >> 24); /* Cast to char to prevent warnings. */ bytes[3] = (char)(hiMant >> 16); bytes[4] = (char)(hiMant >> 8); bytes[5] = (char)hiMant; bytes[6] = (char)(loMant >> 24); bytes[7] = (char)(loMant >> 16); bytes[8] = (char)(loMant >> 8); bytes[9] = (char)loMant; } /*--------------------------------------------------------------------------------------------*/ static short aiffWriteHeader (AUDIOFILEp A) /* Only used within this file, no need to call it */ { /* directly. Call aiffOpenForWriting() and */ short e; /* and aiffCloseForWriting() instead. */ unsigned long numBytes; /* Returns -10 on failure. */ char buf[10]; numBytes = A->frames * (unsigned long)(A->bits >> 3) * A->channels; e = (EOF == fputs("FORM", A->fp)); /* Write IFF header. */ e |= aiffWBlong(8+18+8+12 + numBytes, A->fp); /* COMM hdr + COMM chunk + */ /* SSND hdr + SSND chunk + numBytes. */ e |= (EOF == fputs("AIFF", A->fp)); /* File type */ /*---------------------------------------------------- COMM chunk ------------------------*/ e |= (EOF == fputs("COMM", A->fp)); /* Describes encoding and #frames. */ e |= aiffWBlong ((long)18, A->fp); /* COMM chunk size. */ e |= aiffWBshort(A->channels, A->fp); /* Number of channels. */ e |= aiffWBlong (A->frames, A->fp); /* Number of sampleframes. */ e |= aiffWBshort(A->bits, A->fp); /* Sample width, in bits. */ ConvertToIeeeExtended((double)A->rate, buf); e |= (-10 + fwrite(buf, 1, 10, A->fp)); /* Samplerate in cycles per second. */ /*----------------------------------------------------- SSND chunk -----------------------*/ e |= (EOF == fputs("SSND", A->fp)); e |= aiffWBlong(8 + numBytes, A->fp); /* Chunk size. */ e |= aiffWBlong((long)0L, A->fp); /* Offset. */ e |= aiffWBlong((long)0L, A->fp); /* Block size. */ if (e) e = -10; /* -13 = Error (re)writing header. */ return e; } /*--------------------------------------------------------------------------------*/ short openAIFFWRITE(AUDIOFILEp* A, /* Handle receiving AUDIOFILE object. */ const char* filename, /* Null-terminated string. */ long samplerate, /* In cycles per second. */ short channels, /* Number of interleaved channels. */ short bits) /* Number of bits per channel. */ { short s, err; /* First check arguments. */ if (!A) return -1; /* -1 = No handle. */ if (!filename) return -2; /* -2 = No filename. */ if (!*filename) return -3; /* -3 = Empty filename. */ if ((channels < kMIN_channels) || (channels > kMAX_channels)) return -4; /* -4 = Wrong number of channels. */ if ((bits & 7) || (bits < 8) || (bits > (kMAX_bytes << 3))) /* Must be multiple of 8, between 8 and 32. */ return -5; /* -5 = Wrong number of bits. */ if ((samplerate < kMIN_samplerate) || (samplerate > kMAX_samplerate)) return -6; /* -6 = Invalid samplerate. */ *A = malloc(sizeof(struct AUDIOFILE)); if (!*A) return -7; /* -7 = Out of memory. */ (*A)->fp = fopen(filename, "wb"); /* Try to create a new aiff file: */ if (!(*A)->fp) /* 'b' for platform-compatibility. */ { free(*A); *A = NULL; return -8; } /* -8 = Cannot create AIFF file. */ (*A)->filename = malloc(strlen(filename) + 1); /* Strdup() may not be there. */ if ((*A)->filename == NULL) { fclose((*A)->fp); free(*A); *A = NULL; return -9; } strcpy((*A)->filename, filename); /* Derive scaling- and rounding-constants that can be used later in function */ /* dumpAIFFWRITE(). */ (*A)->shiftRight= 54 - bits; /* Determine how many times */ (*A)->fractMask = 0x003FFFFFFFFFFFFFLL >> bits; /* output has to rightshifted. */ (*A)->half = 0x0020000000000000LL >> bits; /* Determine where the roun- */ /* 8 bit: 0x00qq3FFFFFFFFFFF. ding-bit (0.5) should sit. */ /* 0x00qq200000000000. */ /* 18 +1 +45 -> 46 for 8-bit output. */ /* 16 bit: 0x00qqqq2000000000. */ /* 26 +1 +37 -> 38 for 16-bit output. */ /* 24 bit: 0x00qqqqqq20000000. */ /* 34 +1 +29 -> 30 for 24-bit output. */ /* 32 bit: 0x00qqqqqqqq200000. */ /* 42 +1 +21 -> 22 for 32-bit output. */ s = 32 - bits; (*A)->overflow = (SIGNED64)0x000000007FFFFFFFLL >> s; /* Calculate highest and low- */ (*A)->underflow = (SIGNED64)0xFFFFFFFF80000000LL >> s; /* est possible sample values. */ /* CARE: The latter typecast appears to be essential to make under- */ /* flow negative! printf("UF=%lld, negative?\n", (*A)->underflow); printf("->shiftRight=%d\n", (int)(*A)->shiftRight); // Unsigned short. printf("->fractMask=%lld\n", (*A)->fractMask); // Signed long long. printf("->half=%lld\n", (*A)->half); // Signed long long. */ (*A)->peak = 0LL; /* Reset peak-measurement. */ (*A)->peak_neg = 0LL; (*A)->rate = samplerate; /* Copy arguments into struct. */ (*A)->channels = channels; (*A)->bits = bits; (*A)->bytes = bits >> 3; (*A)->frames = kMAX_frames; /* Set header to max instead of 0, so when */ /* we abort without calling closeAIFFWRITE(), */ err = aiffWriteHeader(*A); /* audiofile content is still accessable. */ (*A)->frames = 0L; /* Reset sampleframe counter. */ if (err) /* Error -10. */ { fclose((*A)->fp); free((*A)->filename); free(*A); *A = NULL; } return err; } /*----------------------------------------------------------------------------*/ short dumpAIFFWRITE(AUDIOFILEp A, const SIGNED64* audio, UNSIGNED32 frames) { short e = 0; while (frames--) { UNSIGNED16 c; for (c = A->channels; c--;) { UNSIGNED16 b; SIGNED64 a, p; a = *audio++; if ((a >= 0LL) || ((a & A->fractMask) != A->half)) a += A->half; a >>= A->shiftRight; p = a; /* Peak measurement. */ if (p > 0LL) /* (There is no 64-bit abs() function.) */ p *= (SIGNED64)-1LL; if (p < A->peak_neg) { A->peak = a; A->peak_neg = p; /* Absolute value * -1 so it always fits. */ } b = A->bytes - 1; /* Sure b > 0. */ A->buffer[b] = (UNSIGNED8)a; /* Copy lower byte to buffer. */ while (b--) { a >>= 8; A->buffer[b] = (UNSIGNED8)a; /* Copy lower byte to buffer. */ } /* This works correctly on both big and little endians. */ if ((size_t)A->bytes != fwrite(A->buffer, 1, A->bytes, A->fp)) return 2; /* 2 = write error. */ } A->frames++; /* Only count completed frames. */ } if ((A->peak < A->underflow) || (A->peak > A->overflow)) e = 1; /* 1 = over- or underflow. */ if (A->frames > kMAX_frames) /* Care for your disk drives. */ { if (!e) e = 3; } /* 3 = file is becoming huge (but */ return e; /* overshoot is more important). */ } /*----------------------------------------------------------------------------*/ void printAIFFWRITE (AUDIOFILEp A, FILE* to) { double secs, p; const char* line = "------------------------------------------\n"; if (A && to) { fprintf(to, line); fprintf(to, " audio file: %s\n", A->filename); fprintf(to, " channels: %d\n", A->channels); fprintf(to, " frames: %ld\n", A->frames); fprintf(to, " bits: %d\n", A->bits); fprintf(to, " samplerate: %ld Hz\n", A->rate); /*--------------------------------------- DERIVED INFO: ----------*/ fprintf(to, " bytes per frame: %d\n", A->channels * A->bytes); fprintf(to, " filesize: %ld bytes\n", 8+18+8+12+8 + (A->frames * A->bytes * A->channels)); fprintf(to, " duration: "); secs = (double)A->frames / (double)A->rate; if (secs >= 3600.0) fprintf(to, "%.2f hours\n", secs/3600.0); if (secs >= 60.0) fprintf(to, "%.2f minutes\n", secs/60.0); else fprintf(to, "%.2f seconds\n", secs); /*--------------------------------------- PEAK MEASUREMENT: ----------*/ fprintf(to, " peak value: %lld", A->peak); if (A->peak >= 0LL) p = (double)A->overflow; else p = (double)A->underflow; p = 100.0 * (double)A->peak / p; fprintf(to, " (%.2f%%)\n", p); /* Over 100% when clipped. */ fprintf(to, line); } } /*----------------------------------------------------------------------------*/ short closeAIFFWRITE (AUDIOFILEp* A) { short err = 0; if (!A) return -1; /* -1 = No handle. */ if (!*A) return -2; /* -2 = Already closed. */ if (fseek((*A)->fp, 0L, 0)) err = -3; /* -3 = Can't rewind to rewrite. */ else err = aiffWriteHeader(*A); /* -10, -11, -12, or -13. */ if (fclose((*A)->fp)) err += -100; /* Closing AIFF file failed. */ free((*A)->filename); free(*A); /* Sure it's there. */ *A = (AUDIOFILEp)NULL; return err; }