X-Git-Url: http://4ch.mooo.com/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fdoslib%2Fext%2Fvorbtool%2Faudio.c;fp=src%2Flib%2Fdoslib%2Fext%2Fvorbtool%2Faudio.c;h=d447c06a23bee3db15d5b3fa6c80e87bf09176e2;hb=1ce4037e53ce951438ba7e35597b08248dd1257a;hp=0000000000000000000000000000000000000000;hpb=7e5115774a8978fd85ac90c2c3c072b2b6a31d0e;p=16.git diff --git a/src/lib/doslib/ext/vorbtool/audio.c b/src/lib/doslib/ext/vorbtool/audio.c new file mode 100644 index 00000000..d447c06a --- /dev/null +++ b/src/lib/doslib/ext/vorbtool/audio.c @@ -0,0 +1,917 @@ +/* OggEnc + ** + ** This program is distributed under the GNU General Public License, version 2. + ** A copy of this license is included with this source. + ** + ** Copyright 2000-2002, Michael Smith + ** 2010, Monty + ** + ** AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty + **/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "audio.h" +#include "platform.h" +#include "i18n.h" +#include "resample.h" + +#ifdef HAVE_LIBFLAC +#include "flac.h" +#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)) + +/* 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")}, +#ifdef HAVE_LIBFLAC + {flac_id, 4, flac_open, flac_close, "flac", N_("FLAC file reader")}, + {oggflac_id, 32, flac_open, flac_close, "ogg", N_("Ogg FLAC file reader")}, +#endif + {NULL, 0, NULL, NULL, NULL, NULL} +}; + +input_format *open_audio_file(FILE *in, oe_enc_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, unsigned int length) +{ + if(fseek(in, length, SEEK_CUR)) + { + /* Failed. Do it the hard way. */ + unsigned char buf[1024]; + unsigned int seek_needed = length; + int 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, unsigned int *len) +{ + unsigned char buf[8]; + + while(1) + { + if(fread(buf,1,8,in) < 8) /* Suck down a chunk specifier */ + { + fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\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; + fprintf(stderr, _("Skipping chunk of type \"%s\", length %d\n"), buf, *len); + } + else + { + *len = READ_U32_LE(buf+4); + return 1; + } + } +} + +static int find_aiff_chunk(FILE *in, char *type, unsigned int *len) +{ + unsigned char buf[8]; + int restarted = 0; + + while(1) + { + if(fread(buf,1,8,in) <8) + { + if(!restarted) { + /* Handle out of order chunks by seeking back to the start + * to retry */ + restarted = 1; + fseek(in, 12, SEEK_SET); + continue; + } + 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; + } +} + + + +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; +} + +static int aiff_permute_matrix[6][6] = +{ + {0}, /* 1.0 mono */ + {0,1}, /* 2.0 stereo */ + {0,2,1}, /* 3.0 channel ('wide') stereo */ + {0,1,2,3}, /* 4.0 discrete quadraphonic (WARN) */ + {0,2,1,3,4}, /* 5.0 surround (WARN) */ + {0,1,2,3,4,5}, /* 5.1 surround (WARN)*/ +}; + + +int aiff_open(FILE *in, oe_enc_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)); + int i; + + if(buf[11]=='C') + aifc=1; + else + aifc=0; + + 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 = 1; + + if(aifc) + { + if(len < 22) + { + fprintf(stderr, _("Warning: AIFF-C header truncated.\n")); + return 0; + } + + if(!memcmp(buffer+18, "NONE", 4)) + { + aiff->bigendian = 1; + } + else if(!memcmp(buffer+18, "sowt", 4)) + { + aiff->bigendian = 0; + } + 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; + + aiff->f = in; + aiff->samplesread = 0; + aiff->channels = format.channels; + aiff->samplesize = format.samplesize; + aiff->totalsamples = format.totalframes; + + if(aiff->channels>3) + fprintf(stderr,"WARNING: AIFF[-C] files with greater than three channels use\n" + "speaker locations incompatable with Vorbis suppound definitions.\n" + "Not performaing channel location mapping.\n"); + + opt->readdata = (void *)aiff; + + aiff->channel_permute = malloc(aiff->channels * sizeof(int)); + if (aiff->channels <= 6) + /* Where we know the mappings, use them. */ + memcpy(aiff->channel_permute, aiff_permute_matrix[aiff->channels-1], + sizeof(int) * aiff->channels); + else + /* Use a default 1-1 mapping */ + for (i=0; i < aiff->channels; i++) + aiff->channel_permute[i] = i; + + seek_forward(in, format.offset); /* Swallow some data */ + return 1; + } + else + { + fprintf(stderr, + _("Warning: OggEnc 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; + + if(len<12) return 0; /* Something screwed up */ + + if(memcmp(buf, "RIFF", 4)) + return 0; /* Not wave */ + + flen = READ_U32_LE(buf+4); /* We don't use this */ + + if(memcmp(buf+8, "WAVE",4)) + return 0; /* RIFF, but not wave */ + + return 1; +} + +static int wav_permute_matrix[8][8] = +{ + {0}, /* 1.0 mono */ + {0,1}, /* 2.0 stereo */ + {0,2,1}, /* 3.0 channel ('wide') stereo */ + {0,1,2,3}, /* 4.0 discrete quadraphonic */ + {0,2,1,3,4}, /* 5.0 surround */ + {0,2,1,4,5,3}, /* 5.1 surround */ + {0,2,1,4,5,6,3}, /* 6.1 surround */ + {0,2,1,6,7,4,5,3} /* 7.1 surround (classic theater 8-track) */ +}; + + +int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen) +{ + unsigned char buf[40]; + unsigned int len; + int samplesize; + wav_fmt format; + wavfile *wav = malloc(sizeof(wavfile)); + int i; + + /* 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)) + 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, 18 or + * 40 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 or 18 bytes in size, report a bug to the + * author. + */ + 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")); + + if(len>40)len=40; + + 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(format.format == -2) /* WAVE_FORMAT_EXTENSIBLE */ + { + if(len<40) + { + fprintf(stderr,"ERROR: Extended WAV format header invalid (too small)\n"); + return 0; + } + + format.mask = READ_U32_LE(buf+20); + /* warn the user if the format mask is not a supported/expected type */ + switch(format.mask){ + case 1539: /* 4.0 using side surround instead of back */ + fprintf(stderr,"WARNING: WAV file uses side surround instead of rear for quadraphonic;\n" + "remapping side speakers to rear in encoding.\n"); + break; + case 1551: /* 5.1 using side instead of rear */ + fprintf(stderr,"WARNING: WAV file uses side surround instead of rear for 5.1;\n" + "remapping side speakers to rear in encoding.\n"); + break; + case 319: /* 6.1 using rear instead of side */ + fprintf(stderr,"WARNING: WAV file uses rear surround instead of side for 6.1;\n" + "remapping rear speakers to side in encoding.\n"); + break; + case 255: /* 7.1 'Widescreen' */ + fprintf(stderr,"WARNING: WAV file is a 7.1 'Widescreen' channel mapping;\n" + "remapping speakers to Vorbis 7.1 format.\n"); + break; + case 0: /* default/undeclared */ + case 1: /* mono */ + case 3: /* stereo */ + case 51: /* quad */ + case 55: /* 5.0 */ + case 63: /* 5.1 */ + case 1807: /* 6.1 */ + case 1599: /* 7.1 */ + break; + default: + fprintf(stderr,"WARNING: Unknown WAV surround channel mask: %d\n" + "blindly mapping speakers using default SMPTE/ITU ordering.\n", + format.mask); + break; + } + format.format = READ_U16_LE(buf+24); + } + + if(!find_wav_chunk(in, "data", &len)) + return 0; /* EOF */ + + if(format.format == 1) + { + samplesize = format.samplesize/8; + opt->read_samples = wav_read; + } + else if(format.format == 3) + { + samplesize = 4; + opt->read_samples = wav_ieee_read; + } + else + { + fprintf(stderr, + _("ERROR: Wav file is unsupported type (must be standard PCM\n" + " or type 3 floating point PCM\n")); + return 0; + } + + 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.samplesize == samplesize*8 && + (format.samplesize == 24 || format.samplesize == 16 || + format.samplesize == 8 || + (format.samplesize == 32 && format.format == 3))) + { + /* 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; + 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 + { + long pos; + pos = ftell(in); + if(fseek(in, 0, SEEK_END) == -1) + { + opt->total_samples_per_channel = 0; /* Give up */ + } + else + { + opt->total_samples_per_channel = (ftell(in) - pos)/ + (format.channels*samplesize); + fseek(in,pos, SEEK_SET); + } + } + wav->totalsamples = opt->total_samples_per_channel; + + opt->readdata = (void *)wav; + + wav->channel_permute = malloc(wav->channels * sizeof(int)); + if (wav->channels <= 8) + /* Where we know the mappings, use them. */ + memcpy(wav->channel_permute, wav_permute_matrix[wav->channels-1], + sizeof(int) * wav->channels); + else + /* Use a default 1-1 mapping */ + for (i=0; i < wav->channels; i++) + wav->channel_permute[i] = i; + + return 1; + } + else + { + fprintf(stderr, + _("ERROR: Wav file is unsupported subformat (must be 8,16, or 24 bit PCM\n" + "or floating point PCM\n")); + return 0; + } +} + +long wav_read(void *in, float **buffer, int samples) +{ + wavfile *f = (wavfile *)in; + int sampbyte = f->samplesize / 8; + signed char *buf = alloca(samples*sampbyte*f->channels); + long bytes_read = fread(buf, 1, samples*sampbyte*f->channels, f->f); + int i,j; + long realsamples; + int *ch_permute = f->channel_permute; + + 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 + ch_permute[j]])-128)/128.0f; + } + } + } + else if(f->samplesize==16) + { + if(!f->bigendian) + { + for(i = 0; i < realsamples; i++) + { + for(j=0; j < f->channels; j++) + { + buffer[j][i] = ((buf[i*2*f->channels + 2*ch_permute[j] + 1]<<8) | + (buf[i*2*f->channels + 2*ch_permute[j]] & 0xff))/32768.0f; + } + } + } + else + { + for(i = 0; i < realsamples; i++) + { + for(j=0; j < f->channels; j++) + { + buffer[j][i]=((buf[i*2*f->channels + 2*ch_permute[j]]<<8) | + (buf[i*2*f->channels + 2*ch_permute[j] + 1] & 0xff))/32768.0f; + } + } + } + } + else if(f->samplesize==24) + { + if(!f->bigendian) { + for(i = 0; i < realsamples; i++) + { + for(j=0; j < f->channels; j++) + { + buffer[j][i] = ((buf[i*3*f->channels + 3*ch_permute[j] + 2] << 16) | + (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j] + 1] << 8) | + (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j]] & 0xff)) + / 8388608.0f; + + } + } + } + else { + fprintf(stderr, _("Big endian 24 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, float **buffer, int samples) +{ + wavfile *f = (wavfile *)in; + float *buf = alloca(samples*4*f->channels); /* de-interleave buffer */ + long bytes_read = fread(buf,1,samples*4*f->channels, f->f); + int i,j; + long realsamples; + + + 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 + f->channel_permute[j]]; + + return realsamples; +} + + +void wav_close(void *info) +{ + wavfile *f = (wavfile *)info; + free(f->channel_permute); + + free(f); +} + +int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen) +{ + wav_fmt format; /* fake wave header ;) */ + wavfile *wav = malloc(sizeof(wavfile)); + int i; + + /* 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; + wav->channel_permute = malloc(wav->channels * sizeof(int)); + for (i=0; i < wav->channels; i++) + wav->channel_permute[i] = i; + + opt->read_samples = wav_read; + opt->readdata = (void *)wav; + opt->total_samples_per_channel = 0; /* raw mode, don't bother */ + return 1; +} + +typedef struct { + res_state resampler; + audio_read_func real_reader; + void *real_readdata; + float **bufs; + int channels; + int bufsize; + int done; +} resampler; + +static long read_resampled(void *d, float **buffer, int samples) +{ + resampler *rs = d; + long in_samples; + int out_samples; + + in_samples = res_push_max_input(&rs->resampler, samples); + if(in_samples > rs->bufsize) + in_samples = rs->bufsize; + + in_samples = rs->real_reader(rs->real_readdata, rs->bufs, in_samples); + + if(in_samples <= 0) { + if(!rs->done) { + rs->done = 1; + out_samples = res_drain(&rs->resampler, buffer); + return out_samples; + } + return 0; + } + + out_samples = res_push(&rs->resampler, buffer, (float const **)rs->bufs, in_samples); + + if(out_samples <= 0) { + fprintf(stderr, _("BUG: Got zero samples from resampler: your file will be truncated. Please report this.\n")); + } + + return out_samples; +} + +int setup_resample(oe_enc_opt *opt) { + resampler *rs = calloc(1, sizeof(resampler)); + int c; + + rs->bufsize = 4096; /* Shrug */ + rs->real_reader = opt->read_samples; + rs->real_readdata = opt->readdata; + rs->bufs = malloc(sizeof(float *) * opt->channels); + rs->channels = opt->channels; + rs->done = 0; + if(res_init(&rs->resampler, rs->channels, opt->resamplefreq, opt->rate, RES_END)) + { + fprintf(stderr, _("Couldn't initialise resampler\n")); + return -1; + } + + for(c=0; c < opt->channels; c++) + rs->bufs[c] = malloc(sizeof(float) * rs->bufsize); + + opt->read_samples = read_resampled; + opt->readdata = rs; + if(opt->total_samples_per_channel) + opt->total_samples_per_channel = (int)((float)opt->total_samples_per_channel * + ((float)opt->resamplefreq/(float)opt->rate)); + opt->rate = opt->resamplefreq; + + return 0; +} + +void clear_resample(oe_enc_opt *opt) { + resampler *rs = opt->readdata; + int i; + + opt->read_samples = rs->real_reader; + opt->readdata = rs->real_readdata; + res_clear(&rs->resampler); + + for(i = 0; i < rs->channels; i++) + free(rs->bufs[i]); + + free(rs->bufs); + + free(rs); +} + +typedef struct { + audio_read_func real_reader; + void *real_readdata; + int channels; + float scale_factor; +} scaler; + +static long read_scaler(void *data, float **buffer, int samples) { + scaler *d = data; + long in_samples = d->real_reader(d->real_readdata, buffer, samples); + int i,j; + + for(i=0; i < d->channels; i++) { + for(j=0; j < in_samples; j++) { + buffer[i][j] *= d->scale_factor; + } + } + + return in_samples; +} + + +void setup_scaler(oe_enc_opt *opt, float scale) { + scaler *d = calloc(1, sizeof(scaler)); + + d->real_reader = opt->read_samples; + d->real_readdata = opt->readdata; + + opt->read_samples = read_scaler; + opt->readdata = d; + d->channels = opt->channels; + d->scale_factor = scale; +} + +void clear_scaler(oe_enc_opt *opt) { + scaler *d = opt->readdata; + + opt->read_samples = d->real_reader; + opt->readdata = d->real_readdata; + + free(d); +} + +typedef struct { + audio_read_func real_reader; + void *real_readdata; + float **bufs; +} downmix; + +static long read_downmix(void *data, float **buffer, int samples) +{ + downmix *d = data; + long in_samples = d->real_reader(d->real_readdata, d->bufs, samples); + int i; + + for(i=0; i < in_samples; i++) { + buffer[0][i] = (d->bufs[0][i] + d->bufs[1][i])*0.5f; + } + + return in_samples; +} + +void setup_downmix(oe_enc_opt *opt) { + downmix *d = calloc(1, sizeof(downmix)); + + if(opt->channels != 2) { + fprintf(stderr, "Internal error! Please report this bug.\n"); + return; + } + + d->bufs = malloc(2 * sizeof(float *)); + d->bufs[0] = malloc(4096 * sizeof(float)); + d->bufs[1] = malloc(4096 * sizeof(float)); + d->real_reader = opt->read_samples; + + d->real_readdata = opt->readdata; + + opt->read_samples = read_downmix; + opt->readdata = d; + + opt->channels = 1; +} +void clear_downmix(oe_enc_opt *opt) { + downmix *d = opt->readdata; + + opt->read_samples = d->real_reader; + opt->readdata = d->real_readdata; + opt->channels = 2; /* other things in cleanup rely on this */ + + free(d->bufs[0]); + free(d->bufs[1]); + free(d->bufs); + free(d); +} +