diff --git a/audio.c b/audio.c index ad63fda..8390145 100644 --- a/audio.c +++ b/audio.c @@ -1,1533 +1,1533 @@ -/* WaveGain - Filename: AUDIO.C - * - * Function: Essentially provides all the wave input and output routines. - * - * Copyright (c) 2002 - 2005 John Edwards - * - * 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 - * - * Portions, (c) Michael Smith - * Portions from Vorbize, (c) Kenneth Arnold - * and libvorbis examples, (c) Monty - * - * AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty - */ - - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#else -# ifndef __APPLE__ -# include -# endif -#endif - -#include - -#ifndef __APPLE__ -#include -#endif - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "audio.h" -#include "i18n.h" -#include "misc.h" - -/* Wrappers for mkstemp / _mktemp_s to a common API - * Always return an open file stream or NULL on error - */ -#ifdef _WIN32 -/* In Windows, _mktemp_s() simply returns a filename from template, - * so fopen is used. Note this is not race-safe! - * _mktemp_s() requires - */ -FILE* fmkstemp(char *template) { - if (_mktemp_s(template, strlen(template)) == 0) - return fopen(template, "wb"); - else - return NULL; -} -#else -/* In POSIX, mkstemp() already opens a file, but not as a stream, - * so fdopen is used. This is race-safe. - * mkstemp() requires - * fdopen() requires - */ -#include -FILE* fmkstemp(char *template) { - int fd = mkstemp(template); - if (fd != -1) - return fdopen(fd, "wb"); - else { - remove(template); - close(fd); - return NULL; - } -} -#endif - -/* Macros to read header data */ -#define READ_U32_LE(buf) \ - (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff)) - -#define READ_U16_LE(buf) \ - (((buf)[1]<<8)|((buf)[0]&0xff)) - -#define READ_U32_BE(buf) \ - (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff)) - -#define READ_U16_BE(buf) \ - (((buf)[0]<<8)|((buf)[1]&0xff)) - -#ifdef __APPLE__ - #define READ_D64 read_d64_be - #define WRITE_D64 write_d64_be -#else - #define READ_D64 read_d64_le - #define WRITE_D64 write_d64_le -#endif - -static unsigned char pcm_guid[16] = -{ - /* (00000001-0000-0010-8000-00aa00389b71) */ - - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; - -static unsigned char ieee_float_guid[16] = -{ - /* (00000003-0000-0010-8000-00aa00389b71) */ - - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; - -/* Define the supported formats here */ -input_format formats[] = { - {wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")}, - {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")}, - {NULL, 0, NULL, NULL, NULL, NULL} -}; - -#if (defined (WIN32) || defined (_WIN32)) -extern int __cdecl _fseeki64(FILE *, Int64_t, int); -extern Int64_t __cdecl _ftelli64(FILE *); - - #define FSEEK64 _fseeki64 - #define FTELL64 _ftelli64 -#else - #define FSEEK64 fseeko - #define FTELL64 ftello -#endif - -/* Global */ -Int64_t current_pos_t; - -#if (defined (WIN32) || defined (_WIN32)) -__inline long int lrint(double flt) -{ - int intgr; - - _asm { - fld flt - fistp intgr - } - - return intgr; -} -/* We're not touching this as we don't want these assembler routines on GHz Intel Core i7 */ -#elif (defined (__MACOSX__)) -#define lrint double2int -inline static long -double2int (register double in) -{ int res [2] ; - - __asm__ __volatile__ - ( "fctiw %1, %1\n\t" - "stfd %1, %0" - : "=m" (res) /* Output */ - : "f" (in) /* Input */ - : "memory" - ) ; - - return res [1] ; -} -#else -#define lrint double2int -static inline long double2int (double in) -{ long retval ; - - __asm__ __volatile__ - ( "fistpl %0" - : "=m" (retval) - : "t" (in) - : "st" - ) ; - - return retval ; -} -#endif - -double read_d64_be(unsigned char *cptr) -{ - int exponent, negative; - double dvalue; - - negative = (cptr [0] & 0x80) ? 1 : 0; - exponent = ((cptr [0] & 0x7F) << 4) | ((cptr [1] >> 4) & 0xF); - - /* Might not have a 64 bit long, so load the mantissa into a double. */ - dvalue = (((cptr [1] & 0xF) << 24) | (cptr [2] << 16) | (cptr [3] << 8) | cptr [4]); - dvalue += ((cptr [5] << 16) | (cptr [6] << 8) | cptr [7]) / ((double) 0x1000000); - - if (exponent == 0 && dvalue == 0.0) - return 0.0; - - dvalue += 0x10000000; - - exponent = exponent - 0x3FF; - - dvalue = dvalue / ((double) 0x10000000); - - if (negative) - dvalue *= -1; - - if (exponent > 0) - dvalue *= (1 << exponent); - else if (exponent < 0) - dvalue /= (1 << abs (exponent)); - - return dvalue; -} - -double read_d64_le(unsigned char *cptr) -{ - int exponent, negative; - double dvalue; - - negative = (cptr [7] & 0x80) ? 1 : 0; - exponent = ((cptr [7] & 0x7F) << 4) | ((cptr [6] >> 4) & 0xF); - - /* Might not have a 64 bit long, so load the mantissa into a double. */ - dvalue = (((cptr [6] & 0xF) << 24) | (cptr [5] << 16) | (cptr [4] << 8) | cptr [3]); - dvalue += ((cptr [2] << 16) | (cptr [1] << 8) | cptr [0]) / ((double) 0x1000000); - - if (exponent == 0 && dvalue == 0.0) - return 0.0; - - dvalue += 0x10000000; - - exponent = exponent - 0x3FF; - - dvalue = dvalue / ((double) 0x10000000); - - if (negative) - dvalue *= -1; - - if (exponent > 0) - dvalue *= (1 << exponent); - else if (exponent < 0) - dvalue /= (1 << abs (exponent)); - - return dvalue; -} - -void write_d64_be(unsigned char *out, double in) -{ - int exponent, mantissa; - - memset (out, 0, sizeof (double)); - - if (in == 0.0) - return; - - if (in < 0.0) { - in *= -1.0; - out [0] |= 0x80; - } - - in = frexp (in, &exponent); - - exponent += 1022; - - out [0] |= (exponent >> 4) & 0x7F; - out [1] |= (exponent << 4) & 0xF0; - - in *= 0x20000000; - mantissa = lrint (floor (in)); - - out [1] |= (mantissa >> 24) & 0xF; - out [2] = (mantissa >> 16) & 0xFF; - out [3] = (mantissa >> 8) & 0xFF; - out [4] = mantissa & 0xFF; - - in = fmod (in, 1.0); - in *= 0x1000000; - mantissa = lrint (floor (in)); - - out [5] = (mantissa >> 16) & 0xFF; - out [6] = (mantissa >> 8) & 0xFF; - out [7] = mantissa & 0xFF; - - return; -} - -void write_d64_le(unsigned char *out, double in) -{ - int exponent, mantissa; - - memset (out, 0, sizeof (double)); - - if (in == 0.0) - return; - - if (in < 0.0) { - in *= -1.0; - out [7] |= 0x80; - } - - in = frexp (in, &exponent); - - exponent += 1022; - - out [7] |= (exponent >> 4) & 0x7F; - out [6] |= (exponent << 4) & 0xF0; - - in *= 0x20000000; - mantissa = lrint (floor (in)); - - out [6] |= (mantissa >> 24) & 0xF; - out [5] = (mantissa >> 16) & 0xFF; - out [4] = (mantissa >> 8) & 0xFF; - out [3] = mantissa & 0xFF; - - in = fmod (in, 1.0); - in *= 0x1000000; - mantissa = lrint (floor (in)); - - out [2] = (mantissa >> 16) & 0xFF; - out [1] = (mantissa >> 8) & 0xFF; - out [0] = mantissa & 0xFF; - - return; -} - -input_format *open_audio_file(FILE *in, wavegain_opt *opt) -{ - int j = 0; - unsigned char *buf = NULL; - int buf_size=0, buf_filled = 0; - int size, ret; - - while (formats[j].id_func) { - size = formats[j].id_data_len; - if (size >= buf_size) { - buf = realloc(buf, size); - buf_size = size; - } - - if (size > buf_filled) { - ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in); - buf_filled += ret; - - if (buf_filled < size) { - /* File truncated */ - j++; - continue; - } - } - - if (formats[j].id_func(buf, buf_filled)) { - /* ok, we now have something that can handle the file */ - if (formats[j].open_func(in, opt, buf, buf_filled)) { - free(buf); - return &formats[j]; - } - } - j++; - } - - free(buf); - - return NULL; -} - -static int seek_forward(FILE *in, Int64_t length) -{ - if (FSEEK64(in, length, SEEK_CUR)) { - /* Failed. Do it the hard way. */ - unsigned char buf[1024]; - int seek_needed = length, seeked; - while (seek_needed > 0) { - seeked = fread(buf, 1, seek_needed > 1024 ? 1024:seek_needed, in); - if (!seeked) - return 0; /* Couldn't read more, can't read file */ - else - seek_needed -= seeked; - } - } - return 1; -} - - -static int find_wav_chunk(FILE *in, char *type, Int64_t *len) -{ - unsigned char buf[8]; - - while (1) { - if (fread(buf,1,8,in) < 8) { - /* Suck down a chunk specifier */ - if (memcmp(type, "gain", 4)) - fprintf(stderr, "Warning: Unexpected EOF in reading WAV header (1)\n"); - return 0; /* EOF before reaching the appropriate chunk */ - } - - if (memcmp(buf, type, 4)) { - *len = READ_U32_LE(buf+4); - if (!seek_forward(in, *len)) - return 0; - - buf[4] = 0; - } - else { - *len = READ_U32_LE(buf+4); - return 1; - } - } - return 0; /* unreachable */ -} - -static int find_gain_chunk(FILE *in, Int64_t *len) -{ - unsigned char buf[8]; - - if (fread(buf,1,8,in) < 8) { - /* Suck down a chunk specifier */ - fprintf(stderr, "Warning: Unexpected EOF in reading WAV header (3)\n"); - return 0; /* EOF before reaching the appropriate chunk */ - } - - if (!memcmp(buf, "gain", 4)) { - *len = READ_U32_LE(buf+4); - return 1; - } - else { - return 0; - } -} - -static int find_aiff_chunk(FILE *in, char *type, unsigned int *len) -{ - unsigned char buf[8]; - - while (1) { - if (fread(buf,1,8,in) <8) { - fprintf(stderr, "Warning: Unexpected EOF in AIFF chunk\n"); - return 0; - } - - *len = READ_U32_BE(buf+4); - - if (memcmp(buf,type,4)) { - if ((*len) & 0x1) - (*len)++; - - if (!seek_forward(in, *len)) - return 0; - } - else - return 1; - } - return 0; /* unreachable */ -} - - - -double read_IEEE80(unsigned char *buf) -{ - int s = buf[0] & 0xff; - int e = ((buf[0] & 0x7f) <<8) | (buf[1] & 0xff); - double f = ((unsigned long)(buf[2] & 0xff) << 24)| - ((buf[3] & 0xff) << 16)| - ((buf[4] & 0xff) << 8) | - (buf[5] & 0xff); - - if (e == 32767) { - if (buf[2] & 0x80) - return HUGE_VAL; /* Really NaN, but this won't happen in reality */ - else { - if (s) - return -HUGE_VAL; - else - return HUGE_VAL; - } - } - - f = ldexp(f, 32); - f += ((buf[6] & 0xff) << 24)| - ((buf[7] & 0xff) << 16)| - ((buf[8] & 0xff) << 8) | - (buf[9] & 0xff); - - return ldexp(f, e-16446); -} - -/* AIFF/AIFC support adapted from the old OggSQUISH application */ -int aiff_id(unsigned char *buf, int len) -{ - if (len < 12) return 0; /* Truncated file, probably */ - - if (memcmp(buf, "FORM", 4)) - return 0; - - if (memcmp(buf + 8, "AIF",3)) - return 0; - - if (buf[11] != 'C' && buf[11] != 'F') - return 0; - - return 1; -} - -int aiff_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen) -{ - int aifc; /* AIFC or AIFF? */ - unsigned int len; - unsigned char *buffer; - unsigned char buf2[8]; - aiff_fmt format; - aifffile *aiff = malloc(sizeof(aifffile)); - - if (buf[11] == 'C') - aifc = 1; - else { - aifc = 0; - opt->format = WAV_FMT_AIFF; - } - - if (!find_aiff_chunk(in, "COMM", &len)) { - fprintf(stderr, "Warning: No common chunk found in AIFF file\n"); - return 0; /* EOF before COMM chunk */ - } - - if (len < 18) { - fprintf(stderr, "Warning: Truncated common chunk in AIFF header\n"); - return 0; /* Weird common chunk */ - } - - buffer = alloca(len); - - if (fread(buffer, 1, len, in) < len) { - fprintf(stderr, "Warning: Unexpected EOF in reading AIFF header\n"); - return 0; - } - - format.channels = READ_U16_BE(buffer); - format.totalframes = READ_U32_BE(buffer + 2); - format.samplesize = READ_U16_BE(buffer + 6); - format.rate = (int)read_IEEE80(buffer + 8); - - aiff->bigendian = BIG; - opt->endianness = BIG; - - if (aifc) { - if (len < 22) { - fprintf(stderr, "Warning: AIFF-C header truncated.\n"); - return 0; - } - - if (!memcmp(buffer + 18, "NONE", 4)) { - aiff->bigendian = BIG; - opt->endianness = BIG; - } - else if (!memcmp(buffer + 18, "sowt", 4)) { - aiff->bigendian = LITTLE; - opt->endianness = LITTLE; - } - else { - fprintf(stderr, "Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n", *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21)); - return 0; /* Compressed. Can't handle */ - } - } - - if (!find_aiff_chunk(in, "SSND", &len)) { - fprintf(stderr, "Warning: No SSND chunk found in AIFF file\n"); - return 0; /* No SSND chunk -> no actual audio */ - } - - if (len < 8) { - fprintf(stderr, "Warning: Corrupted SSND chunk in AIFF header\n"); - return 0; - } - - if (fread(buf2, 1, 8, in) < 8) { - fprintf(stderr, "Warning: Unexpected EOF reading AIFF header\n"); - return 0; - } - - format.offset = READ_U32_BE(buf2); - format.blocksize = READ_U32_BE(buf2+4); - - if ( format.blocksize == 0 && (format.samplesize == 16 || format.samplesize == 8)) { - /* From here on, this is very similar to the wav code. Oh well. */ - - opt->rate = format.rate; - opt->channels = format.channels; - opt->read_samples = wav_read; /* Similar enough, so we use the same */ - opt->total_samples_per_channel = format.totalframes; - opt->samplesize = format.samplesize; - if (aifc && format.samplesize == 8) - opt->format = WAV_FMT_AIFC8; - else if (aifc && format.samplesize == 16) - opt->format = WAV_FMT_AIFC16; - opt->header_size = 0; - opt->header = NULL; - opt->rate = format.rate; - aiff->f = in; - aiff->samplesread = 0; - aiff->channels = format.channels; - aiff->samplesize = format.samplesize; - aiff->totalsamples = format.totalframes; - - opt->readdata = (void *)aiff; - - seek_forward(in, format.offset); /* Swallow some data */ - return 1; - } - else { - fprintf(stderr, "Warning: WaveGain does not support this type of AIFF/AIFC file\n" - " Must be 8 or 16 bit PCM.\n"); - return 0; - } -} - -int wav_id(unsigned char *buf, int len) -{ - /*unsigned int flen = READ_U32_LE(buf + 4);*/ /* We don't use this */; - - if (len < 12) return 0; /* Something screwed up */ - - if (memcmp(buf, "RIFF", 4)) - return 0; /* Not wave */ - - if (memcmp(buf + 8, "WAVE",4)) - return 0; /* RIFF, but not wave */ - - return 1; -} - -int wav_open(FILE *in, wavegain_opt *opt, unsigned char *oldbuf, int buflen) -{ - unsigned char buf[81]; - Int64_t len; - Int64_t current_pos; - int samplesize; - wav_fmt format; - wavfile *wav = malloc(sizeof(wavfile)); - - /* Ok. At this point, we know we have a WAV file. Now we have to detect - * whether we support the subtype, and we have to find the actual data - * We don't (for the wav reader) need to use the buffer we used to id this - * as a wav file (oldbuf) - */ - - if (!find_wav_chunk(in, "fmt ", &len)) { - fprintf(stderr, "Warning: Failed to find fmt chunk in reading WAV header\n"); - return 0; /* EOF */ - } - - if (len < 16) { - fprintf(stderr, "Warning: Unrecognised format chunk in WAV header\n"); - return 0; /* Weird format chunk */ - } - - /* A common error is to have a format chunk that is not 16 or 18 bytes - * in size. This is incorrect, but not fatal, so we only warn about - * it instead of refusing to work with the file. Please, if you - * have a program that's creating format chunks of sizes other than - * 16, 18 or 40 bytes in size, report a bug to the author. - * (40 bytes accommodates WAVEFORMATEXTENSIBLE conforming files.) - */ - if (len != 16 && len != 18 && len != 40) - fprintf(stderr, "Warning: INVALID format chunk in wav header.\n" - " Trying to read anyway (may not work)...\n"); - - /* Deal with stupid broken apps. Don't use these programs. - */ - - if (fread(buf,1,len,in) < len) { - fprintf(stderr, "Warning: Unexpected EOF in reading WAV header\n"); - return 0; - } - - format.format = READ_U16_LE(buf); - format.channels = READ_U16_LE(buf+2); - format.samplerate = READ_U32_LE(buf+4); - format.bytespersec = READ_U32_LE(buf+8); - format.align = READ_U16_LE(buf+12); - format.samplesize = READ_U16_LE(buf+14); - - if (!opt->std_in) { - current_pos = FTELL64(in); - if (!find_gain_chunk(in, &len)) - FSEEK64(in, current_pos, SEEK_SET); - else { - char buf_double[8]; - opt->gain_chunk = 1; - fread(buf_double, 1, 8, in); - opt->gain_scale = READ_D64(buf_double); - } - } - - if (!find_wav_chunk(in, "data", &len)) { - fprintf(stderr, "Warning: Failed to find data chunk in reading WAV header\n"); - return 0; /* EOF */ - } - - if (opt->apply_gain) { - current_pos = FTELL64(in); - current_pos_t = current_pos + len; - FSEEK64(in, 0, SEEK_SET); - if ((opt->header = malloc(sizeof(char) * current_pos)) == NULL) - fprintf(stderr, "Error: unable to allocate memory for header\n"); - else { - opt->header_size = current_pos; - fread(opt->header, 1, opt->header_size, in); - } - FSEEK64(in, current_pos, SEEK_SET); - } - - if(format.format == WAVE_FORMAT_PCM) { - samplesize = format.samplesize/8; - opt->read_samples = wav_read; - /* works with current enum */ - opt->format = samplesize; - } - else if(format.format == WAVE_FORMAT_IEEE_FLOAT) { - samplesize = 4; - opt->read_samples = wav_ieee_read; - opt->endianness = LITTLE; - opt->format = WAV_FMT_FLOAT; - } - else if (format.format == WAVE_FORMAT_EXTENSIBLE) { - format.channel_mask = READ_U32_LE(buf+20); - if (format.channel_mask > 3) { - fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard 1 or 2 channel PCM\n" - " or type 3 floating point PCM)(2)\n"); - return 0; - } - if (memcmp(buf+24, pcm_guid, 16) == 0) { - samplesize = format.samplesize/8; - opt->read_samples = wav_read; - /* works with current enum */ - opt->format = samplesize; - } - else if (memcmp(buf+24, ieee_float_guid, 16) == 0) { - samplesize = 4; - opt->read_samples = wav_ieee_read; - opt->endianness = LITTLE; - opt->format = WAV_FMT_FLOAT; - } - else { - fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard PCM\n" - " or type 3 floating point PCM)(2)\n"); - return 0; - } - } - else { - fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard PCM\n" - " or type 3 floating point PCM\n"); - return 0; - } - - opt->samplesize = format.samplesize; - - if(format.align != format.channels * samplesize) { - /* This is incorrect according to the spec. Warn loudly, then ignore - * this value. - */ - fprintf(stderr, _("Warning: WAV 'block alignment' value is incorrect, ignoring.\n" - "The software that created this file is incorrect.\n")); - } - if ( format.align == format.channels * samplesize && - format.samplesize == samplesize * 8 && - (format.samplesize == 32 || format.samplesize == 24 || - format.samplesize == 16 || format.samplesize == 8)) { - /* OK, good - we have the one supported format, - now we want to find the size of the file */ - opt->rate = format.samplerate; - opt->channels = format.channels; - - wav->f = in; - wav->samplesread = 0; - wav->bigendian = 0; - opt->endianness = LITTLE; - wav->channels = format.channels; /* This is in several places. The price - of trying to abstract stuff. */ - wav->samplesize = format.samplesize; - - if (len) - opt->total_samples_per_channel = len/(format.channels*samplesize); - else { - Int64_t pos; - pos = FTELL64(in); - if (FSEEK64(in, 0, SEEK_END) == -1) - opt->total_samples_per_channel = 0; /* Give up */ - else { - opt->total_samples_per_channel = (FTELL64(in) - pos)/(format.channels * samplesize); - FSEEK64(in, pos, SEEK_SET); - } - } - wav->totalsamples = opt->total_samples_per_channel; - - opt->readdata = (void *)wav; - return 1; - } - else { - fprintf(stderr, "ERROR: Wav file is unsupported subformat (must be 8, 16, 24 or 32 bit PCM\n" - "or floating point PCM)\n"); - return 0; - } -} - -long wav_read(void *in, double **buffer, int samples, int fast, int chunk) -{ - wavfile *f = (wavfile *)in; - int sampbyte = f->samplesize / 8; - signed char *buf = alloca(samples*sampbyte*f->channels); - long bytes_read; - int i, j; - long realsamples; - - if (fast) { - chunk /= (sampbyte * f->channels); - chunk *= (sampbyte * f->channels); - FSEEK64(f->f, chunk, SEEK_SET); - } - - bytes_read = fread(buf, 1, samples * sampbyte * f->channels, f->f); - - if (f->totalsamples && f->samplesread + bytes_read / (sampbyte * f->channels) > f->totalsamples) { - bytes_read = sampbyte * f->channels * (f->totalsamples - f->samplesread); - } - - realsamples = bytes_read / (sampbyte * f->channels); - f->samplesread += realsamples; - - if (f->samplesize == 8) { - unsigned char *bufu = (unsigned char *)buf; - for (i = 0; i < realsamples; i++) { - for (j = 0; j < f->channels; j++) - buffer[j][i] = ((int)(bufu[i * f->channels + j]) - 128) / 128.0; - } - } - else if (f->samplesize==16) { -#ifdef __APPLE__ - if (f->bigendian != machine_endianness) { -#else - if (f->bigendian == machine_endianness) { -#endif - for (i = 0; i < realsamples; i++) { - for (j = 0; j < f->channels; j++) - buffer[j][i] = ((buf[i * 2 * f->channels + 2 * j + 1] <<8) | - (buf[i * 2 * f->channels + 2 * j] & 0xff)) / 32768.0; - } - } - else { - for (i = 0; i < realsamples; i++) { - for (j = 0; j < f->channels; j++) - buffer[j][i]=((buf[i * 2 * f->channels + 2 * j] << 8) | - (buf[i * 2 * f->channels + 2 * j + 1] & 0xff)) / 32768.0; - } - } - } - else if (f->samplesize == 24) { -#ifdef __APPLE__ - if (f->bigendian != machine_endianness) { -#else - if (f->bigendian == machine_endianness) { -#endif - for (i = 0; i < realsamples; i++) { - for (j = 0; j < f->channels; j++) - buffer[j][i] = ((buf[i * 3 * f->channels + 3 * j + 2] << 16) | - (((unsigned char *)buf)[i * 3 * f->channels + 3 * j + 1] << 8) | - (((unsigned char *)buf)[i * 3 * f->channels + 3 * j] & 0xff)) - / 8388608.0; - } - } - else { - fprintf(stderr, "Big endian 24 bit PCM data is not currently " - "supported, aborting.\n"); - return 0; - } - } - else if (f->samplesize == 32) { -#ifdef __APPLE__ - if (f->bigendian != machine_endianness) { -#else - if (f->bigendian == machine_endianness) { -#endif - for (i = 0; i < realsamples; i++) { - for (j = 0; j < f->channels; j++) - buffer[j][i] = ((buf[i * 4 * f->channels + 4 * j + 3] << 24) | - (((unsigned char *)buf)[i * 4 * f->channels + 4 * j + 2] << 16) | - (((unsigned char *)buf)[i * 4 * f->channels + 4 * j + 1] << 8) | - (((unsigned char *)buf)[i * 4 * f->channels + 4 * j] & 0xff)) - / 2147483648.0; - } - } - else { - fprintf(stderr, "Big endian 32 bit PCM data is not currently " - "supported, aborting.\n"); - return 0; - } - } - else { - fprintf(stderr, "Internal error: attempt to read unsupported " - "bitdepth %d\n", f->samplesize); - return 0; - } - - return realsamples; -} - -long wav_ieee_read(void *in, double **buffer, int samples, int fast, int chunk) -{ - wavfile *f = (wavfile *)in; - float *buf = alloca(samples * 4 * f->channels); /* de-interleave buffer */ - long bytes_read; - int i,j; - long realsamples; - - if (fast) { - chunk /= (sizeof(float) * f->channels); - chunk *= (sizeof(float) * f->channels); - FSEEK64(f->f, chunk, SEEK_SET); - } - - bytes_read = fread(buf, 1, samples * 4 * f->channels, f->f); - - if (f->totalsamples && f->samplesread + bytes_read / (4 * f->channels) > f->totalsamples) - bytes_read = 4 * f->channels * (f->totalsamples - f->samplesread); - realsamples = bytes_read / (4 * f->channels); - f->samplesread += realsamples; - - for (i = 0; i < realsamples; i++) - for (j = 0; j < f->channels; j++) - buffer[j][i] = buf[i * f->channels + j]; - - return realsamples; -} - - -void wav_close(void *info) -{ - wavfile *f = (wavfile *)info; - - free(f); -} - -int raw_open(FILE *in, wavegain_opt *opt) -{ - wav_fmt format; /* fake wave header ;) */ - wavfile *wav = malloc(sizeof(wavfile)); - - /* construct fake wav header ;) */ - format.format = 2; - format.channels = opt->channels; - format.samplerate = opt->rate; - format.samplesize = opt->samplesize; - format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8; - format.align = format.bytespersec; - wav->f = in; - wav->samplesread = 0; - wav->bigendian = opt->endianness; - wav->channels = format.channels; - wav->samplesize = opt->samplesize; - wav->totalsamples = 0; - - opt->read_samples = wav_read; - opt->readdata = (void *)wav; - opt->total_samples_per_channel = 0; /* raw mode, don't bother */ - return 1; -} - -/* - * W A V E O U T P U T - */ - -audio_file *open_output_audio_file(char *infile, wavegain_opt *opt) -{ - audio_file *aufile = malloc(sizeof(audio_file)); - - aufile->outputFormat = opt->format; - - aufile->samplerate = opt->rate; - aufile->channels = opt->channels; - aufile->samples = 0; - aufile->endianness = opt->endianness; - aufile->bits_per_sample = opt->samplesize; - - if (opt->std_out) { - aufile->sndfile = stdout; -#ifdef _WIN32 - _setmode( _fileno(stdout), _O_BINARY ); -#endif - } - else - aufile->sndfile = fmkstemp(infile); - - if (aufile->sndfile == NULL) { - if (aufile) - free(aufile); - return NULL; - } - - switch (aufile->outputFormat) { - case WAV_FMT_AIFF: - write_aiff_header(aufile); - break; - case WAV_FMT_8BIT: - case WAV_FMT_16BIT: - case WAV_FMT_24BIT: - case WAV_FMT_32BIT: - case WAV_FMT_FLOAT: { - unsigned int file_size = 0xffffffff; - write_wav_header(aufile, opt, file_size); - } - break; - } - - return aufile; -} - -int write_audio_file(audio_file *aufile, void *sample_buffer, int samples) -{ - switch (aufile->outputFormat) { - case WAV_FMT_8BIT: - return write_audio_8bit(aufile, sample_buffer, samples); - case WAV_FMT_16BIT: - case WAV_FMT_AIFF: - return write_audio_16bit(aufile, sample_buffer, samples); - case WAV_FMT_24BIT: - return write_audio_24bit(aufile, sample_buffer, samples); - case WAV_FMT_32BIT: - return write_audio_32bit(aufile, sample_buffer, samples); - case WAV_FMT_FLOAT: - return write_audio_float(aufile, sample_buffer, samples); - default: - return 0; - } - - return 0; -} - -void close_audio_file( FILE *in, audio_file *aufile, wavegain_opt *opt) -{ - unsigned char *ch; - Int64_t pos; - - if (!opt->std_out) { - - switch (aufile->outputFormat) { - case WAV_FMT_AIFF: - FSEEK64(aufile->sndfile, 0, SEEK_SET); - write_aiff_header(aufile); - break; - case WAV_FMT_8BIT: - case WAV_FMT_16BIT: - case WAV_FMT_24BIT: - case WAV_FMT_32BIT: - case WAV_FMT_FLOAT: { - FSEEK64(in, 0, SEEK_END); - pos = FTELL64 (in); - if ((pos - current_pos_t) > 0) { - FSEEK64 (in, current_pos_t, SEEK_SET); - ch = malloc (sizeof(char) * (pos - current_pos_t)); - - fread (ch, 1, pos - current_pos_t, in); - fwrite (ch, pos - current_pos_t, 1, aufile->sndfile); - - if (ch) - free (ch); - } - FSEEK64(aufile->sndfile, 0, SEEK_END); - pos = FTELL64 (aufile->sndfile); - FSEEK64(aufile->sndfile, 0, SEEK_SET); - write_wav_header(aufile, opt, pos - 8); - break; - } - } - } - - if(opt->header) - free(opt->header); - if(opt) - free(opt); - - fclose(aufile->sndfile); - - if (aufile) - free(aufile); -} - -#define WRITE_U32(buf, x) *(buf) = (unsigned char)((x)&0xff);\ - *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\ - *((buf)+2) = (unsigned char)(((x)>>16)&0xff);\ - *((buf)+3) = (unsigned char)(((x)>>24)&0xff); - -#define WRITE_U16(buf, x) *(buf) = (unsigned char)((x)&0xff);\ - *((buf)+1) = (unsigned char)(((x)>>8)&0xff); - -int write_wav_header(audio_file *aufile, wavegain_opt *opt, Int64_t file_size) -{ - unsigned short channels = opt->channels; - unsigned long samplerate = opt->rate; - unsigned long bytespersec = opt->channels * opt->rate * opt->samplesize / 8; - unsigned short align = opt->channels * opt->samplesize / 8; - unsigned short samplesize = opt->samplesize; - unsigned long size = file_size; - unsigned long data_size = aufile->samples * (opt->samplesize / 8) < 0xffffffff ? - aufile->samples * (opt->samplesize / 8) : 0xffffffff; - - unsigned long sz_fmt; - unsigned char *p = opt->header; - unsigned char *q; - unsigned char *r; -// unsigned char buf[8]; - unsigned long chunks_to_process; - unsigned long no_of_chunks_processed = 0; - - if ((opt->force || opt->undo) && opt->gain_chunk) - chunks_to_process = 3; - else - chunks_to_process = 2; - - for (;;) { - if (!memcmp(p, "RIFF", 4)) { - p += 4; - WRITE_U32(p, size); - p += 4; - no_of_chunks_processed++; - } - if (!memcmp(p, "fmt ", 4)) { - unsigned long fmt_length = 0; - p += 4; - sz_fmt = READ_U32_LE(p); - p += 4; - if (aufile->outputFormat == WAV_FMT_FLOAT) { - WRITE_U16(p, 3); - } - else { - WRITE_U16(p, 1); - } - p += 2; - fmt_length += 2; - WRITE_U16(p, channels); - p += 2; - fmt_length += 2; - WRITE_U32(p, samplerate); - p += 4; - fmt_length += 4; - WRITE_U32(p, bytespersec); - p += 4; - fmt_length += 4; - WRITE_U16(p, align); - p += 2; - fmt_length += 2; - WRITE_U16(p, samplesize); - p += 2; - fmt_length += 2; - p += sz_fmt - fmt_length; - no_of_chunks_processed++; - } - if (chunks_to_process == 3) { - if (!memcmp(p, "gain", 4)) { - p += 8; - WRITE_D64(p, opt->gain_scale); - p += 8; - no_of_chunks_processed++; - } - } - if (no_of_chunks_processed == chunks_to_process) - break; - else - p++; - } - - if (opt->write_chunk == 1 && !opt->gain_chunk) { - if ((q = malloc(sizeof(char) * (opt->header_size + 16))) == NULL) - fprintf(stderr, "Error: unable to allocate memory for header\n"); - else { - r = q; - memcpy(r, opt->header, p - opt->header); - r += (p - opt->header); - memcpy(r, "gain", 4); - r += 4; - WRITE_U32(r, 8); - r += 4; -// buf = (unsigned char *)&opt->gain_scale; - WRITE_D64(r, opt->gain_scale); - r += 8; - } - - memcpy(r, p, (opt->header_size - (p - opt->header))); - - r = q + opt->header_size + 16 - 8; - if (!memcmp(r, "data", 4)) { - r += 4; - WRITE_U32(r, data_size); - r += 4; - fwrite(q, opt->header_size + 16, 1, aufile->sndfile); - } - else - fprintf(stderr, "Error: unable to write header\n"); - if(q) free(q); - } - else { - p = opt->header + opt->header_size - 8; - if (!memcmp(p, "data", 4)) { - p += 4; - WRITE_U32(p, data_size); - p += 4; - fwrite(opt->header, opt->header_size, 1, aufile->sndfile); - } - } - - return 1; -} - -/* - * Write a 80 bit IEEE854 big endian number as 10 octets. Destination is passed as pointer, - * End of destination (p+10) is returned. - */ - -static unsigned char* Convert_to_80bit_BE_IEEE854_Float(unsigned char* p, long double val ) -{ - unsigned long word32 = 0x401E; - - if (val > 0.L) - while (val < (long double)0x80000000) // scales value in the range 2^31...2^32 - word32--, val *= 2.L; // so you have the exponent - - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); // write exponent, sign is assumed as '+' - word32 = (unsigned long) val; - *p++ = (unsigned char)(word32 >> 24); - *p++ = (unsigned char)(word32 >> 16); - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); // write the upper 32 bit of the mantissa - word32 = (unsigned long) ((val - word32) * 4294967296.L); - *p++ = (unsigned char)(word32 >> 24); - *p++ = (unsigned char)(word32 >> 16); - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); // write the lower 32 bit of the mantissa - - return p; -} - -int write_aiff_header(audio_file *aufile) -{ - unsigned char header[54], - *p = header; - unsigned int bytes = (aufile->bits_per_sample + 7) / 8; - unsigned long data_size = aufile->samples * bytes; - unsigned long word32; - - // FORM chunk - *p++ = 'F'; - *p++ = 'O'; - *p++ = 'R'; - *p++ = 'M'; - - word32 = data_size + 0x2E; // size of the AIFF chunk - *p++ = (unsigned char)(word32 >> 24); - *p++ = (unsigned char)(word32 >> 16); - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); - - *p++ = 'A'; - *p++ = 'I'; - *p++ = 'F'; - *p++ = 'F'; - // end of FORM chunk - - // COMM chunk - *p++ = 'C'; - *p++ = 'O'; - *p++ = 'M'; - *p++ = 'M'; - - word32 = 0x12; // size of this chunk - *p++ = (unsigned char)(word32 >> 24); - *p++ = (unsigned char)(word32 >> 16); - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); - - word32 = aufile->channels; // channels - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); - - word32 = aufile->samples / aufile->channels; // no. of sample frames - *p++ = (unsigned char)(word32 >> 24); - *p++ = (unsigned char)(word32 >> 16); - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); - - word32 = aufile->bits_per_sample; // bits - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); - - p = Convert_to_80bit_BE_IEEE854_Float (p, (long double)aufile->samplerate); // sample frequency as big endian 80 bit IEEE854 float - // End of COMM chunk - - // SSND chunk - *p++ = 'S'; - *p++ = 'S'; - *p++ = 'N'; - *p++ = 'D'; - - word32 = data_size + 0x08; // chunk length - *p++ = (unsigned char)(word32 >> 24); - *p++ = (unsigned char)(word32 >> 16); - *p++ = (unsigned char)(word32 >> 8); - *p++ = (unsigned char)(word32 >> 0); - - *p++ = 0; // offset - *p++ = 0; - *p++ = 0; - *p++ = 0; - - *p++ = 0; // block size - *p++ = 0; - *p++ = 0; - *p++ = 0; - - return fwrite(header, sizeof(header), 1, aufile->sndfile); -} - -int write_audio_8bit(audio_file *aufile, void *sample_buffer, unsigned int samples) -{ - int ret; - unsigned int i; - unsigned char *sample_buffer8 = (unsigned char*)sample_buffer; - unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); - - aufile->samples += samples; - - for (i = 0; i < samples; i++) - data[i] = (sample_buffer8[i]+128) & 0xFF; - - ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); - - if (data) - free(data); - - return ret; -} - -int write_audio_16bit(audio_file *aufile, void *sample_buffer, unsigned int samples) -{ - int ret; - unsigned int i; - short *sample_buffer16 = (short*)sample_buffer; - char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); - - aufile->samples += samples; - -#ifdef __APPLE__ - if (aufile->endianness != machine_endianness) { -#else - if (aufile->endianness == machine_endianness) { -#endif - for (i = 0; i < samples; i++) { - data[i*2] = (char)(sample_buffer16[i] & 0xFF); - data[i*2+1] = (char)((sample_buffer16[i] >> 8) & 0xFF); - } - } - else { - for (i = 0; i < samples; i++) { - data[i*2+1] = (char)(sample_buffer16[i] & 0xFF); - data[i*2] = (char)((sample_buffer16[i] >> 8) & 0xFF); - } - } - - ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); - - if (data) - free(data); - - return ret; -} - -int write_audio_24bit(audio_file *aufile, void *sample_buffer, unsigned int samples) -{ - int ret; - unsigned int i; - long *sample_buffer24 = (long*)sample_buffer; - char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); - - aufile->samples += samples; - - for (i = 0; i < samples; i++) { - data[i*3] = (char)(sample_buffer24[i] & 0xFF); - data[i*3+1] = (char)((sample_buffer24[i] >> 8) & 0xFF); - data[i*3+2] = (char)((sample_buffer24[i] >> 16) & 0xFF); - } - - ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); - - if (data) - free(data); - - return ret; -} - -int write_audio_32bit(audio_file *aufile, void *sample_buffer, unsigned int samples) -{ - int ret; - unsigned int i; - long *sample_buffer32 = (long*)sample_buffer; - char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); - - aufile->samples += samples; - - for (i = 0; i < samples; i++) { - data[i*4] = (char)(sample_buffer32[i] & 0xFF); - data[i*4+1] = (char)((sample_buffer32[i] >> 8) & 0xFF); - data[i*4+2] = (char)((sample_buffer32[i] >> 16) & 0xFF); - data[i*4+3] = (char)((sample_buffer32[i] >> 24) & 0xFF); - } - - ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); - - if (data) - free(data); - - return ret; -} - -int write_audio_float(audio_file *aufile, void *sample_buffer, unsigned int samples) -{ - int ret; - unsigned int i; - float *sample_buffer_f = (float*)sample_buffer; - unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); - - aufile->samples += samples; - - for (i = 0; i < samples; i++) { - int exponent, - mantissa, - negative = 0; - float in = sample_buffer_f[i]; - - data[i*4] = 0; - data[i*4+1] = 0; - data[i*4+2] = 0; - data[i*4+3] = 0; - if (in == 0.f) - continue; - - if (in < 0.0) { - in *= -1.0; - negative = 1; - } - in = (float)frexp(in, &exponent); - exponent += 126; - in *= (float)0x1000000; - mantissa = (((int)in) & 0x7FFFFF); - - if (negative) - data[i*4+3] |= 0x80; - - if (exponent & 0x01) - data[i*4+2] |= 0x80; - - data[i*4] = mantissa & 0xFF; - data[i*4+1] = (mantissa >> 8) & 0xFF; - data[i*4+2] |= (mantissa >> 16) & 0x7F; - data[i*4+3] |= (exponent >> 1) & 0x7F; - } - - ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); - - if (data) - free(data); - - return ret; -} - -void* output_to_PCM(double **input, void *sample_buffer, int channels, - int samples, int format) -{ - unsigned char ch; - int i; - - char *char_sample_buffer = (char*)sample_buffer; - short *short_sample_buffer = (short*)sample_buffer; - int *int_sample_buffer = (int*)sample_buffer; - float *float_sample_buffer = (float*)sample_buffer; - - /* - * Copy output to a standard PCM buffer - */ - switch (format) { - case WAV_FMT_8BIT: - for (ch = 0; ch < channels; ch++) { - for (i = 0; i < samples; i++) { - char_sample_buffer[(i*channels)+ch] = (char)input[ch][i]; - } - } - break; - case WAV_FMT_AIFF: - case WAV_FMT_16BIT: - for (ch = 0; ch < channels; ch++) { - for (i = 0; i < samples; i++) { - short_sample_buffer[(i*channels)+ch] = (short)input[ch][i]; - } - } - break; - case WAV_FMT_24BIT: - case WAV_FMT_32BIT: - for (ch = 0; ch < channels; ch++) { - for (i = 0; i < samples; i++) { - int_sample_buffer[(i*channels)+ch] = (int)input[ch][i]; - } - } - break; - case WAV_FMT_FLOAT: - for (ch = 0; ch < channels; ch++) { - for (i = 0; i < samples; i++) { - float_sample_buffer[(i*channels)+ch] = (float)input[ch][i]; - } - } - break; - } - - return sample_buffer; -} - -/* - * end of audio.c - */ +/* WaveGain - Filename: AUDIO.C + * + * Function: Essentially provides all the wave input and output routines. + * + * Copyright (c) 2002 - 2005 John Edwards + * + * 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 + * + * Portions, (c) Michael Smith + * Portions from Vorbize, (c) Kenneth Arnold + * and libvorbis examples, (c) Monty + * + * AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty + */ + + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +# ifndef __APPLE__ +# include +# endif +#endif + +#include + +#ifndef __APPLE__ +#include +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "audio.h" +#include "i18n.h" +#include "misc.h" + +/* Wrappers for mkstemp / _mktemp_s to a common API + * Always return an open file stream or NULL on error + */ +#ifdef _WIN32 +/* In Windows, _mktemp_s() simply returns a filename from template, + * so fopen is used. Note this is not race-safe! + * _mktemp_s() requires + */ +FILE* fmkstemp(char *template) { + if (_mktemp_s(template, strlen(template)) == 0) + return fopen(template, "wb"); + else + return NULL; +} +#else +/* In POSIX, mkstemp() already opens a file, but not as a stream, + * so fdopen is used. This is race-safe. + * mkstemp() requires + * fdopen() requires + */ +#include +FILE* fmkstemp(char *template) { + int fd = mkstemp(template); + if (fd != -1) + return fdopen(fd, "wb"); + else { + remove(template); + close(fd); + return NULL; + } +} +#endif + +/* Macros to read header data */ +#define READ_U32_LE(buf) \ + (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff)) + +#define READ_U16_LE(buf) \ + (((buf)[1]<<8)|((buf)[0]&0xff)) + +#define READ_U32_BE(buf) \ + (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff)) + +#define READ_U16_BE(buf) \ + (((buf)[0]<<8)|((buf)[1]&0xff)) + +#ifdef __APPLE__ + #define READ_D64 read_d64_be + #define WRITE_D64 write_d64_be +#else + #define READ_D64 read_d64_le + #define WRITE_D64 write_d64_le +#endif + +static unsigned char pcm_guid[16] = +{ + /* (00000001-0000-0010-8000-00aa00389b71) */ + + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +}; + +static unsigned char ieee_float_guid[16] = +{ + /* (00000003-0000-0010-8000-00aa00389b71) */ + + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +}; + +/* Define the supported formats here */ +input_format formats[] = { + {wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")}, + {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")}, + {NULL, 0, NULL, NULL, NULL, NULL} +}; + +#if (defined (WIN32) || defined (_WIN32)) +extern int __cdecl _fseeki64(FILE *, Int64_t, int); +extern Int64_t __cdecl _ftelli64(FILE *); + + #define FSEEK64 _fseeki64 + #define FTELL64 _ftelli64 +#else + #define FSEEK64 fseeko + #define FTELL64 ftello +#endif + +/* Global */ +Int64_t current_pos_t; + +#if (defined (WIN32) || defined (_WIN32)) +__inline long int lrint(double flt) +{ + int intgr; + + _asm { + fld flt + fistp intgr + } + + return intgr; +} +/* We're not touching this as we don't want these assembler routines on GHz Intel Core i7 */ +#elif (defined (__MACOSX__)) +#define lrint double2int +inline static long +double2int (register double in) +{ int res [2] ; + + __asm__ __volatile__ + ( "fctiw %1, %1\n\t" + "stfd %1, %0" + : "=m" (res) /* Output */ + : "f" (in) /* Input */ + : "memory" + ) ; + + return res [1] ; +} +#else +#define lrint double2int +static inline long double2int (double in) +{ long retval ; + + __asm__ __volatile__ + ( "fistpl %0" + : "=m" (retval) + : "t" (in) + : "st" + ) ; + + return retval ; +} +#endif + +double read_d64_be(unsigned char *cptr) +{ + int exponent, negative; + double dvalue; + + negative = (cptr [0] & 0x80) ? 1 : 0; + exponent = ((cptr [0] & 0x7F) << 4) | ((cptr [1] >> 4) & 0xF); + + /* Might not have a 64 bit long, so load the mantissa into a double. */ + dvalue = (((cptr [1] & 0xF) << 24) | (cptr [2] << 16) | (cptr [3] << 8) | cptr [4]); + dvalue += ((cptr [5] << 16) | (cptr [6] << 8) | cptr [7]) / ((double) 0x1000000); + + if (exponent == 0 && dvalue == 0.0) + return 0.0; + + dvalue += 0x10000000; + + exponent = exponent - 0x3FF; + + dvalue = dvalue / ((double) 0x10000000); + + if (negative) + dvalue *= -1; + + if (exponent > 0) + dvalue *= (1 << exponent); + else if (exponent < 0) + dvalue /= (1 << abs (exponent)); + + return dvalue; +} + +double read_d64_le(unsigned char *cptr) +{ + int exponent, negative; + double dvalue; + + negative = (cptr [7] & 0x80) ? 1 : 0; + exponent = ((cptr [7] & 0x7F) << 4) | ((cptr [6] >> 4) & 0xF); + + /* Might not have a 64 bit long, so load the mantissa into a double. */ + dvalue = (((cptr [6] & 0xF) << 24) | (cptr [5] << 16) | (cptr [4] << 8) | cptr [3]); + dvalue += ((cptr [2] << 16) | (cptr [1] << 8) | cptr [0]) / ((double) 0x1000000); + + if (exponent == 0 && dvalue == 0.0) + return 0.0; + + dvalue += 0x10000000; + + exponent = exponent - 0x3FF; + + dvalue = dvalue / ((double) 0x10000000); + + if (negative) + dvalue *= -1; + + if (exponent > 0) + dvalue *= (1 << exponent); + else if (exponent < 0) + dvalue /= (1 << abs (exponent)); + + return dvalue; +} + +void write_d64_be(unsigned char *out, double in) +{ + int exponent, mantissa; + + memset (out, 0, sizeof (double)); + + if (in == 0.0) + return; + + if (in < 0.0) { + in *= -1.0; + out [0] |= 0x80; + } + + in = frexp (in, &exponent); + + exponent += 1022; + + out [0] |= (exponent >> 4) & 0x7F; + out [1] |= (exponent << 4) & 0xF0; + + in *= 0x20000000; + mantissa = lrint (floor (in)); + + out [1] |= (mantissa >> 24) & 0xF; + out [2] = (mantissa >> 16) & 0xFF; + out [3] = (mantissa >> 8) & 0xFF; + out [4] = mantissa & 0xFF; + + in = fmod (in, 1.0); + in *= 0x1000000; + mantissa = lrint (floor (in)); + + out [5] = (mantissa >> 16) & 0xFF; + out [6] = (mantissa >> 8) & 0xFF; + out [7] = mantissa & 0xFF; + + return; +} + +void write_d64_le(unsigned char *out, double in) +{ + int exponent, mantissa; + + memset (out, 0, sizeof (double)); + + if (in == 0.0) + return; + + if (in < 0.0) { + in *= -1.0; + out [7] |= 0x80; + } + + in = frexp (in, &exponent); + + exponent += 1022; + + out [7] |= (exponent >> 4) & 0x7F; + out [6] |= (exponent << 4) & 0xF0; + + in *= 0x20000000; + mantissa = lrint (floor (in)); + + out [6] |= (mantissa >> 24) & 0xF; + out [5] = (mantissa >> 16) & 0xFF; + out [4] = (mantissa >> 8) & 0xFF; + out [3] = mantissa & 0xFF; + + in = fmod (in, 1.0); + in *= 0x1000000; + mantissa = lrint (floor (in)); + + out [2] = (mantissa >> 16) & 0xFF; + out [1] = (mantissa >> 8) & 0xFF; + out [0] = mantissa & 0xFF; + + return; +} + +input_format *open_audio_file(FILE *in, wavegain_opt *opt) +{ + int j = 0; + unsigned char *buf = NULL; + int buf_size=0, buf_filled = 0; + int size, ret; + + while (formats[j].id_func) { + size = formats[j].id_data_len; + if (size >= buf_size) { + buf = realloc(buf, size); + buf_size = size; + } + + if (size > buf_filled) { + ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in); + buf_filled += ret; + + if (buf_filled < size) { + /* File truncated */ + j++; + continue; + } + } + + if (formats[j].id_func(buf, buf_filled)) { + /* ok, we now have something that can handle the file */ + if (formats[j].open_func(in, opt, buf, buf_filled)) { + free(buf); + return &formats[j]; + } + } + j++; + } + + free(buf); + + return NULL; +} + +static int seek_forward(FILE *in, Int64_t length) +{ + if (FSEEK64(in, length, SEEK_CUR)) { + /* Failed. Do it the hard way. */ + unsigned char buf[1024]; + int seek_needed = length, seeked; + while (seek_needed > 0) { + seeked = fread(buf, 1, seek_needed > 1024 ? 1024:seek_needed, in); + if (!seeked) + return 0; /* Couldn't read more, can't read file */ + else + seek_needed -= seeked; + } + } + return 1; +} + + +static int find_wav_chunk(FILE *in, char *type, Int64_t *len) +{ + unsigned char buf[8]; + + while (1) { + if (fread(buf,1,8,in) < 8) { + /* Suck down a chunk specifier */ + if (memcmp(type, "gain", 4)) + fprintf(stderr, "Warning: Unexpected EOF in reading WAV header (1)\n"); + return 0; /* EOF before reaching the appropriate chunk */ + } + + if (memcmp(buf, type, 4)) { + *len = READ_U32_LE(buf+4); + if (!seek_forward(in, *len)) + return 0; + + buf[4] = 0; + } + else { + *len = READ_U32_LE(buf+4); + return 1; + } + } + return 0; /* unreachable */ +} + +static int find_gain_chunk(FILE *in, Int64_t *len) +{ + unsigned char buf[8]; + + if (fread(buf,1,8,in) < 8) { + /* Suck down a chunk specifier */ + fprintf(stderr, "Warning: Unexpected EOF in reading WAV header (3)\n"); + return 0; /* EOF before reaching the appropriate chunk */ + } + + if (!memcmp(buf, "gain", 4)) { + *len = READ_U32_LE(buf+4); + return 1; + } + else { + return 0; + } +} + +static int find_aiff_chunk(FILE *in, char *type, unsigned int *len) +{ + unsigned char buf[8]; + + while (1) { + if (fread(buf,1,8,in) <8) { + fprintf(stderr, "Warning: Unexpected EOF in AIFF chunk\n"); + return 0; + } + + *len = READ_U32_BE(buf+4); + + if (memcmp(buf,type,4)) { + if ((*len) & 0x1) + (*len)++; + + if (!seek_forward(in, *len)) + return 0; + } + else + return 1; + } + return 0; /* unreachable */ +} + + + +double read_IEEE80(unsigned char *buf) +{ + int s = buf[0] & 0xff; + int e = ((buf[0] & 0x7f) <<8) | (buf[1] & 0xff); + double f = ((unsigned long)(buf[2] & 0xff) << 24)| + ((buf[3] & 0xff) << 16)| + ((buf[4] & 0xff) << 8) | + (buf[5] & 0xff); + + if (e == 32767) { + if (buf[2] & 0x80) + return HUGE_VAL; /* Really NaN, but this won't happen in reality */ + else { + if (s) + return -HUGE_VAL; + else + return HUGE_VAL; + } + } + + f = ldexp(f, 32); + f += ((buf[6] & 0xff) << 24)| + ((buf[7] & 0xff) << 16)| + ((buf[8] & 0xff) << 8) | + (buf[9] & 0xff); + + return ldexp(f, e-16446); +} + +/* AIFF/AIFC support adapted from the old OggSQUISH application */ +int aiff_id(unsigned char *buf, int len) +{ + if (len < 12) return 0; /* Truncated file, probably */ + + if (memcmp(buf, "FORM", 4)) + return 0; + + if (memcmp(buf + 8, "AIF",3)) + return 0; + + if (buf[11] != 'C' && buf[11] != 'F') + return 0; + + return 1; +} + +int aiff_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen) +{ + int aifc; /* AIFC or AIFF? */ + unsigned int len; + unsigned char *buffer; + unsigned char buf2[8]; + aiff_fmt format; + aifffile *aiff = malloc(sizeof(aifffile)); + + if (buf[11] == 'C') + aifc = 1; + else { + aifc = 0; + opt->format = WAV_FMT_AIFF; + } + + if (!find_aiff_chunk(in, "COMM", &len)) { + fprintf(stderr, "Warning: No common chunk found in AIFF file\n"); + return 0; /* EOF before COMM chunk */ + } + + if (len < 18) { + fprintf(stderr, "Warning: Truncated common chunk in AIFF header\n"); + return 0; /* Weird common chunk */ + } + + buffer = alloca(len); + + if (fread(buffer, 1, len, in) < len) { + fprintf(stderr, "Warning: Unexpected EOF in reading AIFF header\n"); + return 0; + } + + format.channels = READ_U16_BE(buffer); + format.totalframes = READ_U32_BE(buffer + 2); + format.samplesize = READ_U16_BE(buffer + 6); + format.rate = (int)read_IEEE80(buffer + 8); + + aiff->bigendian = BIG; + opt->endianness = BIG; + + if (aifc) { + if (len < 22) { + fprintf(stderr, "Warning: AIFF-C header truncated.\n"); + return 0; + } + + if (!memcmp(buffer + 18, "NONE", 4)) { + aiff->bigendian = BIG; + opt->endianness = BIG; + } + else if (!memcmp(buffer + 18, "sowt", 4)) { + aiff->bigendian = LITTLE; + opt->endianness = LITTLE; + } + else { + fprintf(stderr, "Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n", *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21)); + return 0; /* Compressed. Can't handle */ + } + } + + if (!find_aiff_chunk(in, "SSND", &len)) { + fprintf(stderr, "Warning: No SSND chunk found in AIFF file\n"); + return 0; /* No SSND chunk -> no actual audio */ + } + + if (len < 8) { + fprintf(stderr, "Warning: Corrupted SSND chunk in AIFF header\n"); + return 0; + } + + if (fread(buf2, 1, 8, in) < 8) { + fprintf(stderr, "Warning: Unexpected EOF reading AIFF header\n"); + return 0; + } + + format.offset = READ_U32_BE(buf2); + format.blocksize = READ_U32_BE(buf2+4); + + if ( format.blocksize == 0 && (format.samplesize == 16 || format.samplesize == 8)) { + /* From here on, this is very similar to the wav code. Oh well. */ + + opt->rate = format.rate; + opt->channels = format.channels; + opt->read_samples = wav_read; /* Similar enough, so we use the same */ + opt->total_samples_per_channel = format.totalframes; + opt->samplesize = format.samplesize; + if (aifc && format.samplesize == 8) + opt->format = WAV_FMT_AIFC8; + else if (aifc && format.samplesize == 16) + opt->format = WAV_FMT_AIFC16; + opt->header_size = 0; + opt->header = NULL; + opt->rate = format.rate; + aiff->f = in; + aiff->samplesread = 0; + aiff->channels = format.channels; + aiff->samplesize = format.samplesize; + aiff->totalsamples = format.totalframes; + + opt->readdata = (void *)aiff; + + seek_forward(in, format.offset); /* Swallow some data */ + return 1; + } + else { + fprintf(stderr, "Warning: WaveGain does not support this type of AIFF/AIFC file\n" + " Must be 8 or 16 bit PCM.\n"); + return 0; + } +} + +int wav_id(unsigned char *buf, int len) +{ + /*unsigned int flen = READ_U32_LE(buf + 4);*/ /* We don't use this */; + + if (len < 12) return 0; /* Something screwed up */ + + if (memcmp(buf, "RIFF", 4)) + return 0; /* Not wave */ + + if (memcmp(buf + 8, "WAVE",4)) + return 0; /* RIFF, but not wave */ + + return 1; +} + +int wav_open(FILE *in, wavegain_opt *opt, unsigned char *oldbuf, int buflen) +{ + unsigned char buf[81]; + Int64_t len; + Int64_t current_pos; + int samplesize; + wav_fmt format; + wavfile *wav = malloc(sizeof(wavfile)); + + /* Ok. At this point, we know we have a WAV file. Now we have to detect + * whether we support the subtype, and we have to find the actual data + * We don't (for the wav reader) need to use the buffer we used to id this + * as a wav file (oldbuf) + */ + + if (!find_wav_chunk(in, "fmt ", &len)) { + fprintf(stderr, "Warning: Failed to find fmt chunk in reading WAV header\n"); + return 0; /* EOF */ + } + + if (len < 16) { + fprintf(stderr, "Warning: Unrecognised format chunk in WAV header\n"); + return 0; /* Weird format chunk */ + } + + /* A common error is to have a format chunk that is not 16 or 18 bytes + * in size. This is incorrect, but not fatal, so we only warn about + * it instead of refusing to work with the file. Please, if you + * have a program that's creating format chunks of sizes other than + * 16, 18 or 40 bytes in size, report a bug to the author. + * (40 bytes accommodates WAVEFORMATEXTENSIBLE conforming files.) + */ + if (len != 16 && len != 18 && len != 40) + fprintf(stderr, "Warning: INVALID format chunk in wav header.\n" + " Trying to read anyway (may not work)...\n"); + + /* Deal with stupid broken apps. Don't use these programs. + */ + + if (fread(buf,1,len,in) < len) { + fprintf(stderr, "Warning: Unexpected EOF in reading WAV header\n"); + return 0; + } + + format.format = READ_U16_LE(buf); + format.channels = READ_U16_LE(buf+2); + format.samplerate = READ_U32_LE(buf+4); + format.bytespersec = READ_U32_LE(buf+8); + format.align = READ_U16_LE(buf+12); + format.samplesize = READ_U16_LE(buf+14); + + if (!opt->std_in) { + current_pos = FTELL64(in); + if (!find_gain_chunk(in, &len)) + FSEEK64(in, current_pos, SEEK_SET); + else { + char buf_double[8]; + opt->gain_chunk = 1; + fread(buf_double, 1, 8, in); + opt->gain_scale = READ_D64(buf_double); + } + } + + if (!find_wav_chunk(in, "data", &len)) { + fprintf(stderr, "Warning: Failed to find data chunk in reading WAV header\n"); + return 0; /* EOF */ + } + + if (opt->apply_gain) { + current_pos = FTELL64(in); + current_pos_t = current_pos + len; + FSEEK64(in, 0, SEEK_SET); + if ((opt->header = malloc(sizeof(char) * current_pos)) == NULL) + fprintf(stderr, "Error: unable to allocate memory for header\n"); + else { + opt->header_size = current_pos; + fread(opt->header, 1, opt->header_size, in); + } + FSEEK64(in, current_pos, SEEK_SET); + } + + if(format.format == WAVE_FORMAT_PCM) { + samplesize = format.samplesize/8; + opt->read_samples = wav_read; + /* works with current enum */ + opt->format = samplesize; + } + else if(format.format == WAVE_FORMAT_IEEE_FLOAT) { + samplesize = 4; + opt->read_samples = wav_ieee_read; + opt->endianness = LITTLE; + opt->format = WAV_FMT_FLOAT; + } + else if (format.format == WAVE_FORMAT_EXTENSIBLE) { + format.channel_mask = READ_U32_LE(buf+20); + if (format.channel_mask > 3) { + fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard 1 or 2 channel PCM\n" + " or type 3 floating point PCM)(2)\n"); + return 0; + } + if (memcmp(buf+24, pcm_guid, 16) == 0) { + samplesize = format.samplesize/8; + opt->read_samples = wav_read; + /* works with current enum */ + opt->format = samplesize; + } + else if (memcmp(buf+24, ieee_float_guid, 16) == 0) { + samplesize = 4; + opt->read_samples = wav_ieee_read; + opt->endianness = LITTLE; + opt->format = WAV_FMT_FLOAT; + } + else { + fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard PCM\n" + " or type 3 floating point PCM)(2)\n"); + return 0; + } + } + else { + fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard PCM\n" + " or type 3 floating point PCM\n"); + return 0; + } + + opt->samplesize = format.samplesize; + + if(format.align != format.channels * samplesize) { + /* This is incorrect according to the spec. Warn loudly, then ignore + * this value. + */ + fprintf(stderr, _("Warning: WAV 'block alignment' value is incorrect, ignoring.\n" + "The software that created this file is incorrect.\n")); + } + if ( format.align == format.channels * samplesize && + format.samplesize == samplesize * 8 && + (format.samplesize == 32 || format.samplesize == 24 || + format.samplesize == 16 || format.samplesize == 8)) { + /* OK, good - we have the one supported format, + now we want to find the size of the file */ + opt->rate = format.samplerate; + opt->channels = format.channels; + + wav->f = in; + wav->samplesread = 0; + wav->bigendian = 0; + opt->endianness = LITTLE; + wav->channels = format.channels; /* This is in several places. The price + of trying to abstract stuff. */ + wav->samplesize = format.samplesize; + + if (len) + opt->total_samples_per_channel = len/(format.channels*samplesize); + else { + Int64_t pos; + pos = FTELL64(in); + if (FSEEK64(in, 0, SEEK_END) == -1) + opt->total_samples_per_channel = 0; /* Give up */ + else { + opt->total_samples_per_channel = (FTELL64(in) - pos)/(format.channels * samplesize); + FSEEK64(in, pos, SEEK_SET); + } + } + wav->totalsamples = opt->total_samples_per_channel; + + opt->readdata = (void *)wav; + return 1; + } + else { + fprintf(stderr, "ERROR: Wav file is unsupported subformat (must be 8, 16, 24 or 32 bit PCM\n" + "or floating point PCM)\n"); + return 0; + } +} + +long wav_read(void *in, double **buffer, int samples, int fast, int chunk) +{ + wavfile *f = (wavfile *)in; + int sampbyte = f->samplesize / 8; + signed char *buf = alloca(samples*sampbyte*f->channels); + long bytes_read; + int i, j; + long realsamples; + + if (fast) { + chunk /= (sampbyte * f->channels); + chunk *= (sampbyte * f->channels); + FSEEK64(f->f, chunk, SEEK_SET); + } + + bytes_read = fread(buf, 1, samples * sampbyte * f->channels, f->f); + + if (f->totalsamples && f->samplesread + bytes_read / (sampbyte * f->channels) > f->totalsamples) { + bytes_read = sampbyte * f->channels * (f->totalsamples - f->samplesread); + } + + realsamples = bytes_read / (sampbyte * f->channels); + f->samplesread += realsamples; + + if (f->samplesize == 8) { + unsigned char *bufu = (unsigned char *)buf; + for (i = 0; i < realsamples; i++) { + for (j = 0; j < f->channels; j++) + buffer[j][i] = ((int)(bufu[i * f->channels + j]) - 128) / 128.0; + } + } + else if (f->samplesize==16) { +#ifdef __APPLE__ + if (f->bigendian != machine_endianness) { +#else + if (f->bigendian == machine_endianness) { +#endif + for (i = 0; i < realsamples; i++) { + for (j = 0; j < f->channels; j++) + buffer[j][i] = ((buf[i * 2 * f->channels + 2 * j + 1] <<8) | + (buf[i * 2 * f->channels + 2 * j] & 0xff)) / 32768.0; + } + } + else { + for (i = 0; i < realsamples; i++) { + for (j = 0; j < f->channels; j++) + buffer[j][i]=((buf[i * 2 * f->channels + 2 * j] << 8) | + (buf[i * 2 * f->channels + 2 * j + 1] & 0xff)) / 32768.0; + } + } + } + else if (f->samplesize == 24) { +#ifdef __APPLE__ + if (f->bigendian != machine_endianness) { +#else + if (f->bigendian == machine_endianness) { +#endif + for (i = 0; i < realsamples; i++) { + for (j = 0; j < f->channels; j++) + buffer[j][i] = ((buf[i * 3 * f->channels + 3 * j + 2] << 16) | + (((unsigned char *)buf)[i * 3 * f->channels + 3 * j + 1] << 8) | + (((unsigned char *)buf)[i * 3 * f->channels + 3 * j] & 0xff)) + / 8388608.0; + } + } + else { + fprintf(stderr, "Big endian 24 bit PCM data is not currently " + "supported, aborting.\n"); + return 0; + } + } + else if (f->samplesize == 32) { +#ifdef __APPLE__ + if (f->bigendian != machine_endianness) { +#else + if (f->bigendian == machine_endianness) { +#endif + for (i = 0; i < realsamples; i++) { + for (j = 0; j < f->channels; j++) + buffer[j][i] = ((buf[i * 4 * f->channels + 4 * j + 3] << 24) | + (((unsigned char *)buf)[i * 4 * f->channels + 4 * j + 2] << 16) | + (((unsigned char *)buf)[i * 4 * f->channels + 4 * j + 1] << 8) | + (((unsigned char *)buf)[i * 4 * f->channels + 4 * j] & 0xff)) + / 2147483648.0; + } + } + else { + fprintf(stderr, "Big endian 32 bit PCM data is not currently " + "supported, aborting.\n"); + return 0; + } + } + else { + fprintf(stderr, "Internal error: attempt to read unsupported " + "bitdepth %d\n", f->samplesize); + return 0; + } + + return realsamples; +} + +long wav_ieee_read(void *in, double **buffer, int samples, int fast, int chunk) +{ + wavfile *f = (wavfile *)in; + float *buf = alloca(samples * 4 * f->channels); /* de-interleave buffer */ + long bytes_read; + int i,j; + long realsamples; + + if (fast) { + chunk /= (sizeof(float) * f->channels); + chunk *= (sizeof(float) * f->channels); + FSEEK64(f->f, chunk, SEEK_SET); + } + + bytes_read = fread(buf, 1, samples * 4 * f->channels, f->f); + + if (f->totalsamples && f->samplesread + bytes_read / (4 * f->channels) > f->totalsamples) + bytes_read = 4 * f->channels * (f->totalsamples - f->samplesread); + realsamples = bytes_read / (4 * f->channels); + f->samplesread += realsamples; + + for (i = 0; i < realsamples; i++) + for (j = 0; j < f->channels; j++) + buffer[j][i] = buf[i * f->channels + j]; + + return realsamples; +} + + +void wav_close(void *info) +{ + wavfile *f = (wavfile *)info; + + free(f); +} + +int raw_open(FILE *in, wavegain_opt *opt) +{ + wav_fmt format; /* fake wave header ;) */ + wavfile *wav = malloc(sizeof(wavfile)); + + /* construct fake wav header ;) */ + format.format = 2; + format.channels = opt->channels; + format.samplerate = opt->rate; + format.samplesize = opt->samplesize; + format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8; + format.align = format.bytespersec; + wav->f = in; + wav->samplesread = 0; + wav->bigendian = opt->endianness; + wav->channels = format.channels; + wav->samplesize = opt->samplesize; + wav->totalsamples = 0; + + opt->read_samples = wav_read; + opt->readdata = (void *)wav; + opt->total_samples_per_channel = 0; /* raw mode, don't bother */ + return 1; +} + +/* + * W A V E O U T P U T + */ + +audio_file *open_output_audio_file(char *infile, wavegain_opt *opt) +{ + audio_file *aufile = malloc(sizeof(audio_file)); + + aufile->outputFormat = opt->format; + + aufile->samplerate = opt->rate; + aufile->channels = opt->channels; + aufile->samples = 0; + aufile->endianness = opt->endianness; + aufile->bits_per_sample = opt->samplesize; + + if (opt->std_out) { + aufile->sndfile = stdout; +#ifdef _WIN32 + _setmode( _fileno(stdout), _O_BINARY ); +#endif + } + else + aufile->sndfile = fmkstemp(infile); + + if (aufile->sndfile == NULL) { + if (aufile) + free(aufile); + return NULL; + } + + switch (aufile->outputFormat) { + case WAV_FMT_AIFF: + write_aiff_header(aufile); + break; + case WAV_FMT_8BIT: + case WAV_FMT_16BIT: + case WAV_FMT_24BIT: + case WAV_FMT_32BIT: + case WAV_FMT_FLOAT: { + unsigned int file_size = 0xffffffff; + write_wav_header(aufile, opt, file_size); + } + break; + } + + return aufile; +} + +int write_audio_file(audio_file *aufile, void *sample_buffer, int samples) +{ + switch (aufile->outputFormat) { + case WAV_FMT_8BIT: + return write_audio_8bit(aufile, sample_buffer, samples); + case WAV_FMT_16BIT: + case WAV_FMT_AIFF: + return write_audio_16bit(aufile, sample_buffer, samples); + case WAV_FMT_24BIT: + return write_audio_24bit(aufile, sample_buffer, samples); + case WAV_FMT_32BIT: + return write_audio_32bit(aufile, sample_buffer, samples); + case WAV_FMT_FLOAT: + return write_audio_float(aufile, sample_buffer, samples); + default: + return 0; + } + + return 0; +} + +void close_audio_file( FILE *in, audio_file *aufile, wavegain_opt *opt) +{ + unsigned char *ch; + Int64_t pos; + + if (!opt->std_out) { + + switch (aufile->outputFormat) { + case WAV_FMT_AIFF: + FSEEK64(aufile->sndfile, 0, SEEK_SET); + write_aiff_header(aufile); + break; + case WAV_FMT_8BIT: + case WAV_FMT_16BIT: + case WAV_FMT_24BIT: + case WAV_FMT_32BIT: + case WAV_FMT_FLOAT: { + FSEEK64(in, 0, SEEK_END); + pos = FTELL64 (in); + if ((pos - current_pos_t) > 0) { + FSEEK64 (in, current_pos_t, SEEK_SET); + ch = malloc (sizeof(char) * (pos - current_pos_t)); + + fread (ch, 1, pos - current_pos_t, in); + fwrite (ch, pos - current_pos_t, 1, aufile->sndfile); + + if (ch) + free (ch); + } + FSEEK64(aufile->sndfile, 0, SEEK_END); + pos = FTELL64 (aufile->sndfile); + FSEEK64(aufile->sndfile, 0, SEEK_SET); + write_wav_header(aufile, opt, pos - 8); + break; + } + } + } + + if(opt->header) + free(opt->header); + if(opt) + free(opt); + + fclose(aufile->sndfile); + + if (aufile) + free(aufile); +} + +#define WRITE_U32(buf, x) *(buf) = (unsigned char)((x)&0xff);\ + *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\ + *((buf)+2) = (unsigned char)(((x)>>16)&0xff);\ + *((buf)+3) = (unsigned char)(((x)>>24)&0xff); + +#define WRITE_U16(buf, x) *(buf) = (unsigned char)((x)&0xff);\ + *((buf)+1) = (unsigned char)(((x)>>8)&0xff); + +int write_wav_header(audio_file *aufile, wavegain_opt *opt, Int64_t file_size) +{ + unsigned short channels = opt->channels; + unsigned long samplerate = opt->rate; + unsigned long bytespersec = opt->channels * opt->rate * opt->samplesize / 8; + unsigned short align = opt->channels * opt->samplesize / 8; + unsigned short samplesize = opt->samplesize; + unsigned long size = file_size; + unsigned long data_size = aufile->samples * (opt->samplesize / 8) < 0xffffffff ? + aufile->samples * (opt->samplesize / 8) : 0xffffffff; + + unsigned long sz_fmt; + unsigned char *p = opt->header; + unsigned char *q; + unsigned char *r; +// unsigned char buf[8]; + unsigned long chunks_to_process; + unsigned long no_of_chunks_processed = 0; + + if ((opt->force || opt->undo) && opt->gain_chunk) + chunks_to_process = 3; + else + chunks_to_process = 2; + + for (;;) { + if (!memcmp(p, "RIFF", 4)) { + p += 4; + WRITE_U32(p, size); + p += 4; + no_of_chunks_processed++; + } + if (!memcmp(p, "fmt ", 4)) { + unsigned long fmt_length = 0; + p += 4; + sz_fmt = READ_U32_LE(p); + p += 4; + if (aufile->outputFormat == WAV_FMT_FLOAT) { + WRITE_U16(p, 3); + } + else { + WRITE_U16(p, 1); + } + p += 2; + fmt_length += 2; + WRITE_U16(p, channels); + p += 2; + fmt_length += 2; + WRITE_U32(p, samplerate); + p += 4; + fmt_length += 4; + WRITE_U32(p, bytespersec); + p += 4; + fmt_length += 4; + WRITE_U16(p, align); + p += 2; + fmt_length += 2; + WRITE_U16(p, samplesize); + p += 2; + fmt_length += 2; + p += sz_fmt - fmt_length; + no_of_chunks_processed++; + } + if (chunks_to_process == 3) { + if (!memcmp(p, "gain", 4)) { + p += 8; + WRITE_D64(p, opt->gain_scale); + p += 8; + no_of_chunks_processed++; + } + } + if (no_of_chunks_processed == chunks_to_process) + break; + else + p++; + } + + if (opt->write_chunk == 1 && !opt->gain_chunk) { + if ((q = malloc(sizeof(char) * (opt->header_size + 16))) == NULL) + fprintf(stderr, "Error: unable to allocate memory for header\n"); + else { + r = q; + memcpy(r, opt->header, p - opt->header); + r += (p - opt->header); + memcpy(r, "gain", 4); + r += 4; + WRITE_U32(r, 8); + r += 4; +// buf = (unsigned char *)&opt->gain_scale; + WRITE_D64(r, opt->gain_scale); + r += 8; + } + + memcpy(r, p, (opt->header_size - (p - opt->header))); + + r = q + opt->header_size + 16 - 8; + if (!memcmp(r, "data", 4)) { + r += 4; + WRITE_U32(r, data_size); + r += 4; + fwrite(q, opt->header_size + 16, 1, aufile->sndfile); + } + else + fprintf(stderr, "Error: unable to write header\n"); + if(q) free(q); + } + else { + p = opt->header + opt->header_size - 8; + if (!memcmp(p, "data", 4)) { + p += 4; + WRITE_U32(p, data_size); + p += 4; + fwrite(opt->header, opt->header_size, 1, aufile->sndfile); + } + } + + return 1; +} + +/* + * Write a 80 bit IEEE854 big endian number as 10 octets. Destination is passed as pointer, + * End of destination (p+10) is returned. + */ + +static unsigned char* Convert_to_80bit_BE_IEEE854_Float(unsigned char* p, long double val ) +{ + unsigned long word32 = 0x401E; + + if (val > 0.L) + while (val < (long double)0x80000000) // scales value in the range 2^31...2^32 + word32--, val *= 2.L; // so you have the exponent + + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); // write exponent, sign is assumed as '+' + word32 = (unsigned long) val; + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); // write the upper 32 bit of the mantissa + word32 = (unsigned long) ((val - word32) * 4294967296.L); + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); // write the lower 32 bit of the mantissa + + return p; +} + +int write_aiff_header(audio_file *aufile) +{ + unsigned char header[54], + *p = header; + unsigned int bytes = (aufile->bits_per_sample + 7) / 8; + unsigned long data_size = aufile->samples * bytes; + unsigned long word32; + + // FORM chunk + *p++ = 'F'; + *p++ = 'O'; + *p++ = 'R'; + *p++ = 'M'; + + word32 = data_size + 0x2E; // size of the AIFF chunk + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + *p++ = 'A'; + *p++ = 'I'; + *p++ = 'F'; + *p++ = 'F'; + // end of FORM chunk + + // COMM chunk + *p++ = 'C'; + *p++ = 'O'; + *p++ = 'M'; + *p++ = 'M'; + + word32 = 0x12; // size of this chunk + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + word32 = aufile->channels; // channels + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + word32 = aufile->samples / aufile->channels; // no. of sample frames + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + word32 = aufile->bits_per_sample; // bits + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + p = Convert_to_80bit_BE_IEEE854_Float (p, (long double)aufile->samplerate); // sample frequency as big endian 80 bit IEEE854 float + // End of COMM chunk + + // SSND chunk + *p++ = 'S'; + *p++ = 'S'; + *p++ = 'N'; + *p++ = 'D'; + + word32 = data_size + 0x08; // chunk length + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + *p++ = 0; // offset + *p++ = 0; + *p++ = 0; + *p++ = 0; + + *p++ = 0; // block size + *p++ = 0; + *p++ = 0; + *p++ = 0; + + return fwrite(header, sizeof(header), 1, aufile->sndfile); +} + +int write_audio_8bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + unsigned char *sample_buffer8 = (unsigned char*)sample_buffer; + unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) + data[i] = (sample_buffer8[i]+128) & 0xFF; + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +int write_audio_16bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + short *sample_buffer16 = (short*)sample_buffer; + char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + +#ifdef __APPLE__ + if (aufile->endianness != machine_endianness) { +#else + if (aufile->endianness == machine_endianness) { +#endif + for (i = 0; i < samples; i++) { + data[i*2] = (char)(sample_buffer16[i] & 0xFF); + data[i*2+1] = (char)((sample_buffer16[i] >> 8) & 0xFF); + } + } + else { + for (i = 0; i < samples; i++) { + data[i*2+1] = (char)(sample_buffer16[i] & 0xFF); + data[i*2] = (char)((sample_buffer16[i] >> 8) & 0xFF); + } + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +int write_audio_24bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + long *sample_buffer24 = (long*)sample_buffer; + char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) { + data[i*3] = (char)(sample_buffer24[i] & 0xFF); + data[i*3+1] = (char)((sample_buffer24[i] >> 8) & 0xFF); + data[i*3+2] = (char)((sample_buffer24[i] >> 16) & 0xFF); + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +int write_audio_32bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + long *sample_buffer32 = (long*)sample_buffer; + char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) { + data[i*4] = (char)(sample_buffer32[i] & 0xFF); + data[i*4+1] = (char)((sample_buffer32[i] >> 8) & 0xFF); + data[i*4+2] = (char)((sample_buffer32[i] >> 16) & 0xFF); + data[i*4+3] = (char)((sample_buffer32[i] >> 24) & 0xFF); + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +int write_audio_float(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + float *sample_buffer_f = (float*)sample_buffer; + unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) { + int exponent, + mantissa, + negative = 0; + float in = sample_buffer_f[i]; + + data[i*4] = 0; + data[i*4+1] = 0; + data[i*4+2] = 0; + data[i*4+3] = 0; + if (in == 0.f) + continue; + + if (in < 0.0) { + in *= -1.0; + negative = 1; + } + in = (float)frexp(in, &exponent); + exponent += 126; + in *= (float)0x1000000; + mantissa = (((int)in) & 0x7FFFFF); + + if (negative) + data[i*4+3] |= 0x80; + + if (exponent & 0x01) + data[i*4+2] |= 0x80; + + data[i*4] = mantissa & 0xFF; + data[i*4+1] = (mantissa >> 8) & 0xFF; + data[i*4+2] |= (mantissa >> 16) & 0x7F; + data[i*4+3] |= (exponent >> 1) & 0x7F; + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +void* output_to_PCM(double **input, void *sample_buffer, int channels, + int samples, int format) +{ + unsigned char ch; + int i; + + char *char_sample_buffer = (char*)sample_buffer; + short *short_sample_buffer = (short*)sample_buffer; + int *int_sample_buffer = (int*)sample_buffer; + float *float_sample_buffer = (float*)sample_buffer; + + /* + * Copy output to a standard PCM buffer + */ + switch (format) { + case WAV_FMT_8BIT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + char_sample_buffer[(i*channels)+ch] = (char)input[ch][i]; + } + } + break; + case WAV_FMT_AIFF: + case WAV_FMT_16BIT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + short_sample_buffer[(i*channels)+ch] = (short)input[ch][i]; + } + } + break; + case WAV_FMT_24BIT: + case WAV_FMT_32BIT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + int_sample_buffer[(i*channels)+ch] = (int)input[ch][i]; + } + } + break; + case WAV_FMT_FLOAT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + float_sample_buffer[(i*channels)+ch] = (float)input[ch][i]; + } + } + break; + } + + return sample_buffer; +} + +/* + * end of audio.c + */ diff --git a/audio.h b/audio.h index 49feacd..93007d7 100644 --- a/audio.h +++ b/audio.h @@ -1,178 +1,178 @@ -/* WaveGain - Filename: AUDIO.H - * - * Copyright (c) 2002 - 2005 John Edwards - * - * 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 - * - * Portions Copyright 2000-2002, Michael Smith - * - * AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty - */ - - -#ifndef AUDIO_H_INCLUDED -#define AUDIO_H_INCLUDED - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include "misc.h" - -/* In WIN32, the following bitmap can be found in sdk\inc\ksmedia.h and sdk\inc\mmreg.h: */ - -#ifndef SPEAKER_FRONT_LEFT -# define SPEAKER_FRONT_LEFT 0x1 -# define SPEAKER_FRONT_RIGHT 0x2 -#endif - -#ifndef WAVE_FORMAT_PCM -# define WAVE_FORMAT_PCM 0x0001 -#endif -#ifndef WAVE_FORMAT_IEEE_FLOAT -# define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif -#ifndef WAVE_FORMAT_EXTENSIBLE -# define WAVE_FORMAT_EXTENSIBLE 0xfffe -#endif - -typedef long (*audio_read_func)(void *src, - double **buffer, - int samples, - int fast, - int chunk); - -typedef struct -{ - audio_read_func read_samples; - - void *readdata; - - unsigned long total_samples_per_channel; - int channels; - long rate; - int samplesize; - int endianness; - int format; - int gain_chunk; - double gain_scale; - int std_in; - int std_out; - int apply_gain; - int write_chunk; - int force; - int undo; - int header_size; - unsigned char *header; - - FILE *out; - char *filename; -} wavegain_opt; - -typedef struct -{ - int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */ - int id_data_len; /* Amount of data needed to id whether this can load the file */ - int (*open_func)(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); - void (*close_func)(void *); - char *format; - char *description; -} input_format; - - -typedef struct { - unsigned short format; - unsigned short channels; - unsigned int channel_mask; - unsigned int samplerate; - unsigned int bytespersec; - unsigned short align; - unsigned short samplesize; -} wav_fmt; - -typedef struct { - short channels; - short samplesize; - unsigned long totalsamples; - unsigned long samplesread; - FILE *f; - short bigendian; -} wavfile; - -typedef struct { - short channels; - unsigned long totalframes; - short samplesize; - int rate; - int offset; - int blocksize; -} aiff_fmt; - -typedef wavfile aifffile; /* They're the same */ - -input_format *open_audio_file(FILE *in, wavegain_opt *opt); - -int raw_open(FILE *in, wavegain_opt *opt); -int wav_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); -int aiff_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); -int wav_id(unsigned char *buf, int len); -int aiff_id(unsigned char *buf, int len); -void wav_close(void *); -void raw_close(void *); - -long wav_read(void *, double **buffer, int samples, int fast, int chunk); -long wav_ieee_read(void *, double **buffer, int samples, int fast, int chunk); - -enum file_formats { - WAV_NO_FMT = 0, - WAV_FMT_8BIT, - WAV_FMT_16BIT, - WAV_FMT_24BIT, - WAV_FMT_32BIT, - WAV_FMT_FLOAT, - WAV_FMT_AIFF, - WAV_FMT_AIFC8, - WAV_FMT_AIFC16 -}; - -typedef struct -{ - int outputFormat; - FILE *sndfile; - unsigned long samplerate; - unsigned long bits_per_sample; - unsigned long channels; - unsigned long samples; - int endianness; - int format; -} audio_file; - -audio_file *open_output_audio_file(char *infile, wavegain_opt *opt); -int write_audio_file(audio_file *aufile, void *sample_buffer, int samples); -void close_audio_file(FILE *in, audio_file *aufile, wavegain_opt *opt); -int write_wav_header(audio_file *aufile, wavegain_opt *opt, Int64_t file_size); -int write_aiff_header(audio_file *aufile); -int write_audio_8bit(audio_file *aufile, void *sample_buffer, unsigned int samples); -int write_audio_16bit(audio_file *aufile, void *sample_buffer, unsigned int samples); -int write_audio_24bit(audio_file *aufile, void *sample_buffer, unsigned int samples); -int write_audio_32bit(audio_file *aufile, void *sample_buffer, unsigned int samples); -int write_audio_float(audio_file *aufile, void *sample_buffer, unsigned int samples); -void* output_to_PCM(double **input, void *samplebuffer, int channels, int samples, int format); - -#ifdef __cplusplus -} -#endif -#endif /* AUDIO_H_INCLUDED */ +/* WaveGain - Filename: AUDIO.H + * + * Copyright (c) 2002 - 2005 John Edwards + * + * 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 + * + * Portions Copyright 2000-2002, Michael Smith + * + * AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty + */ + + +#ifndef AUDIO_H_INCLUDED +#define AUDIO_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "misc.h" + +/* In WIN32, the following bitmap can be found in sdk\inc\ksmedia.h and sdk\inc\mmreg.h: */ + +#ifndef SPEAKER_FRONT_LEFT +# define SPEAKER_FRONT_LEFT 0x1 +# define SPEAKER_FRONT_RIGHT 0x2 +#endif + +#ifndef WAVE_FORMAT_PCM +# define WAVE_FORMAT_PCM 0x0001 +#endif +#ifndef WAVE_FORMAT_IEEE_FLOAT +# define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif +#ifndef WAVE_FORMAT_EXTENSIBLE +# define WAVE_FORMAT_EXTENSIBLE 0xfffe +#endif + +typedef long (*audio_read_func)(void *src, + double **buffer, + int samples, + int fast, + int chunk); + +typedef struct +{ + audio_read_func read_samples; + + void *readdata; + + unsigned long total_samples_per_channel; + int channels; + long rate; + int samplesize; + int endianness; + int format; + int gain_chunk; + double gain_scale; + int std_in; + int std_out; + int apply_gain; + int write_chunk; + int force; + int undo; + int header_size; + unsigned char *header; + + FILE *out; + char *filename; +} wavegain_opt; + +typedef struct +{ + int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */ + int id_data_len; /* Amount of data needed to id whether this can load the file */ + int (*open_func)(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); + void (*close_func)(void *); + char *format; + char *description; +} input_format; + + +typedef struct { + unsigned short format; + unsigned short channels; + unsigned int channel_mask; + unsigned int samplerate; + unsigned int bytespersec; + unsigned short align; + unsigned short samplesize; +} wav_fmt; + +typedef struct { + short channels; + short samplesize; + unsigned long totalsamples; + unsigned long samplesread; + FILE *f; + short bigendian; +} wavfile; + +typedef struct { + short channels; + unsigned long totalframes; + short samplesize; + int rate; + int offset; + int blocksize; +} aiff_fmt; + +typedef wavfile aifffile; /* They're the same */ + +input_format *open_audio_file(FILE *in, wavegain_opt *opt); + +int raw_open(FILE *in, wavegain_opt *opt); +int wav_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); +int aiff_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); +int wav_id(unsigned char *buf, int len); +int aiff_id(unsigned char *buf, int len); +void wav_close(void *); +void raw_close(void *); + +long wav_read(void *, double **buffer, int samples, int fast, int chunk); +long wav_ieee_read(void *, double **buffer, int samples, int fast, int chunk); + +enum file_formats { + WAV_NO_FMT = 0, + WAV_FMT_8BIT, + WAV_FMT_16BIT, + WAV_FMT_24BIT, + WAV_FMT_32BIT, + WAV_FMT_FLOAT, + WAV_FMT_AIFF, + WAV_FMT_AIFC8, + WAV_FMT_AIFC16 +}; + +typedef struct +{ + int outputFormat; + FILE *sndfile; + unsigned long samplerate; + unsigned long bits_per_sample; + unsigned long channels; + unsigned long samples; + int endianness; + int format; +} audio_file; + +audio_file *open_output_audio_file(char *infile, wavegain_opt *opt); +int write_audio_file(audio_file *aufile, void *sample_buffer, int samples); +void close_audio_file(FILE *in, audio_file *aufile, wavegain_opt *opt); +int write_wav_header(audio_file *aufile, wavegain_opt *opt, Int64_t file_size); +int write_aiff_header(audio_file *aufile); +int write_audio_8bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +int write_audio_16bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +int write_audio_24bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +int write_audio_32bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +int write_audio_float(audio_file *aufile, void *sample_buffer, unsigned int samples); +void* output_to_PCM(double **input, void *samplebuffer, int channels, int samples, int format); + +#ifdef __cplusplus +} +#endif +#endif /* AUDIO_H_INCLUDED */ diff --git a/dither.c b/dither.c index 230cd22..729e2c2 100644 --- a/dither.c +++ b/dither.c @@ -1,184 +1,184 @@ -/* This program is licensed under the GNU Library General Public License, version 2, - * a copy of which is included with this program (with filename LICENSE.LGPL). - * - * (c) 2002 John Edwards - * mostly lifted from work by Frank Klemm - * random functions for dithering. - * - * last modified: $ID:$ - */ -#include "dither.h" -#include - -static const unsigned char Parity [256] = { // parity - 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, - 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, - 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, - 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, - 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, - 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, - 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, - 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 -}; - -static unsigned int __r1 = 1; -static unsigned int __r2 = 1; - - -/* - * This is a simple random number generator with good quality for audio purposes. - * It consists of two polycounters with opposite rotation direction and different - * periods. The periods are coprime, so the total period is the product of both. - * - * ------------------------------------------------------------------------------------------------- - * +-> |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| - * | ------------------------------------------------------------------------------------------------- - * | | | | | | | - * | +--+--+--+-XOR-+--------+ - * | | - * +--------------------------------------------------------------------------------------+ - * - * ------------------------------------------------------------------------------------------------- - * |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| <-+ - * ------------------------------------------------------------------------------------------------- | - * | | | | | - * +--+----XOR----+--+ | - * | | - * +----------------------------------------------------------------------------------------+ - * - * - * The first has an period of 3*5*17*257*65537, the second of 7*47*73*178481, - * which gives a period of 18.410.713.077.675.721.215. The result is the - * XORed values of both generators. - */ - - -unsigned int -random_int ( void ) -{ - unsigned int t1, t2, t3, t4; - - t3 = t1 = __r1; t4 = t2 = __r2; // Parity calculation is done via table lookup, this is also available - t1 &= 0xF5; t2 >>= 25; // on CPUs without parity, can be implemented in C and avoid unpredictable - t1 = Parity [t1]; t2 &= 0x63; // jumps and slow rotate through the carry flag operations. - t1 <<= 31; t2 = Parity [t2]; - - return (__r1 = (t3 >> 1) | t1 ) ^ (__r2 = (t4 + t4) | t2 ); -} - - - -double -Random_Equi ( double mult ) // gives a equal distributed random number -{ // between -2^31*mult and +2^31*mult - return mult * (int) random_int (); -} - -double -Random_Triangular ( double mult ) // gives a triangular distributed random number -{ // between -2^32*mult and +2^32*mult - return mult * ( (double) (int) random_int () + (double) (int) random_int () ); -} - -/*********************************************************************************************************************/ - -static const float F44_0 [16 + 32] = { - (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, - (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, - - (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, - (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, - - (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, - (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0 -}; - - -static const float F44_1 [16 + 32] = { /* SNR(w) = 4.843163 dB, SNR = -3.192134 dB */ - (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, - (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, - (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, - (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, - - (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, - (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, - (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, - (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, - - (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, - (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, - (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, - (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, -}; - - -static const float F44_2 [16 + 32] = { /* SNR(w) = 10.060213 dB, SNR = -12.766730 dB */ - (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, - (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, - (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, - (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, - - (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, - (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, - (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, - (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, - - (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, - (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, - (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, - (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, -}; - - -static const float F44_3 [16 + 32] = { /* SNR(w) = 15.382598 dB, SNR = -29.402334 dB */ - (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, - (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, - (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, - (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, - - (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, - (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, - (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, - (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, - - (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, - (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, - (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, - (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099 -}; - - -double -scalar16 ( const float* x, const float* y ) -{ - return x[ 0]*y[ 0] + x[ 1]*y[ 1] + x[ 2]*y[ 2] + x[ 3]*y[ 3] - + x[ 4]*y[ 4] + x[ 5]*y[ 5] + x[ 6]*y[ 6] + x[ 7]*y[ 7] - + x[ 8]*y[ 8] + x[ 9]*y[ 9] + x[10]*y[10] + x[11]*y[11] - + x[12]*y[12] + x[13]*y[13] + x[14]*y[14] + x[15]*y[15]; -} - - -void -Init_Dither ( int bits, int shapingtype ) -{ - static unsigned char default_dither [] = { 92, 92, 88, 84, 81, 78, 74, 67, 0, 0 }; - static const float* F [] = { F44_0, F44_1, F44_2, F44_3 }; - int index; - - if (shapingtype < 0) shapingtype = 0; - if (shapingtype > 3) shapingtype = 3; - index = bits - 11 - shapingtype; - if (index < 0) index = 0; - if (index > 9) index = 9; - - memset ( Dither.ErrorHistory , 0, sizeof (Dither.ErrorHistory ) ); - memset ( Dither.DitherHistory, 0, sizeof (Dither.DitherHistory) ); - - Dither.FilterCoeff = F [shapingtype]; - Dither.Mask = ((Uint64_t)-1) << (32 - bits); - Dither.Add = 0.5 * ((1L << (32 - bits)) - 1); - Dither.Dither = 0.01*default_dither[index] / (((Int64_t)1) << bits); -} - - - +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (with filename LICENSE.LGPL). + * + * (c) 2002 John Edwards + * mostly lifted from work by Frank Klemm + * random functions for dithering. + * + * last modified: $ID:$ + */ +#include "dither.h" +#include + +static const unsigned char Parity [256] = { // parity + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 +}; + +static unsigned int __r1 = 1; +static unsigned int __r2 = 1; + + +/* + * This is a simple random number generator with good quality for audio purposes. + * It consists of two polycounters with opposite rotation direction and different + * periods. The periods are coprime, so the total period is the product of both. + * + * ------------------------------------------------------------------------------------------------- + * +-> |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| + * | ------------------------------------------------------------------------------------------------- + * | | | | | | | + * | +--+--+--+-XOR-+--------+ + * | | + * +--------------------------------------------------------------------------------------+ + * + * ------------------------------------------------------------------------------------------------- + * |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| <-+ + * ------------------------------------------------------------------------------------------------- | + * | | | | | + * +--+----XOR----+--+ | + * | | + * +----------------------------------------------------------------------------------------+ + * + * + * The first has an period of 3*5*17*257*65537, the second of 7*47*73*178481, + * which gives a period of 18.410.713.077.675.721.215. The result is the + * XORed values of both generators. + */ + + +unsigned int +random_int ( void ) +{ + unsigned int t1, t2, t3, t4; + + t3 = t1 = __r1; t4 = t2 = __r2; // Parity calculation is done via table lookup, this is also available + t1 &= 0xF5; t2 >>= 25; // on CPUs without parity, can be implemented in C and avoid unpredictable + t1 = Parity [t1]; t2 &= 0x63; // jumps and slow rotate through the carry flag operations. + t1 <<= 31; t2 = Parity [t2]; + + return (__r1 = (t3 >> 1) | t1 ) ^ (__r2 = (t4 + t4) | t2 ); +} + + + +double +Random_Equi ( double mult ) // gives a equal distributed random number +{ // between -2^31*mult and +2^31*mult + return mult * (int) random_int (); +} + +double +Random_Triangular ( double mult ) // gives a triangular distributed random number +{ // between -2^32*mult and +2^32*mult + return mult * ( (double) (int) random_int () + (double) (int) random_int () ); +} + +/*********************************************************************************************************************/ + +static const float F44_0 [16 + 32] = { + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0 +}; + + +static const float F44_1 [16 + 32] = { /* SNR(w) = 4.843163 dB, SNR = -3.192134 dB */ + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, + + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, + + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, +}; + + +static const float F44_2 [16 + 32] = { /* SNR(w) = 10.060213 dB, SNR = -12.766730 dB */ + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, + + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, + + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, +}; + + +static const float F44_3 [16 + 32] = { /* SNR(w) = 15.382598 dB, SNR = -29.402334 dB */ + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, + + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, + + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099 +}; + + +double +scalar16 ( const float* x, const float* y ) +{ + return x[ 0]*y[ 0] + x[ 1]*y[ 1] + x[ 2]*y[ 2] + x[ 3]*y[ 3] + + x[ 4]*y[ 4] + x[ 5]*y[ 5] + x[ 6]*y[ 6] + x[ 7]*y[ 7] + + x[ 8]*y[ 8] + x[ 9]*y[ 9] + x[10]*y[10] + x[11]*y[11] + + x[12]*y[12] + x[13]*y[13] + x[14]*y[14] + x[15]*y[15]; +} + + +void +Init_Dither ( int bits, int shapingtype ) +{ + static unsigned char default_dither [] = { 92, 92, 88, 84, 81, 78, 74, 67, 0, 0 }; + static const float* F [] = { F44_0, F44_1, F44_2, F44_3 }; + int index; + + if (shapingtype < 0) shapingtype = 0; + if (shapingtype > 3) shapingtype = 3; + index = bits - 11 - shapingtype; + if (index < 0) index = 0; + if (index > 9) index = 9; + + memset ( Dither.ErrorHistory , 0, sizeof (Dither.ErrorHistory ) ); + memset ( Dither.DitherHistory, 0, sizeof (Dither.DitherHistory) ); + + Dither.FilterCoeff = F [shapingtype]; + Dither.Mask = ((Uint64_t)-1) << (32 - bits); + Dither.Add = 0.5 * ((1L << (32 - bits)) - 1); + Dither.Dither = 0.01*default_dither[index] / (((Int64_t)1) << bits); +} + + + diff --git a/dither.h b/dither.h index dd2784b..3359e47 100644 --- a/dither.h +++ b/dither.h @@ -1,44 +1,44 @@ -/* This program is licensed under the GNU Library General Public License, version 2, - * a copy of which is included with this program (with filename LICENSE.LGPL). - * - * (c) 2002 John Edwards - * - * rand_t header. - * - * last modified: $ID:$ - */ - -#ifndef __RAND_T_H -#define __RAND_T_H - -#include "misc.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - const float* FilterCoeff; - Uint64_t Mask; - double Add; - float Dither; - float ErrorHistory [2] [16]; // max. 2 channels, 16th order Noise shaping - float DitherHistory [2] [16]; - int LastRandomNumber [2]; -} dither_t; - -extern dither_t Dither; -extern double doubletmp; -static const unsigned char Parity [256]; -unsigned int random_int ( void ); -extern double scalar16 ( const float* x, const float* y ); -extern double Random_Equi ( double mult ); -extern double Random_Triangular ( double mult ); -void Init_Dither ( int bits, int shapingtype ); - -#ifdef __cplusplus -} -#endif - -#endif /* __RAND_T_H */ - +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (with filename LICENSE.LGPL). + * + * (c) 2002 John Edwards + * + * rand_t header. + * + * last modified: $ID:$ + */ + +#ifndef __RAND_T_H +#define __RAND_T_H + +#include "misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const float* FilterCoeff; + Uint64_t Mask; + double Add; + float Dither; + float ErrorHistory [2] [16]; // max. 2 channels, 16th order Noise shaping + float DitherHistory [2] [16]; + int LastRandomNumber [2]; +} dither_t; + +extern dither_t Dither; +extern double doubletmp; +static const unsigned char Parity [256]; +unsigned int random_int ( void ); +extern double scalar16 ( const float* x, const float* y ); +extern double Random_Equi ( double mult ); +extern double Random_Triangular ( double mult ); +void Init_Dither ( int bits, int shapingtype ); + +#ifdef __cplusplus +} +#endif + +#endif /* __RAND_T_H */ + diff --git a/getopt.c b/getopt.c index bb4e893..6ab474b 100644 --- a/getopt.c +++ b/getopt.c @@ -1,1047 +1,1047 @@ -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to drepper@gnu.org - before changing it! - - Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 - Free Software Foundation, Inc. - - The GNU C 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. - - The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* This tells Alpha OSF/1 not to define a getopt prototype in . - Ditto for AIX 3.2 and . */ -#ifndef _NO_PROTO -# define _NO_PROTO -#endif - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if !defined __STDC__ || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -# ifndef const -# define const -# endif -#endif - -#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -# include -# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -# define ELIDE_CODE -# endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -# include -# include -#endif /* GNU C library. */ - -#ifdef VMS -# include -# if HAVE_STRING_H - 0 -# include -# endif -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. - When compiling libc, the _ macro is predefined. */ -# ifdef HAVE_LIBINTL_H -# include -# define _(msgid) gettext (msgid) -# else -# define _(msgid) (msgid) -# endif -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -# include -# define my_index strchr -#else - -#include - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -#ifndef getenv -extern char *getenv (); -#endif - -static char * -my_index (str, chr) - const char *str; - int chr; -{ - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -# if (!defined __STDC__ || !__STDC__) && !defined strlen -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ -extern int strlen (const char *); -# endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -#ifdef _LIBC -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; - -static int nonoption_flags_max_len; -static int nonoption_flags_len; - -static int original_argc; -static char *const *original_argv; - -/* Make sure the environment variable bash 2.0 puts in the environment - is valid for the getopt call we must make sure that the ARGV passed - to getopt is that one passed to the process. */ -static void -__attribute__ ((unused)) -store_args_and_env (int argc, char *const *argv) -{ - /* XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ - original_argc = argc; - original_argv = argv; -} -# ifdef text_set_element -text_set_element (__libc_subinit, store_args_and_env); -# endif /* text_set_element */ - -# define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -#if defined __STDC__ && __STDC__ -static void exchange (char **); -#endif - -static void -exchange (argv) - char **argv; -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - -#ifdef _LIBC - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else - { - memset (__mempcpy (new_str, __getopt_nonoption_flags, - nonoption_flags_max_len), - '\0', top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -#if defined __STDC__ && __STDC__ -static const char *_getopt_initialize (int, char *const *, const char *); -#endif -static const char * -_getopt_initialize (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - posixly_correct = getenv ("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - -#ifdef _LIBC - if (posixly_correct == NULL - && argc == original_argc && argv == original_argv) - { - if (nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = strlen (orig_str); - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), - '\0', nonoption_flags_max_len - len); - } - } - nonoption_flags_len = nonoption_flags_max_len; - } - else - nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; -{ - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) - { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring); - __getopt_initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#ifdef _LIBC -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) - { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (opterr) - { - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf (stderr, - _("%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); - else - /* +option or -option */ - fprintf (stderr, - _("%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], pfound->name); - } - - nextchar += strlen (nextchar); - - optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - if (opterr) - { - if (argv[optind][1] == '-') - /* --option */ - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); - else - /* +option or -option */ - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); - } - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') - { - if (opterr) - { - if (posixly_correct) - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, _("%s: illegal option -- %c\n"), - argv[0], c); - else - fprintf (stderr, _("%s: invalid option -- %c\n"), - argv[0], c); - } - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (opterr) - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); - - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -int -getopt (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - -#endif /* Not ELIDE_CODE. */ - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == -1) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 + Free Software Foundation, Inc. + + The GNU C 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. + + The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +#include + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/getopt.h b/getopt.h index 91eb54e..b0147e9 100644 --- a/getopt.h +++ b/getopt.h @@ -1,169 +1,169 @@ -/* Declarations for getopt. - Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C 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. - - The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GETOPT_H - -#ifndef __need_getopt -# define _GETOPT_H 1 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -#ifndef __need_getopt -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ -# if defined __STDC__ && __STDC__ - const char *name; -# else - char *name; -# endif - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 -#endif /* need getopt */ - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `--', then non-option arguments are treated as - arguments to the option '\0'. This behavior is specific to the GNU - `getopt'. */ - -#if defined __STDC__ && __STDC__ -# ifdef __GNU_LIBRARY__ -/* Many other libraries have conflicting prototypes for getopt, with - differences in the consts, in stdlib.h. To avoid compilation - errors, only prototype getopt for the GNU C library. */ -extern int getopt (int __argc, char *const *__argv, const char *__shortopts); -# else /* not __GNU_LIBRARY__ */ -extern int getopt (); -# endif /* __GNU_LIBRARY__ */ - -# ifndef __need_getopt -extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, - const struct option *__longopts, int *__longind); -extern int getopt_long_only (int __argc, char *const *__argv, - const char *__shortopts, - const struct option *__longopts, int *__longind); - -/* Internal only. Users should not call this directly. */ -extern int _getopt_internal (int __argc, char *const *__argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only); -# endif -#else /* not __STDC__ */ -extern int getopt (); -# ifndef __need_getopt -extern int getopt_long (); -extern int getopt_long_only (); - -extern int _getopt_internal (); -# endif -#endif /* __STDC__ */ - -#ifdef __cplusplus -} -#endif - -/* Make sure we later can get all the definitions and declarations. */ -#undef __need_getopt - -#endif /* getopt.h */ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C 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. + + The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +# if defined __STDC__ && __STDC__ + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if defined __STDC__ && __STDC__ +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/getopt1.c b/getopt1.c index 9cd0644..8c1a78f 100644 --- a/getopt1.c +++ b/getopt1.c @@ -1,188 +1,188 @@ -/* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C 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. - - The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "getopt.h" - -#if !defined __STDC__ || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -#ifndef const -#define const -#endif -#endif - -#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -#include -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include -#endif - -#ifndef NULL -#define NULL 0 -#endif - -int -getopt_long (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 0); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 1); -} - - -#endif /* Not ELIDE_CODE. */ - -#ifdef TEST - -#include - -int -main (argc, argv) - int argc; - char **argv; -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - int option_index = 0; - static struct option long_options[] = - { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "abc:d:0123456789", - long_options, &option_index); - if (c == -1) - break; - - switch (c) - { - case 0: - printf ("option %s", long_options[option_index].name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case 'd': - printf ("option d with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C 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. + + The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/i18n.h b/i18n.h index 535101d..36178d0 100644 --- a/i18n.h +++ b/i18n.h @@ -1,18 +1,18 @@ -#ifndef I18N_H -#define I18N_H - -#ifdef ENABLE_NLS -#include -#define _(X) gettext(X) -#else -#define _(X) (X) -#define textdomain(X) -#define bindtextdomain(X, Y) -#endif -#ifdef gettext_noop -#define N_(X) gettext_noop(X) -#else -#define N_(X) (X) -#endif - -#endif +#ifndef I18N_H +#define I18N_H + +#ifdef ENABLE_NLS +#include +#define _(X) gettext(X) +#else +#define _(X) (X) +#define textdomain(X) +#define bindtextdomain(X, Y) +#endif +#ifdef gettext_noop +#define N_(X) gettext_noop(X) +#else +#define N_(X) (X) +#endif + +#endif diff --git a/misc.c b/misc.c index 278d8b2..65c9def 100644 --- a/misc.c +++ b/misc.c @@ -1,100 +1,100 @@ -/* - * Misc utility functions - * - * This program is distributed under the GNU General Public License, version - * 2.1. A copy of this license is included with this source. - * - * Copyright (C) 2002 Gian-Carlo Pascutto and Magnus Holmgren - */ -#include -#include -#include -#include -#include "config.h" -#include "i18n.h" -#include "misc.h" - -#ifndef _WIN32 -#include -#include -#endif - -extern char log_file_name[]; - -/** - * \brief Display a file (or other I/O) error message. - * - * Display a file (or other I/O) error message. First a message is formatted - * and printed, followed by the error text, terminated by a line feed. - * - * \param message message format to display. - * \param ... printf-arguments used to format the message. - */ -void file_error(const char* message, ...) -{ - int err_num = errno; - va_list args; - - va_start(args, message); - vfprintf(stderr, message, args); - va_end(args); - - fprintf(stderr, "%s", strerror(err_num)); - fprintf(stderr, "\n"); -} - - -/** - * \brief Get the last component in a path. - * - * Get the last component in a path. If no directory separator is found, - * return the path as is. - * - * \param path path to get last component of. - * \return the last path component, or path. - */ -char* last_path(const char* path) -{ - int i; - - for (i = strlen(path) - 1; i >= 0; i--) { -#ifdef _WIN32 - if ((path[i] == '\\') || (path[i] == ':')) { -#else - if (path[i] == '/') { -#endif - return (char*) &path[i + 1]; - } - } - - return (char*) path; -} - -void write_log(const char *fmt, ...) -{ - va_list ap; - FILE *fp; - char msgbuf[1024]; - char *bufp = msgbuf; - - /* - * A really rough sanity check to protect against blatant buffer overrun - */ - if (strlen(fmt) > 750) - sprintf(msgbuf, "%s %s", " ", fmt); - else { - va_start(ap, fmt); - vsprintf(bufp, fmt, ap); - va_end(ap); - } - if ((fp = fopen(log_file_name, "a")) == (FILE *)NULL) - return; - - fprintf(fp, "%s", msgbuf); - fflush(fp); - fclose(fp); - - va_end(ap); -} - - +/* + * Misc utility functions + * + * This program is distributed under the GNU General Public License, version + * 2.1. A copy of this license is included with this source. + * + * Copyright (C) 2002 Gian-Carlo Pascutto and Magnus Holmgren + */ +#include +#include +#include +#include +#include "config.h" +#include "i18n.h" +#include "misc.h" + +#ifndef _WIN32 +#include +#include +#endif + +extern char log_file_name[]; + +/** + * \brief Display a file (or other I/O) error message. + * + * Display a file (or other I/O) error message. First a message is formatted + * and printed, followed by the error text, terminated by a line feed. + * + * \param message message format to display. + * \param ... printf-arguments used to format the message. + */ +void file_error(const char* message, ...) +{ + int err_num = errno; + va_list args; + + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + + fprintf(stderr, "%s", strerror(err_num)); + fprintf(stderr, "\n"); +} + + +/** + * \brief Get the last component in a path. + * + * Get the last component in a path. If no directory separator is found, + * return the path as is. + * + * \param path path to get last component of. + * \return the last path component, or path. + */ +char* last_path(const char* path) +{ + int i; + + for (i = strlen(path) - 1; i >= 0; i--) { +#ifdef _WIN32 + if ((path[i] == '\\') || (path[i] == ':')) { +#else + if (path[i] == '/') { +#endif + return (char*) &path[i + 1]; + } + } + + return (char*) path; +} + +void write_log(const char *fmt, ...) +{ + va_list ap; + FILE *fp; + char msgbuf[1024]; + char *bufp = msgbuf; + + /* + * A really rough sanity check to protect against blatant buffer overrun + */ + if (strlen(fmt) > 750) + sprintf(msgbuf, "%s %s", " ", fmt); + else { + va_start(ap, fmt); + vsprintf(bufp, fmt, ap); + va_end(ap); + } + if ((fp = fopen(log_file_name, "a")) == (FILE *)NULL) + return; + + fprintf(fp, "%s", msgbuf); + fflush(fp); + fclose(fp); + + va_end(ap); +} + + diff --git a/misc.h b/misc.h index 8b6b74f..532605e 100644 --- a/misc.h +++ b/misc.h @@ -1,20 +1,20 @@ -#ifndef MISC_H -#define MISC_H - -#ifdef _WIN32 -typedef signed __int64 Int64_t; -typedef unsigned __int64 Uint64_t; -#define chdir _chdir -#define strdup _strdup -#define getcwd _getcwd -#define getpid _getpid -#else -typedef signed long long Int64_t; -typedef unsigned long long Uint64_t; -#endif - -void file_error(const char* message, ...); -char* last_path(const char* path); -extern void write_log(const char *fmt, ...); - -#endif /* MISC_H */ +#ifndef MISC_H +#define MISC_H + +#ifdef _WIN32 +typedef signed __int64 Int64_t; +typedef unsigned __int64 Uint64_t; +#define chdir _chdir +#define strdup _strdup +#define getcwd _getcwd +#define getpid _getpid +#else +typedef signed long long Int64_t; +typedef unsigned long long Uint64_t; +#endif + +void file_error(const char* message, ...); +char* last_path(const char* path); +extern void write_log(const char *fmt, ...); + +#endif /* MISC_H */ diff --git a/wavegain.c b/wavegain.c index c483f2c..8014b79 100644 --- a/wavegain.c +++ b/wavegain.c @@ -1,694 +1,694 @@ -/* - * function: ReplayGain support for Wave files (http://www.replaygain.org) - * - * Reads in a Vorbis file, figures out the peak and ReplayGain levels and - * applies the Gain tags to the Wave file - * - * This program is distributed under the GNU General Public License, version - * 2.1. A copy of this license is included with this source. - * - * Copyright (C) 2002-2004 John Edwards - * Additional code by Magnus Holmgren and Gian-Carlo Pascutto - * Linux patch by Marc Brooker - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -/* For handling file attributes (owner, group, permissions) */ -#include -#include -#include -#endif - -#ifdef _WIN32 -#include -#include -#else -# ifndef __APPLE__ -# include -# endif -#endif - -#include - -#ifndef __APPLE__ -#include -#endif - -#include "gain_analysis.h" -#include "i18n.h" -#include "getopt.h" -#include "misc.h" -#include "audio.h" -#include "dither.h" -#include "main.h" -#include "wavegain.h" - -#ifdef _WIN32 -#include -#endif - -#ifdef ENABLE_RECURSIVE -#include "recurse.h" -#endif - -/*Gcc uses LL as a suffix for long long int (64 bit) types - Marc Brooker 8/4/2004*/ -#ifdef __GNUC__ -#define ROUND64(x) ( doubletmp = (x) + Dither.Add + (Int64_t)0x001FFFFD80000000LL, *(Int64_t*)(&doubletmp) - (Int64_t)0x433FFFFD80000000LL ) -#else -#define ROUND64(x) ( doubletmp = (x) + Dither.Add + (Int64_t)0x001FFFFD80000000L, *(Int64_t*)(&doubletmp) - (Int64_t)0x433FFFFD80000000L ) -#endif - -#ifndef _WIN32 -#define _snprintf snprintf -#endif - -extern int write_to_log; -dither_t Dither; -double doubletmp; -double total_samples; -double total_files; - -/* Replaced with a double based function for consistency 2005-11-17 -static float FABS(float x) -{ - unsigned int *ix=(unsigned int *)&x; - *ix&=0x7fffffffUL; - return(x); -} -*/ -static double DABS(double x) -{ - Uint64_t *ix=(Uint64_t *)&x; -#ifdef __GNUC__ - *ix&=0x7fffffffffffffffULL; -#else - *ix&=0x7fffffffffffffff; -#endif - return(x); -} - -/* Dither output */ -Int64_t dither_output(int dithering, int shapingtype, int i, double Sum, int k, int format) -{ - double Sum2; - Int64_t val; - if(dithering) { - if(!shapingtype) { - double tmp = Random_Equi ( Dither.Dither ); - Sum2 = tmp - Dither.LastRandomNumber [k]; - Dither.LastRandomNumber [k] = (int)tmp; - Sum2 = Sum += Sum2; - val = ROUND64 (Sum2) & Dither.Mask; - } - else { - Sum2 = Random_Triangular ( Dither.Dither ) - scalar16 ( Dither.DitherHistory[k], Dither.FilterCoeff + i ); - Sum += Dither.DitherHistory [k] [(-1-i)&15] = (float)Sum2; - Sum2 = Sum + scalar16 ( Dither.ErrorHistory [k], Dither.FilterCoeff + i ); - val = ROUND64 (Sum2) & Dither.Mask; - Dither.ErrorHistory [k] [(-1-i)&15] = (float)(Sum - val); - } - } - else - val = (Int64_t)(ROUND64 (Sum)); - - if (format == WAV_FMT_8BIT) - val = val >> 24; - else if (format == WAV_FMT_16BIT || format == WAV_FMT_AIFF) - val = val >> 16; - else if (format == WAV_FMT_24BIT) - val = val >> 8; - - return (val); -} - -/* Get the gain and peak value for a file. Runs in audiophile mode if - * audiophile is true. - * - * If an error occured, 0 is returned (a message has been printed). - */ - -int get_gain(const char *filename, double *track_peak, double *track_gain, - double *dc_offset, double *offset, SETTINGS *settings) -{ - wavegain_opt *wg_opts = malloc(sizeof(wavegain_opt)); - FILE *infile; - int result = 0; - double new_peak, - factor_clip, - scale, - peak = 0., - dB; - int k, i; - long chunk; - input_format *format; - - memset(wg_opts, 0, sizeof(wavegain_opt)); - - wg_opts->force = settings->force; - - if(!strcmp(filename, "-")) { - infile = stdin; - settings->apply_gain = 0; - wg_opts->std_in = 1; -#ifdef _WIN32 - _setmode( _fileno(stdin), _O_BINARY ); -#endif - } - else - infile = fopen(filename, "rb"); - - if (infile == NULL) { - fprintf (stderr, " Not able to open input file %s.\n", filename) ; - goto exit; - } - wg_opts->apply_gain = 0; - - /* - * Now, we need to select an input audio format - */ - - format = open_audio_file(infile, wg_opts); - if (!format) { - /* error reported by reader */ - fprintf (stderr, " Unrecognized file format for %s.\n", filename); - goto exit; - } - - if (wg_opts->gain_chunk == 1 && !wg_opts->force) { - fprintf (stderr, " Skipping File %s, it has already been processed.\n", filename); -// result = 1; - goto exit; - } - - if ((wg_opts->channels != 1) && (wg_opts->channels != 2)) { - fprintf(stderr, " Unsupported number of channels.\n"); - goto exit; - } - - /* Only initialize gain analysis once in audiophile mode */ - if (settings->first_file || !settings->audiophile) { - if (InitGainAnalysis(wg_opts->rate) != INIT_GAIN_ANALYSIS_OK) { - fprintf(stderr, " Error Initializing Gain Analysis (non-standard samplerate?)\n"); - goto exit; - } - } - - if (settings->first_file) { - total_samples = (double)wg_opts->total_samples_per_channel; - fprintf(stderr, "\n Analyzing...\n\n"); - fprintf(stderr, " Gain | Peak | Scale | New Peak |Left DC|Right DC| Track\n"); - fprintf(stderr, " | | | |Offset | Offset |\n"); - fprintf(stderr, " --------------------------------------------------------------\n"); - if(write_to_log) { - write_log("\n Analyzing...\n\n"); - write_log(" Gain | Peak | Scale | New Peak |Left DC|Right DC| Track\n"); - write_log(" | | | |Offset | Offset |\n"); - write_log(" --------------------------------------------------------------\n"); - } - settings->first_file = 0; - } - else - total_samples += (double)wg_opts->total_samples_per_channel; - - if (settings->fast && (wg_opts->total_samples_per_channel * (wg_opts->samplesize / 8) - * wg_opts->channels > 8192000)) { - - long samples_read; - double **buffer = malloc(sizeof(double *) * wg_opts->channels); - - for (i = 0; i < wg_opts->channels; i++) - buffer[i] = malloc(BUFFER_LEN * sizeof(double)); - - chunk = ((wg_opts->total_samples_per_channel * (wg_opts->samplesize / 8) * wg_opts->channels) + 44) / 1200; - - for(k = 100; k < 1100; k+=5) { - - samples_read = wg_opts->read_samples(wg_opts->readdata, buffer, BUFFER_LEN, - settings->fast, chunk * k); - if (samples_read == 0) { - break; - } - else { - if (samples_read < 0) { - /* Error in the stream. Not a problem, just reporting it in case - * we (the app) cares. In this case, we don't - */ - } - else { - int i; - int j; - - for (i = 0; i < wg_opts->channels; i++) { - for (j = 0; j < samples_read; j++) { - buffer[i][j] *= 0x7fff; - if (DABS(buffer[i][j]) > peak) - peak = DABS(buffer[i][j]); - } - } - - if (AnalyzeSamples(buffer[0], buffer[1], samples_read, - wg_opts->channels) != GAIN_ANALYSIS_OK) { - fprintf(stderr, " Error processing samples.\n"); - for (i = 0; i < wg_opts->channels; i++) - if (buffer[i]) free(buffer[i]); - if (buffer) free(buffer); - goto exit; - } - } - } - } - for (i = 0; i < wg_opts->channels; i++) - if (buffer[i]) free(buffer[i]); - if (buffer) free(buffer); - } - else - { - long samples_read; - double **buffer = malloc(sizeof(double *) * wg_opts->channels); - - for (i = 0; i < wg_opts->channels; i++) - buffer[i] = malloc(BUFFER_LEN * sizeof(double)); - - while (1) { - - samples_read = wg_opts->read_samples(wg_opts->readdata, buffer, BUFFER_LEN, 0, 0); - - if (samples_read == 0) { - break; - } - else { - if (samples_read < 0) { - /* Error in the stream. Not a problem, just reporting it in case - * we (the app) cares. In this case, we don't - */ - } - else { - int i; - int j; - - for (i = 0; i < wg_opts->channels; i++) { - for (j = 0; j < samples_read; j++) { - offset[i] += buffer[i][j]; - buffer[i][j] *= 0x7fff; - if (DABS(buffer[i][j]) > peak) - peak = DABS(buffer[i][j]); - } - } - - if (AnalyzeSamples(buffer[0], buffer[1], samples_read, - wg_opts->channels) != GAIN_ANALYSIS_OK) { - fprintf(stderr, " Error processing samples.\n"); - for (i = 0; i < wg_opts->channels; i++) - if (buffer[i]) free(buffer[i]); - if (buffer) free(buffer); - goto exit; - } - } - } - } - - for (i = 0; i < wg_opts->channels; i++) { - if (buffer[i]) free(buffer[i]); - dc_offset[i] = (double)(offset[i] / wg_opts->total_samples_per_channel); - } - if (buffer) free(buffer); - } - /* - * calculate factors for ReplayGain and ClippingPrevention - */ - *track_gain = (GetTitleGain() + settings->man_gain); - scale = (pow(10., *track_gain * 0.05)); - if(settings->clip_prev) { - factor_clip = (32767./( peak + 1)); - if(scale < factor_clip) - factor_clip = 1.0; - else - factor_clip /= scale; - scale *= factor_clip; - } - new_peak = (peak * scale); - - dB = 20. * log10(scale); - *track_gain = dB; - { - int dc_l; - int dc_r; - if (settings->no_offset) { - dc_l = 0; - dc_r = 0; - } - else { - dc_l = (int)(dc_offset[0] * 32768 * -1); - dc_r = (int)(dc_offset[1] * 32768 * -1); - } - fprintf(stderr, " %+6.2lf dB | %6.0lf | %5.2lf | %8.0lf | %4d | %4d | %s\n", - *track_gain, peak, scale, new_peak, dc_l, dc_r, filename); - if(write_to_log) { - write_log(" %+6.2lf dB | %6.0lf | %5.2lf | %8.0lf | %4d | %4d | %s\n", - *track_gain, peak, scale, new_peak, dc_l, dc_r, filename); - } - } - if (settings->scale && !settings->audiophile) - fprintf(stdout, "%8.6lf", scale); - - settings->album_peak = settings->album_peak < peak ? peak : settings->album_peak; - *track_peak = new_peak; - result = 1; - -exit: - if (result) - format->close_func(wg_opts->readdata); - if (wg_opts) - free(wg_opts); - if (infile) - fclose(infile); - return result; -} - - -/* Use the ReplayGain calculations to adjust the gain on the wave file. - * If audiophile_gain is selected, that value is used, otherwise the - * radio_gain value is used. - */ -int write_gains(const char *filename, double radio_gain, double audiophile_gain, double TitlePeak, - double *dc_offset, double *album_dc_offset, SETTINGS *settings) -{ - wavegain_opt *wg_opts = malloc(sizeof(wavegain_opt)); - FILE *infile; - audio_file *aufile; - int readcount, - result = 0, - i; - double Gain; - double scale; - double total_read = 0.; - double wrap_prev_pos; - double wrap_prev_neg; - void *sample_buffer; - input_format *format; - - char template[] = ".tmp_XXXXXX"; - int tempSize = strlen(filename) + strlen(template) + 1; - char* tempName = NULL; - struct stat fst; - - memset(wg_opts, 0, sizeof(wavegain_opt)); - - wg_opts->force = settings->force; - wg_opts->undo = settings->undo; - - infile = fopen(filename, "rb"); - - if (infile == NULL) { - fprintf (stderr, " Not able to open input file %s.\n", filename) ; - goto exit; - } - wg_opts->apply_gain = 1; - wg_opts->write_chunk = settings->write_chunk; - - /* - * Now, we need to select an input audio format - */ - - format = open_audio_file(infile, wg_opts); - if (!format) { - format->close_func(wg_opts->readdata); - if (wg_opts) - free(wg_opts); - fclose(infile); - /* error reported by reader */ - fprintf (stderr, " Unrecognized file format for %s.\n", filename) ; - } - else if (wg_opts->undo && !wg_opts->gain_chunk) { - format->close_func(wg_opts->readdata); - if (wg_opts) - free(wg_opts); - fclose(infile); - fprintf(stderr, " Skipping file: %s - 'gain' chunk not found.\n", filename); - if (write_to_log) { - write_log(" Skipping file: %s - 'gain' chunk not found.\n", filename); - } - result = 1; - } - else if (wg_opts->gain_scale == 1.0 && !wg_opts->force) { - format->close_func(wg_opts->readdata); - if (wg_opts) - free(wg_opts); - fclose(infile); - fprintf(stderr, " Skipping file: %s - Gain already undone.\n", filename); - if (write_to_log) { - write_log(" Skipping file: %s - Gain already undone.\n", filename); - } - result = 1; - } - else { - double **pcm = malloc(sizeof(double *) * wg_opts->channels); - - for (i = 0; i < wg_opts->channels; i++) - pcm[i] = malloc(BUFFER_LEN * sizeof(double)); - - switch(settings->format) { - case WAV_NO_FMT: - if (wg_opts->format == WAV_FMT_AIFF || wg_opts->format == WAV_FMT_AIFC8 - || wg_opts->format == WAV_FMT_AIFC16) { - wg_opts->format = WAV_FMT_AIFF; - wrap_prev_pos = 0x7fff; - wrap_prev_neg = -0x8000; - } - else if (wg_opts->format == WAV_FMT_8BIT) { - wrap_prev_pos = 0x7f; - wrap_prev_neg = -0x80; - } - else if (wg_opts->format == WAV_FMT_16BIT) { - wrap_prev_pos = 0x7fff; - wrap_prev_neg = -0x8000; - } - else if (wg_opts->format == WAV_FMT_24BIT) { - wrap_prev_pos = 0x7fffff; - wrap_prev_neg = -0x800000; - } - else if (wg_opts->format == WAV_FMT_32BIT) { - wrap_prev_pos = 0x7fffffff; - wrap_prev_neg = -0x7fffffff; - } - else if (wg_opts->format == WAV_FMT_FLOAT) { - wrap_prev_pos = 1 - (1 / 0x80000000); - wrap_prev_neg = -1.; - } - break; - case WAV_FMT_8BIT: - wg_opts->format = WAV_FMT_8BIT; - wg_opts->samplesize = 8; - wrap_prev_pos = 0x7f; - wrap_prev_neg = -0x80; - break; - case WAV_FMT_AIFF: - wg_opts->format = WAV_FMT_AIFF; - wg_opts->samplesize = 16; - wg_opts->endianness = BIG; - wrap_prev_pos = 0x7fff; - wrap_prev_neg = -0x8000; - break; - case WAV_FMT_16BIT: - wg_opts->format = WAV_FMT_16BIT; - wg_opts->samplesize = 16; - wg_opts->endianness = LITTLE; - wrap_prev_pos = 0x7fff; - wrap_prev_neg = -0x8000; - break; - case WAV_FMT_24BIT: - wg_opts->format = WAV_FMT_24BIT; - wg_opts->samplesize = 24; - wg_opts->endianness = LITTLE; - wrap_prev_pos = 0x7fffff; - wrap_prev_neg = -0x800000; - break; - case WAV_FMT_32BIT: - wg_opts->format = WAV_FMT_32BIT; - wg_opts->samplesize = 32; - wg_opts->endianness = LITTLE; - wrap_prev_pos = 0x7fffffff; - wrap_prev_neg = -0x7fffffff; - break; - case WAV_FMT_FLOAT: - wg_opts->format = WAV_FMT_FLOAT; - wg_opts->samplesize = 32; - wg_opts->endianness = LITTLE; - wrap_prev_pos = 1 - (1 / 0x80000000); - wrap_prev_neg = -1.; - break; - } - - wg_opts->std_out = settings->std_out; - - /* Create temp file name */ - if ((tempName = malloc(tempSize * sizeof(*tempName))) == NULL) { - fprintf(stderr, " Error allocating memory for output file name\n"); - goto exit; - } - _snprintf(tempName, tempSize, "%s%s", filename, template); - - aufile = open_output_audio_file(tempName, wg_opts); - - if (aufile == NULL) { - fprintf (stderr, " Not able to open output file %s.\n", tempName); - fclose(infile); - goto exit; - } - - Init_Dither (wg_opts->samplesize, settings->shapingtype); - - if (wg_opts->undo) { - scale = 1.0 / wg_opts->gain_scale; - Gain = 20. * log10(scale); - wg_opts->gain_scale = 1.0; - } - else { - if (settings->audiophile) - Gain = audiophile_gain; - else - Gain = radio_gain; - - scale = pow(10., Gain * 0.05); - wg_opts->gain_scale = scale; - } - - fprintf(stderr, " \r"); - fprintf(stderr, " Applying Gain of %+5.2lf dB to file: %s\n", Gain, filename); - if (write_to_log) { - write_log(" Applying Gain of %+5.2lf dB to file: %s\n", Gain, filename); - } - - while (1) { - - readcount = wg_opts->read_samples(wg_opts->readdata, pcm, BUFFER_LEN, 0, 0); - - total_read += ((double)readcount / wg_opts->rate); - total_files += ((double)readcount / wg_opts->rate); - if( (long)total_files % 4 == 0) { - if (wg_opts->undo) - fprintf(stderr, "This file %3.0lf%% done\r", - total_read / (wg_opts->total_samples_per_channel / wg_opts->rate) * 100); - else - fprintf(stderr, "This file %3.0lf%% done\tAll files %3.0lf%% done\r", - total_read / (wg_opts->total_samples_per_channel / wg_opts->rate) * 100, - total_files / (total_samples / wg_opts->rate) * 100); - } - - if (readcount == 0) { - break; - } - else if (readcount < 0) { - /* Error in the stream. Not a problem, just reporting it in case - * we (the app) cares. In this case, we don't - */ - } - else { - int convsize = BUFFER_LEN; - int j, - i = 0, - k; - int bout = (readcount < convsize ? readcount : convsize); - - /* scale doubles to 8, 16, 24 or 32 bit signed ints - * (host order) (unless float output) - * and apply ReplayGain scaling, etc. - */ - sample_buffer = malloc(sizeof(double) * wg_opts->channels * bout); - for(k = 0; k < wg_opts->channels; k++) { - for(j = 0; j < bout; j++, i++) { - Int64_t val; - double Sum; - - if (!settings->no_offset) { - if (settings->adc) - pcm[k][j] -= album_dc_offset[k]; - else - pcm[k][j] -= dc_offset[k]; - } - - pcm[k][j] *= scale; - if (settings->limiter) { /* hard 6dB limiting */ - if (pcm[k][j] < -0.5) - pcm[k][j] = tanh((pcm[k][j] + 0.5) / (1-0.5)) * (1-0.5) - 0.5; - else if (pcm[k][j] > 0.5) - pcm[k][j] = tanh((pcm[k][j] - 0.5) / (1-0.5)) * (1-0.5) + 0.5; - } - if (wg_opts->format != WAV_FMT_FLOAT) { - Sum = pcm[k][j]*2147483647.f; - if (i > 31) - i = 0; - val = dither_output(settings->dithering, settings->shapingtype, i, - Sum, k, wg_opts->format); - if (val > (Int64_t)wrap_prev_pos) - val = (Int64_t)wrap_prev_pos; - else if (val < (Int64_t)wrap_prev_neg) - val = (Int64_t)wrap_prev_neg; - pcm[k][j] = (double)val; - } - else { - if (pcm[k][j] > wrap_prev_pos) - pcm[k][j] = wrap_prev_pos; - else if (pcm[k][j] < wrap_prev_neg) - pcm[k][j] = wrap_prev_neg; - } - } - } - sample_buffer = output_to_PCM(pcm, sample_buffer, wg_opts->channels, - bout, wg_opts->format); - /* write to file */ - write_audio_file(aufile, sample_buffer, bout * wg_opts->channels); - - free(sample_buffer); - } - } - for (i = 0; i < wg_opts->channels; i++) - if (pcm[i]) free(pcm[i]); - if (pcm) free(pcm); - format->close_func(wg_opts->readdata); - close_audio_file(infile, aufile, wg_opts); - fclose(infile); - - if (!settings->std_out) { -#ifdef _WIN32 - /* WIN32's rename(temp, original) does not allow original to exist, - * so we must remove() it first. Ideally, there should be a way - * to remove+rename in a single, atomic function, so if either one - * fails, the original file is preserved. - */ - if (remove(filename) != 0) { - fprintf(stderr, " Error deleting old file '%s'\n", filename); - goto exit; - } -#endif -#ifndef _WIN32 - /* copy owner, group, permissions from original to output */ - stat(filename, &fst); - chown(tempName, fst.st_uid, -1); - chown(tempName, -1 ,fst.st_gid); - chmod(tempName, fst.st_mode); -#endif - if (rename(tempName, filename) != 0) { - fprintf(stderr, " Error renaming '%s' to '%s' (uh-oh)\n", tempName, filename); - goto exit; - } - } - result = 1; - } -exit: - if(tempName) free(tempName); - return result; -} - - +/* + * function: ReplayGain support for Wave files (http://www.replaygain.org) + * + * Reads in a Vorbis file, figures out the peak and ReplayGain levels and + * applies the Gain tags to the Wave file + * + * This program is distributed under the GNU General Public License, version + * 2.1. A copy of this license is included with this source. + * + * Copyright (C) 2002-2004 John Edwards + * Additional code by Magnus Holmgren and Gian-Carlo Pascutto + * Linux patch by Marc Brooker + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +/* For handling file attributes (owner, group, permissions) */ +#include +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#else +# ifndef __APPLE__ +# include +# endif +#endif + +#include + +#ifndef __APPLE__ +#include +#endif + +#include "gain_analysis.h" +#include "i18n.h" +#include "getopt.h" +#include "misc.h" +#include "audio.h" +#include "dither.h" +#include "main.h" +#include "wavegain.h" + +#ifdef _WIN32 +#include +#endif + +#ifdef ENABLE_RECURSIVE +#include "recurse.h" +#endif + +/*Gcc uses LL as a suffix for long long int (64 bit) types - Marc Brooker 8/4/2004*/ +#ifdef __GNUC__ +#define ROUND64(x) ( doubletmp = (x) + Dither.Add + (Int64_t)0x001FFFFD80000000LL, *(Int64_t*)(&doubletmp) - (Int64_t)0x433FFFFD80000000LL ) +#else +#define ROUND64(x) ( doubletmp = (x) + Dither.Add + (Int64_t)0x001FFFFD80000000L, *(Int64_t*)(&doubletmp) - (Int64_t)0x433FFFFD80000000L ) +#endif + +#ifndef _WIN32 +#define _snprintf snprintf +#endif + +extern int write_to_log; +dither_t Dither; +double doubletmp; +double total_samples; +double total_files; + +/* Replaced with a double based function for consistency 2005-11-17 +static float FABS(float x) +{ + unsigned int *ix=(unsigned int *)&x; + *ix&=0x7fffffffUL; + return(x); +} +*/ +static double DABS(double x) +{ + Uint64_t *ix=(Uint64_t *)&x; +#ifdef __GNUC__ + *ix&=0x7fffffffffffffffULL; +#else + *ix&=0x7fffffffffffffff; +#endif + return(x); +} + +/* Dither output */ +Int64_t dither_output(int dithering, int shapingtype, int i, double Sum, int k, int format) +{ + double Sum2; + Int64_t val; + if(dithering) { + if(!shapingtype) { + double tmp = Random_Equi ( Dither.Dither ); + Sum2 = tmp - Dither.LastRandomNumber [k]; + Dither.LastRandomNumber [k] = (int)tmp; + Sum2 = Sum += Sum2; + val = ROUND64 (Sum2) & Dither.Mask; + } + else { + Sum2 = Random_Triangular ( Dither.Dither ) - scalar16 ( Dither.DitherHistory[k], Dither.FilterCoeff + i ); + Sum += Dither.DitherHistory [k] [(-1-i)&15] = (float)Sum2; + Sum2 = Sum + scalar16 ( Dither.ErrorHistory [k], Dither.FilterCoeff + i ); + val = ROUND64 (Sum2) & Dither.Mask; + Dither.ErrorHistory [k] [(-1-i)&15] = (float)(Sum - val); + } + } + else + val = (Int64_t)(ROUND64 (Sum)); + + if (format == WAV_FMT_8BIT) + val = val >> 24; + else if (format == WAV_FMT_16BIT || format == WAV_FMT_AIFF) + val = val >> 16; + else if (format == WAV_FMT_24BIT) + val = val >> 8; + + return (val); +} + +/* Get the gain and peak value for a file. Runs in audiophile mode if + * audiophile is true. + * + * If an error occured, 0 is returned (a message has been printed). + */ + +int get_gain(const char *filename, double *track_peak, double *track_gain, + double *dc_offset, double *offset, SETTINGS *settings) +{ + wavegain_opt *wg_opts = malloc(sizeof(wavegain_opt)); + FILE *infile; + int result = 0; + double new_peak, + factor_clip, + scale, + peak = 0., + dB; + int k, i; + long chunk; + input_format *format; + + memset(wg_opts, 0, sizeof(wavegain_opt)); + + wg_opts->force = settings->force; + + if(!strcmp(filename, "-")) { + infile = stdin; + settings->apply_gain = 0; + wg_opts->std_in = 1; +#ifdef _WIN32 + _setmode( _fileno(stdin), _O_BINARY ); +#endif + } + else + infile = fopen(filename, "rb"); + + if (infile == NULL) { + fprintf (stderr, " Not able to open input file %s.\n", filename) ; + goto exit; + } + wg_opts->apply_gain = 0; + + /* + * Now, we need to select an input audio format + */ + + format = open_audio_file(infile, wg_opts); + if (!format) { + /* error reported by reader */ + fprintf (stderr, " Unrecognized file format for %s.\n", filename); + goto exit; + } + + if (wg_opts->gain_chunk == 1 && !wg_opts->force) { + fprintf (stderr, " Skipping File %s, it has already been processed.\n", filename); +// result = 1; + goto exit; + } + + if ((wg_opts->channels != 1) && (wg_opts->channels != 2)) { + fprintf(stderr, " Unsupported number of channels.\n"); + goto exit; + } + + /* Only initialize gain analysis once in audiophile mode */ + if (settings->first_file || !settings->audiophile) { + if (InitGainAnalysis(wg_opts->rate) != INIT_GAIN_ANALYSIS_OK) { + fprintf(stderr, " Error Initializing Gain Analysis (non-standard samplerate?)\n"); + goto exit; + } + } + + if (settings->first_file) { + total_samples = (double)wg_opts->total_samples_per_channel; + fprintf(stderr, "\n Analyzing...\n\n"); + fprintf(stderr, " Gain | Peak | Scale | New Peak |Left DC|Right DC| Track\n"); + fprintf(stderr, " | | | |Offset | Offset |\n"); + fprintf(stderr, " --------------------------------------------------------------\n"); + if(write_to_log) { + write_log("\n Analyzing...\n\n"); + write_log(" Gain | Peak | Scale | New Peak |Left DC|Right DC| Track\n"); + write_log(" | | | |Offset | Offset |\n"); + write_log(" --------------------------------------------------------------\n"); + } + settings->first_file = 0; + } + else + total_samples += (double)wg_opts->total_samples_per_channel; + + if (settings->fast && (wg_opts->total_samples_per_channel * (wg_opts->samplesize / 8) + * wg_opts->channels > 8192000)) { + + long samples_read; + double **buffer = malloc(sizeof(double *) * wg_opts->channels); + + for (i = 0; i < wg_opts->channels; i++) + buffer[i] = malloc(BUFFER_LEN * sizeof(double)); + + chunk = ((wg_opts->total_samples_per_channel * (wg_opts->samplesize / 8) * wg_opts->channels) + 44) / 1200; + + for(k = 100; k < 1100; k+=5) { + + samples_read = wg_opts->read_samples(wg_opts->readdata, buffer, BUFFER_LEN, + settings->fast, chunk * k); + if (samples_read == 0) { + break; + } + else { + if (samples_read < 0) { + /* Error in the stream. Not a problem, just reporting it in case + * we (the app) cares. In this case, we don't + */ + } + else { + int i; + int j; + + for (i = 0; i < wg_opts->channels; i++) { + for (j = 0; j < samples_read; j++) { + buffer[i][j] *= 0x7fff; + if (DABS(buffer[i][j]) > peak) + peak = DABS(buffer[i][j]); + } + } + + if (AnalyzeSamples(buffer[0], buffer[1], samples_read, + wg_opts->channels) != GAIN_ANALYSIS_OK) { + fprintf(stderr, " Error processing samples.\n"); + for (i = 0; i < wg_opts->channels; i++) + if (buffer[i]) free(buffer[i]); + if (buffer) free(buffer); + goto exit; + } + } + } + } + for (i = 0; i < wg_opts->channels; i++) + if (buffer[i]) free(buffer[i]); + if (buffer) free(buffer); + } + else + { + long samples_read; + double **buffer = malloc(sizeof(double *) * wg_opts->channels); + + for (i = 0; i < wg_opts->channels; i++) + buffer[i] = malloc(BUFFER_LEN * sizeof(double)); + + while (1) { + + samples_read = wg_opts->read_samples(wg_opts->readdata, buffer, BUFFER_LEN, 0, 0); + + if (samples_read == 0) { + break; + } + else { + if (samples_read < 0) { + /* Error in the stream. Not a problem, just reporting it in case + * we (the app) cares. In this case, we don't + */ + } + else { + int i; + int j; + + for (i = 0; i < wg_opts->channels; i++) { + for (j = 0; j < samples_read; j++) { + offset[i] += buffer[i][j]; + buffer[i][j] *= 0x7fff; + if (DABS(buffer[i][j]) > peak) + peak = DABS(buffer[i][j]); + } + } + + if (AnalyzeSamples(buffer[0], buffer[1], samples_read, + wg_opts->channels) != GAIN_ANALYSIS_OK) { + fprintf(stderr, " Error processing samples.\n"); + for (i = 0; i < wg_opts->channels; i++) + if (buffer[i]) free(buffer[i]); + if (buffer) free(buffer); + goto exit; + } + } + } + } + + for (i = 0; i < wg_opts->channels; i++) { + if (buffer[i]) free(buffer[i]); + dc_offset[i] = (double)(offset[i] / wg_opts->total_samples_per_channel); + } + if (buffer) free(buffer); + } + /* + * calculate factors for ReplayGain and ClippingPrevention + */ + *track_gain = (GetTitleGain() + settings->man_gain); + scale = (pow(10., *track_gain * 0.05)); + if(settings->clip_prev) { + factor_clip = (32767./( peak + 1)); + if(scale < factor_clip) + factor_clip = 1.0; + else + factor_clip /= scale; + scale *= factor_clip; + } + new_peak = (peak * scale); + + dB = 20. * log10(scale); + *track_gain = dB; + { + int dc_l; + int dc_r; + if (settings->no_offset) { + dc_l = 0; + dc_r = 0; + } + else { + dc_l = (int)(dc_offset[0] * 32768 * -1); + dc_r = (int)(dc_offset[1] * 32768 * -1); + } + fprintf(stderr, " %+6.2lf dB | %6.0lf | %5.2lf | %8.0lf | %4d | %4d | %s\n", + *track_gain, peak, scale, new_peak, dc_l, dc_r, filename); + if(write_to_log) { + write_log(" %+6.2lf dB | %6.0lf | %5.2lf | %8.0lf | %4d | %4d | %s\n", + *track_gain, peak, scale, new_peak, dc_l, dc_r, filename); + } + } + if (settings->scale && !settings->audiophile) + fprintf(stdout, "%8.6lf", scale); + + settings->album_peak = settings->album_peak < peak ? peak : settings->album_peak; + *track_peak = new_peak; + result = 1; + +exit: + if (result) + format->close_func(wg_opts->readdata); + if (wg_opts) + free(wg_opts); + if (infile) + fclose(infile); + return result; +} + + +/* Use the ReplayGain calculations to adjust the gain on the wave file. + * If audiophile_gain is selected, that value is used, otherwise the + * radio_gain value is used. + */ +int write_gains(const char *filename, double radio_gain, double audiophile_gain, double TitlePeak, + double *dc_offset, double *album_dc_offset, SETTINGS *settings) +{ + wavegain_opt *wg_opts = malloc(sizeof(wavegain_opt)); + FILE *infile; + audio_file *aufile; + int readcount, + result = 0, + i; + double Gain; + double scale; + double total_read = 0.; + double wrap_prev_pos; + double wrap_prev_neg; + void *sample_buffer; + input_format *format; + + char template[] = ".tmp_XXXXXX"; + int tempSize = strlen(filename) + strlen(template) + 1; + char* tempName = NULL; + struct stat fst; + + memset(wg_opts, 0, sizeof(wavegain_opt)); + + wg_opts->force = settings->force; + wg_opts->undo = settings->undo; + + infile = fopen(filename, "rb"); + + if (infile == NULL) { + fprintf (stderr, " Not able to open input file %s.\n", filename) ; + goto exit; + } + wg_opts->apply_gain = 1; + wg_opts->write_chunk = settings->write_chunk; + + /* + * Now, we need to select an input audio format + */ + + format = open_audio_file(infile, wg_opts); + if (!format) { + format->close_func(wg_opts->readdata); + if (wg_opts) + free(wg_opts); + fclose(infile); + /* error reported by reader */ + fprintf (stderr, " Unrecognized file format for %s.\n", filename) ; + } + else if (wg_opts->undo && !wg_opts->gain_chunk) { + format->close_func(wg_opts->readdata); + if (wg_opts) + free(wg_opts); + fclose(infile); + fprintf(stderr, " Skipping file: %s - 'gain' chunk not found.\n", filename); + if (write_to_log) { + write_log(" Skipping file: %s - 'gain' chunk not found.\n", filename); + } + result = 1; + } + else if (wg_opts->gain_scale == 1.0 && !wg_opts->force) { + format->close_func(wg_opts->readdata); + if (wg_opts) + free(wg_opts); + fclose(infile); + fprintf(stderr, " Skipping file: %s - Gain already undone.\n", filename); + if (write_to_log) { + write_log(" Skipping file: %s - Gain already undone.\n", filename); + } + result = 1; + } + else { + double **pcm = malloc(sizeof(double *) * wg_opts->channels); + + for (i = 0; i < wg_opts->channels; i++) + pcm[i] = malloc(BUFFER_LEN * sizeof(double)); + + switch(settings->format) { + case WAV_NO_FMT: + if (wg_opts->format == WAV_FMT_AIFF || wg_opts->format == WAV_FMT_AIFC8 + || wg_opts->format == WAV_FMT_AIFC16) { + wg_opts->format = WAV_FMT_AIFF; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + } + else if (wg_opts->format == WAV_FMT_8BIT) { + wrap_prev_pos = 0x7f; + wrap_prev_neg = -0x80; + } + else if (wg_opts->format == WAV_FMT_16BIT) { + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + } + else if (wg_opts->format == WAV_FMT_24BIT) { + wrap_prev_pos = 0x7fffff; + wrap_prev_neg = -0x800000; + } + else if (wg_opts->format == WAV_FMT_32BIT) { + wrap_prev_pos = 0x7fffffff; + wrap_prev_neg = -0x7fffffff; + } + else if (wg_opts->format == WAV_FMT_FLOAT) { + wrap_prev_pos = 1 - (1 / 0x80000000); + wrap_prev_neg = -1.; + } + break; + case WAV_FMT_8BIT: + wg_opts->format = WAV_FMT_8BIT; + wg_opts->samplesize = 8; + wrap_prev_pos = 0x7f; + wrap_prev_neg = -0x80; + break; + case WAV_FMT_AIFF: + wg_opts->format = WAV_FMT_AIFF; + wg_opts->samplesize = 16; + wg_opts->endianness = BIG; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + break; + case WAV_FMT_16BIT: + wg_opts->format = WAV_FMT_16BIT; + wg_opts->samplesize = 16; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + break; + case WAV_FMT_24BIT: + wg_opts->format = WAV_FMT_24BIT; + wg_opts->samplesize = 24; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fffff; + wrap_prev_neg = -0x800000; + break; + case WAV_FMT_32BIT: + wg_opts->format = WAV_FMT_32BIT; + wg_opts->samplesize = 32; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fffffff; + wrap_prev_neg = -0x7fffffff; + break; + case WAV_FMT_FLOAT: + wg_opts->format = WAV_FMT_FLOAT; + wg_opts->samplesize = 32; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 1 - (1 / 0x80000000); + wrap_prev_neg = -1.; + break; + } + + wg_opts->std_out = settings->std_out; + + /* Create temp file name */ + if ((tempName = malloc(tempSize * sizeof(*tempName))) == NULL) { + fprintf(stderr, " Error allocating memory for output file name\n"); + goto exit; + } + _snprintf(tempName, tempSize, "%s%s", filename, template); + + aufile = open_output_audio_file(tempName, wg_opts); + + if (aufile == NULL) { + fprintf (stderr, " Not able to open output file %s.\n", tempName); + fclose(infile); + goto exit; + } + + Init_Dither (wg_opts->samplesize, settings->shapingtype); + + if (wg_opts->undo) { + scale = 1.0 / wg_opts->gain_scale; + Gain = 20. * log10(scale); + wg_opts->gain_scale = 1.0; + } + else { + if (settings->audiophile) + Gain = audiophile_gain; + else + Gain = radio_gain; + + scale = pow(10., Gain * 0.05); + wg_opts->gain_scale = scale; + } + + fprintf(stderr, " \r"); + fprintf(stderr, " Applying Gain of %+5.2lf dB to file: %s\n", Gain, filename); + if (write_to_log) { + write_log(" Applying Gain of %+5.2lf dB to file: %s\n", Gain, filename); + } + + while (1) { + + readcount = wg_opts->read_samples(wg_opts->readdata, pcm, BUFFER_LEN, 0, 0); + + total_read += ((double)readcount / wg_opts->rate); + total_files += ((double)readcount / wg_opts->rate); + if( (long)total_files % 4 == 0) { + if (wg_opts->undo) + fprintf(stderr, "This file %3.0lf%% done\r", + total_read / (wg_opts->total_samples_per_channel / wg_opts->rate) * 100); + else + fprintf(stderr, "This file %3.0lf%% done\tAll files %3.0lf%% done\r", + total_read / (wg_opts->total_samples_per_channel / wg_opts->rate) * 100, + total_files / (total_samples / wg_opts->rate) * 100); + } + + if (readcount == 0) { + break; + } + else if (readcount < 0) { + /* Error in the stream. Not a problem, just reporting it in case + * we (the app) cares. In this case, we don't + */ + } + else { + int convsize = BUFFER_LEN; + int j, + i = 0, + k; + int bout = (readcount < convsize ? readcount : convsize); + + /* scale doubles to 8, 16, 24 or 32 bit signed ints + * (host order) (unless float output) + * and apply ReplayGain scaling, etc. + */ + sample_buffer = malloc(sizeof(double) * wg_opts->channels * bout); + for(k = 0; k < wg_opts->channels; k++) { + for(j = 0; j < bout; j++, i++) { + Int64_t val; + double Sum; + + if (!settings->no_offset) { + if (settings->adc) + pcm[k][j] -= album_dc_offset[k]; + else + pcm[k][j] -= dc_offset[k]; + } + + pcm[k][j] *= scale; + if (settings->limiter) { /* hard 6dB limiting */ + if (pcm[k][j] < -0.5) + pcm[k][j] = tanh((pcm[k][j] + 0.5) / (1-0.5)) * (1-0.5) - 0.5; + else if (pcm[k][j] > 0.5) + pcm[k][j] = tanh((pcm[k][j] - 0.5) / (1-0.5)) * (1-0.5) + 0.5; + } + if (wg_opts->format != WAV_FMT_FLOAT) { + Sum = pcm[k][j]*2147483647.f; + if (i > 31) + i = 0; + val = dither_output(settings->dithering, settings->shapingtype, i, + Sum, k, wg_opts->format); + if (val > (Int64_t)wrap_prev_pos) + val = (Int64_t)wrap_prev_pos; + else if (val < (Int64_t)wrap_prev_neg) + val = (Int64_t)wrap_prev_neg; + pcm[k][j] = (double)val; + } + else { + if (pcm[k][j] > wrap_prev_pos) + pcm[k][j] = wrap_prev_pos; + else if (pcm[k][j] < wrap_prev_neg) + pcm[k][j] = wrap_prev_neg; + } + } + } + sample_buffer = output_to_PCM(pcm, sample_buffer, wg_opts->channels, + bout, wg_opts->format); + /* write to file */ + write_audio_file(aufile, sample_buffer, bout * wg_opts->channels); + + free(sample_buffer); + } + } + for (i = 0; i < wg_opts->channels; i++) + if (pcm[i]) free(pcm[i]); + if (pcm) free(pcm); + format->close_func(wg_opts->readdata); + close_audio_file(infile, aufile, wg_opts); + fclose(infile); + + if (!settings->std_out) { +#ifdef _WIN32 + /* WIN32's rename(temp, original) does not allow original to exist, + * so we must remove() it first. Ideally, there should be a way + * to remove+rename in a single, atomic function, so if either one + * fails, the original file is preserved. + */ + if (remove(filename) != 0) { + fprintf(stderr, " Error deleting old file '%s'\n", filename); + goto exit; + } +#endif +#ifndef _WIN32 + /* copy owner, group, permissions from original to output */ + stat(filename, &fst); + chown(tempName, fst.st_uid, -1); + chown(tempName, -1 ,fst.st_gid); + chmod(tempName, fst.st_mode); +#endif + if (rename(tempName, filename) != 0) { + fprintf(stderr, " Error renaming '%s' to '%s' (uh-oh)\n", tempName, filename); + goto exit; + } + } + result = 1; + } +exit: + if(tempName) free(tempName); + return result; +} + + diff --git a/wavegain.h b/wavegain.h index 9e0ceee..113e70d 100644 --- a/wavegain.h +++ b/wavegain.h @@ -1,15 +1,15 @@ -#ifndef WAVEGAIN_H -#define WAVEGAIN_H - -#include "main.h" - -#define NO_PEAK -1.f -#define NO_GAIN -10000.f - -extern int get_gain(const char *filename, double *track_peak, double *track_gain, double *dc_offset, double *offset, - SETTINGS *settings); -extern int write_gains(const char *filename, double radio_gain, double audiophile_gain, double TitlePeak, - double *dc_offset, double *album_dc_offset, SETTINGS *settings); - -#endif /* WAVEGAIN_H */ - +#ifndef WAVEGAIN_H +#define WAVEGAIN_H + +#include "main.h" + +#define NO_PEAK -1.f +#define NO_GAIN -10000.f + +extern int get_gain(const char *filename, double *track_peak, double *track_gain, double *dc_offset, double *offset, + SETTINGS *settings); +extern int write_gains(const char *filename, double radio_gain, double audiophile_gain, double TitlePeak, + double *dc_offset, double *album_dc_offset, SETTINGS *settings); + +#endif /* WAVEGAIN_H */ +