From add6ac02ad6b5da9cc50366733fd1bb2d4818f7f Mon Sep 17 00:00:00 2001 From: thor Date: Mon, 26 Feb 2018 00:27:43 +0000 Subject: [PATCH] out123+libsyn123: getting crazy with mixing and conversion git-svn-id: svn://scm.orgis.org/mpg123/trunk@4423 35dc7657-300d-0410-a2e5-dc2837fedb53 --- NEWS | 6 + configure.ac | 17 +++ src/libsyn123/libsyn123.c | 12 +- src/libsyn123/sampleconv.c | 259 +++++++++++++++++++++++++++++++++---- src/libsyn123/syn123.h.in | 64 ++++++++- src/libsyn123/syn123_int.h | 14 ++ src/out123.c | 255 ++++++++++++++++++++---------------- 7 files changed, 478 insertions(+), 149 deletions(-) diff --git a/NEWS b/NEWS index 8c8a7f24..d4505c0b 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ 1.26.0 ------ +TODO: dither flag in syn123_handle ... some place for the noise ... +TPDF is enough, isn't it? The highpass is specific to a sampling rate. +Not sure if buffering TPDF is worth it. It is cheap, very cheap. +TODO: drop special optimization flags, default to just -On ... but + consider defaulting to O3 for auto-vectorization TODO: avoid MPG123_NEED_MORE from non-feed reader reading frame bodies or see it as a feature - Starting to intentionaly use C99 in the codebase. API headers are still @@ -24,6 +29,7 @@ bodies or see it as a feature output. -- It also hosts sample format conversions as a necessity to be able to directly produce the format output devices need. +-- Well, also channel mixing while we're at it. TODO: Make libout123 and/or mpg123 use that to convert on the fly. Optionally? A new incompatible version of libmpg123 would drop duplicate code for conversions … diff --git a/configure.ac b/configure.ac index fc824169..4db1d4f3 100644 --- a/configure.ac +++ b/configure.ac @@ -571,6 +571,20 @@ if test "x$ieee" = xenabled; then AC_DEFINE(IEEE_FLOAT, 1, [ Define to indicate that float storage follows IEEE754. ]) fi +AC_ARG_ENABLE(cases, + [ --enable-cases=[yes/no] include special cases for likely parameter values (channel count, encoding sizes in libsyn123 routines) in the hope of better optimization at the expense of some code bloat (default enabled) ], + [ + if test "x$enableval" = xyes; then + specialcases=enabled + else + specialcases=disabled + fi + ], [ specialcases=enabled ]) + +if test "x$specialcases" = xdisabled; then + AC_DEFINE(SYN123_NO_CASES, 1, [ Define to not duplicate some code for likely cases in libsyn123. ]) +fi + sys_cppflags= newoldwritesample=disabled case $host in @@ -2498,6 +2512,9 @@ Note: Disabling core features is not commonly done and some combinations might n # just an empty line echo +echo " libsyn123 special cases . $specialcases" +echo + echo " Modules ................. $modules" echo " Checked audio modules ... $check_modules Detected audio support ..$output_modules diff --git a/src/libsyn123/libsyn123.c b/src/libsyn123/libsyn123.c index e93ddd84..29cfcf13 100644 --- a/src/libsyn123/libsyn123.c +++ b/src/libsyn123/libsyn123.c @@ -39,16 +39,6 @@ static size_t round2size(double a) return a < 0 ? 0 : (size_t)(a+0.5); } -static size_t smin(size_t a, size_t b) -{ - return a < b ? a : b; -} - -static size_t smax(size_t a, size_t b) -{ - return a > b ? a : b; -} - /* fractional part, relating to frequencies (so long matches) */ static double myfrac(double a) { @@ -597,7 +587,7 @@ syn123_read( syn123_handle *sh, void *dest, size_t dest_bytes ) int err = syn123_conv( sh->workbuf[0], sh->fmt.encoding, sizeof(sh->workbuf[0]) , sh->workbuf[1], MPG123_ENC_FLOAT_64, sizeof(double)*block - , NULL ); + , NULL, NULL ); if(err) { debug1("conv error: %i", err); diff --git a/src/libsyn123/sampleconv.c b/src/libsyn123/sampleconv.c index 2ec22af9..a3b3f9c8 100644 --- a/src/libsyn123/sampleconv.c +++ b/src/libsyn123/sampleconv.c @@ -16,6 +16,7 @@ */ #define NO_GROW_BUF +#define NO_SMAX #include "syn123_int.h" #include "sample.h" #include "debug.h" @@ -117,8 +118,8 @@ syn123_clip(void *buf, int encoding, size_t samples) // double or float. #define FROM_FLT(type) \ -{ type *tsrc = src; type *tend = tsrc+samples; char *tdest = dest; \ -switch(dest_enc) \ +{ type *tsrc = src; type *tend = tsrc+samples; char *tdest = dst; \ +switch(dst_enc) \ { \ case MPG123_ENC_SIGNED_16: \ for(; tsrc!=tend; ++tsrc, tdest+=2) \ @@ -181,7 +182,7 @@ switch(dest_enc) \ }} #define TO_FLT(type) \ -{ type* tdest = dest; type* tend = tdest + samples; char * tsrc = src; \ +{ type* tdest = dst; type* tend = tdest + samples; char * tsrc = src; \ switch(src_enc) \ { \ case MPG123_ENC_SIGNED_16: \ @@ -245,27 +246,39 @@ switch(src_enc) \ }} int attribute_align_arg -syn123_conv( void * MPG123_RESTRICT dest, int dest_enc, size_t dest_size -, void * MPG123_RESTRICT src, int src_enc, size_t src_bytes -, size_t *dest_bytes) +syn123_mixenc(int encoding) { - size_t inblock = MPG123_SAMPLESIZE(src_enc); - size_t outblock = MPG123_SAMPLESIZE(dest_enc); - size_t samples = src_bytes/inblock; + int esize = MPG123_SAMPLESIZE(encoding); + if(!esize) + return 0; + else + return (encoding != MPG123_ENC_FLOAT_32 && esize > 3) + ? MPG123_ENC_FLOAT_64 + : MPG123_ENC_FLOAT_32; +} + +int attribute_align_arg +syn123_conv( void * MPG123_RESTRICT dst, int dst_enc, size_t dst_size +, void * MPG123_RESTRICT src, int src_enc, size_t src_bytes +, size_t *dst_bytes, syn123_handle *sh ) +{ + size_t srcframe = MPG123_SAMPLESIZE(src_enc); + size_t dstframe = MPG123_SAMPLESIZE(dst_enc); + if(!srcframe || !dstframe) + return SYN123_BAD_ENC; + size_t samples = src_bytes/srcframe; debug6( "conv from %i (%i) to %i (%i), %zu into %zu bytes" , src_enc, MPG123_SAMPLESIZE(src_enc) - , dest_enc, MPG123_SAMPLESIZE(dest_enc) - , src_bytes, dest_size ); - if(!inblock || !outblock) - return SYN123_BAD_ENC; - if(!dest || !src) + , dst_enc, MPG123_SAMPLESIZE(dst_enc) + , src_bytes, dst_size ); + if(!dst || !src) return SYN123_BAD_BUF; - if(samples*inblock != src_bytes) + if(samples*srcframe != src_bytes) return SYN123_BAD_CHOP; - if(samples*outblock > dest_size) + if(samples*dstframe > dst_size) return SYN123_BAD_SIZE; - if(src_enc == dest_enc) - memcpy(dest, src, samples*outblock); + if(src_enc == dst_enc) + memcpy(dst, src, samples*dstframe); else if(src_enc & MPG123_ENC_FLOAT) { if(src_enc == MPG123_ENC_FLOAT_64) @@ -275,19 +288,53 @@ syn123_conv( void * MPG123_RESTRICT dest, int dest_enc, size_t dest_size else return SYN123_BAD_CONV; } - else if(dest_enc & MPG123_ENC_FLOAT) + else if(dst_enc & MPG123_ENC_FLOAT) { - if(dest_enc == MPG123_ENC_FLOAT_64) + if(dst_enc == MPG123_ENC_FLOAT_64) TO_FLT(double) - else if(dest_enc == MPG123_ENC_FLOAT_32) + else if(dst_enc == MPG123_ENC_FLOAT_32) TO_FLT(float) else return SYN123_BAD_CONV; } - else + else if(sh) + { + char *cdst = dst; + char *csrc = src; + int mixenc = syn123_mixenc(dst_enc); + int mixframe = MPG123_SAMPLESIZE(mixenc); + if(!mixenc || !mixframe) + return SYN123_BAD_CONV; + // Use the whole workbuf, both halves. + int mbufblock = 2*bufblock*sizeof(double)/mixframe; + mdebug("mbufblock=%i (enc %i)", mbufblock, mixenc); + // Abuse the handle workbuf for intermediate storage. + size_t samples_left = samples; + while(samples_left) + { + int block = (int)smin(samples_left, mbufblock); + int err = syn123_conv( + sh->workbuf, mixenc, sizeof(sh->workbuf) + , csrc, src_enc, srcframe*block + , NULL, NULL ); + if(!err) + err = syn123_conv( + cdst, dst_enc, dstframe*block + , sh->workbuf, mixenc, mixframe*block + , NULL, NULL ); + if(err) + { + mdebug("conv error: %i", err); + return SYN123_BAD_CONV; + } + cdst += dstframe*block; + csrc += srcframe*block; + samples_left -= block; + } + } else return SYN123_BAD_CONV; - if(dest_bytes) - *dest_bytes = outblock*samples; + if(dst_bytes) + *dst_bytes = dstframe*samples; return SYN123_OK; } @@ -331,6 +378,7 @@ void attribute_align_arg syn123_mono2many( void * MPG123_RESTRICT dest, void * MPG123_RESTRICT src , int channels, size_t samplesize, size_t samplecount ) { +#ifndef SYN123_NO_CASES switch(channels) { case 1: @@ -377,12 +425,16 @@ syn123_mono2many( void * MPG123_RESTRICT dest, void * MPG123_RESTRICT src } break; } +#else + BYTEMULTIPLY(dest, src, samplesize, samplecount, channels) +#endif } void attribute_align_arg syn123_interleave(void * MPG123_RESTRICT dest, void ** MPG123_RESTRICT src , int channels, size_t samplesize, size_t samplecount) { +#ifndef SYN123_NO_CASEs switch(channels) { case 1: @@ -429,12 +481,16 @@ syn123_interleave(void * MPG123_RESTRICT dest, void ** MPG123_RESTRICT src } break; } +#else + BYTEINTERLEAVE(dest, src, samplesize, samplecount, channels) +#endif } void attribute_align_arg syn123_deinterleave(void ** MPG123_RESTRICT dest, void * MPG123_RESTRICT src , int channels, size_t samplesize, size_t samplecount) { +#ifndef SYN123_NO_CASES switch(channels) { case 1: @@ -481,6 +537,161 @@ syn123_deinterleave(void ** MPG123_RESTRICT dest, void * MPG123_RESTRICT src } break; } +#else + BYTEDEINTERLEAVE(dest, src, samplesize, samplecount, channels) +#endif +} + +#define MIX_CODE(type,scc,dcc) \ + for(size_t i=0; i mixoutframe ? mixinframe : mixoutframe; + if(!mixenc || !mixinframe || !mixoutframe) + return SYN123_BAD_CONV; + // Mix from buffblock[0] to buffblock[1]. + int mbufblock = bufblock*sizeof(double)/mixframe; + mdebug("mbufblock=%i (enc %i)", mbufblock, mixenc); + // Need at least one sample per round to avoid endless loop. + // Of course, we would prefer more, but it's your fault for + // giving an excessive amount of channels without handling conversion + // beforehand. + if(mbufblock < 1) + return SYN123_BAD_CONV; + while(samples) + { + int block = (int)smin(samples, mbufblock); + int err = syn123_conv( + sh->workbuf[0], mixenc, sizeof(sh->workbuf[0]) + , csrc, src_enc, srcframe*block + , NULL, NULL ); + if(err) + return err; + err = syn123_mix( sh->workbuf[1], mixenc, dst_channels + , sh->workbuf[0], mixenc, src_channels, mixmatrix, block, NULL ); + if(err) + return err; + err = syn123_conv( + cdst, dst_enc, dstframe*block + , sh->workbuf[1], mixenc, mixoutframe*block + , NULL, NULL ); + if(err) + return err; + cdst += dstframe*block; + csrc += srcframe*block; + samples -= block; + } + return SYN123_OK; + } } /* All the byte-swappery for those little big endian boxes. */ diff --git a/src/libsyn123/syn123.h.in b/src/libsyn123/syn123.h.in index 7b9874ef..932ad1ac 100644 --- a/src/libsyn123/syn123.h.in +++ b/src/libsyn123/syn123.h.in @@ -73,6 +73,12 @@ extern "C" { * 1. Create handle with desired output format. * 2. Set up synthesis mode with parameters. * 3. Repeatedly extract buffers with PCM samples. + * + * The other functions for encoding conversion, (de-)interleaving, + * and interleaved mixing work without a handle and only use the + * buffers you hand in. Only the functions that are able to return + * a success code do check arguments for obvious trouble like + * NULL pointers. You are supposed to act responsibly when calling. @{ */ @@ -271,12 +277,16 @@ int syn123_setup_silence(syn123_handle *sh); * \param src_enc source encoding * \param src_bytes source buffer size in bytes * \param dest_bytes optional address to store the written byte count to + * \param sh an optional syn123_handle which enables arbitrary encoding + * conversions by utilizing the contained buffer as intermediate storage, + * can be NULL, disabling any conversion not involving floating point + * input or output * \return success code */ MPG123_EXPORT int syn123_conv( void * MPG123_RESTRICT dest, int dest_enc, size_t dest_size , void * MPG123_RESTRICT src, int src_enc, size_t src_bytes -, size_t *dest_bytes ); +, size_t *dest_bytes, syn123_handle * sh ); /** Clip samples in buffer to default range. * This only does anything with floating point encoding, but you can always @@ -336,6 +346,58 @@ MPG123_EXPORT void syn123_mono2many( void * MPG123_RESTRICT dest, void * MPG123_RESTRICT src , int channels, size_t samplesize, size_t samplecount ); +/** A little helper/reminder on how interleaved format works: + * Produce the offset of the given sample for the given channel. + */ +#define SYN123_IOFF(sample, channel, channels) ((sample)*(channels)+(channel)) + +/** Specify floating point encoding to use for preserving precision in + * intermediate computations for given output encoding. + * This should return either MPG123_ENC_FLOAT_32 or MPG123_ENC_FLOAT_64, + * unless an uncertain future adds things like 16 bit fp ... + * This is what syn123_conv() and syn123_mix() will use internally if + * intermediate conversion is necessary. + * \param inputenc input encoding + * \param outputenc output encoding + * \return encoding value, zero if none can be chosen (invalid parameters) + */ +MPG123_EXPORT +int syn123_mixenc(int encoding); + +/** Mix n input channels into m output channels. + * This takes an interleaved input stream and mixes its channels + * into the output stream given a channel matrix (m,n) where + * each of the m rows contains the n volume factors (weights) + * to apply when summing the samples from the n input channels. + * This works either on 32 bit or 64 bit floating point encodings. It + * may have some optimization to work faster with mono or stereo on + * either side and slower generic code for arbitrary channel counts. + * You can use syn123_conv() to convert from/to input/output encodings. + * There are no optimizations for special cases of mixing factors, so + * you should always be able to predict the number of floating point + * operations being executed. + * For fun, you could give the same problem to a BLAS implementation + * of your choice and compare the performance;-) + * \param dst destination buffer + * \param dst_channels destination channel count (m) + * \param src source buffer + * \param src_channels source channel count (n) + * \param mixmatrix mixing factors ((m,n) matrix), same encoding as + * the audio data + * \param encoding sample encoding, must be MPG123_ENC_FLOAT_32 or + * MPG123_ENC_FLOAT_64 unless a syn123_handle is provided + * \param sh an optional syn123_handle which enables work on non-float + * encodings by utilizing the contained buffer as intermediate storage, + * converting to/from float transparently; Note that this may limit + * the amount of channels depending on the available buffer space. + * \return success code (e.g. bad encoding, channel counts ...) + */ +MPG123_EXPORT +int syn123_mix( void * MPG123_RESTRICT dst, int dst_enc, int dst_channels +, void * MPG123_RESTRICT src, int src_enc, int src_channels +, const double * mixmatrix +, size_t samples, syn123_handle *sh ); + /** Swap byte order between little/big endian. * \param buf buffer to work on * \param samplesize size of one sample (see MPG123_SAMPLESIZE) diff --git a/src/libsyn123/syn123_int.h b/src/libsyn123/syn123_int.h index c514bc7e..09adcfcd 100644 --- a/src/libsyn123/syn123_int.h +++ b/src/libsyn123/syn123_int.h @@ -60,6 +60,20 @@ struct syn123_struct size_t offset; // offset in buffer for extraction helper }; +#ifndef NO_SMIN +static size_t smin(size_t a, size_t b) +{ + return a < b ? a : b; +} +#endif + +#ifndef NO_SMAX +static size_t smax(size_t a, size_t b) +{ + return a > b ? a : b; +} +#endif + #ifndef NO_GROW_BUF // Grow period buffer to at least given size. // Content is not preserved. diff --git a/src/out123.c b/src/out123.c index 50f1e77d..e052a0ed 100644 --- a/src/out123.c +++ b/src/out123.c @@ -7,8 +7,14 @@ initially written by Thomas Orgis (extracted from mpg123.c) - This is a stripped down mpg123 that only uses libout123 to write standard input - to an audio device. + This is a stripped down mpg123 that only uses libout123 to write standard + input to an audio device. Of course, it got some enhancements with the + advent of libsyn123. + + Please bear in mind that the code started out as a nasty hack on a very + old piece made out of nasty hacks and plain ugly code. Some nastiness + (like lax parameter checking) even serves a purpose: Test the robustness + of our libraries in catching bad caller behaviour. TODO: Add basic parsing of WAV headers to be able to pipe in WAV files, especially from something like mpg123 -w -. @@ -73,10 +79,8 @@ static char *encoding_name = NULL; static int encoding = MPG123_ENC_SIGNED_16; static char *inputenc_name = NULL; static int inputenc = 0; -static int mixenc = 0; static int channels = 2; static int inputch = 0; -static float *chmatrix = NULL; static long rate = 44100; static char *driver = NULL; static char *device = NULL; @@ -110,13 +114,15 @@ size_t pcmblock = 1152; /* samples (pcm frames) we treat en bloc */ /* To be set after settling format. */ size_t pcmframe = 0; size_t pcminframe = 0; -size_t pcmmixframe = 0; unsigned char *audio = NULL; unsigned char *inaudio = NULL; -unsigned char *mixaudio = NULL; +char *mixmat_string = NULL; +double *mixmat = NULL; -/* Option to play some oscillatory test signals. */ +// Option to play some oscillatory test signals. +// Also used for conversions. syn123_handle *waver = NULL; +int generate = FALSE; // Wheter to use the syn123 generator. out123_handle *ao = NULL; char *cmd_name = NULL; @@ -170,10 +176,10 @@ static void safe_exit(int code) /* It's ugly... but let's just fix this still-reachable memory chunk of static char*. */ split_dir_file("", &dummy, &dammy); if(fullprogname) free(fullprogname); - if(mixaudio) free(mixaudio); + if(mixmat) free(mixmat); if(inaudio && inaudio != audio) free(inaudio); if(audio) free(audio); - syn123_del(waver); + if(waver) syn123_del(waver); exit(code); } @@ -184,7 +190,17 @@ static void check_fatal_output(int code) if(!quiet) error2( "out123 error %i: %s" , out123_errcode(ao), out123_strerror(ao) ); - safe_exit(code); + safe_exit(133); + } +} + +static void check_fatal_syn(int code) +{ + if(code) + { + if(!quiet) + merror("syn123 error %i: %s", code, syn123_strerror(code)); + safe_exit(132); } } @@ -472,6 +488,7 @@ topt opts[] = { {0, "stereo", GLO_INT, 0, &channels, 2}, {'c', "channels", GLO_ARG | GLO_INT, 0, &channels, 0}, {'C', "inputch", GLO_ARG | GLO_INT, 0, &inputch, 0}, + {'M', "mix", GLO_ARG | GLO_CHAR, 0, &mixmat_string, 0}, {'r', "rate", GLO_ARG | GLO_LONG, 0, &rate, 0}, {0, "clip", GLO_INT, 0, &do_clip, TRUE}, {0, "headphones", 0, set_output_h, 0,0}, @@ -571,52 +588,57 @@ static void setup_wavegen(void) int synerr = 0; size_t common = 0; + if(!generate) + wave_limit = 0; + waver = syn123_new(rate, inputch, inputenc, wave_limit, &synerr); + check_fatal_syn(synerr); + if(!waver) + safe_exit(132); + // At least have waver handy for conversions. + if(!generate) + return; + if(!strcmp(signal_source, "pink")) { - waver = syn123_new(rate, channels, inputenc, wave_limit, &synerr); - if(waver) - synerr = syn123_setup_pink(waver, pink_rows, &common); - if(!waver || synerr) + synerr = syn123_setup_pink(waver, pink_rows, &common); + if(synerr) { - error1("setting up pink noise generator: %s\n", syn123_strerror(synerr)); + if(!quiet) + merror("setting up pink noise generator: %s\n", syn123_strerror(synerr)); safe_exit(132); } if(verbose) { - if(common) - fprintf(stderr, "out123: periodic signal table of %" SIZE_P " samples\n", common); - else - fprintf(stderr, "out123: live signal generation\n"); - fprintf(stderr, "out123; pink noise with %i generator rows (0=internal default)\n" - , pink_rows); + fprintf( stderr + , ME ": pink noise with %i generator rows (0=internal default)\n" + , pink_rows ); } - return; + goto setup_waver_end; } - - if(!strcmp(signal_source, "geiger")) + else if(!strcmp(signal_source, "geiger")) { - waver = syn123_new(rate, channels, inputenc, wave_limit, &synerr); - if(waver) - synerr = syn123_setup_geiger(waver, geiger_activity, &common); - if(!waver || synerr) + synerr = syn123_setup_geiger(waver, geiger_activity, &common); + if(synerr) { - error1("setting up geiger generator: %s\n", syn123_strerror(synerr)); + if(!quiet) + merror("setting up geiger generator: %s\n", syn123_strerror(synerr)); safe_exit(132); } if(verbose) { - if(common) - fprintf(stderr, "out123: periodic signal table of %" SIZE_P " samples\n", common); - else - fprintf(stderr, "out123: live signal generation\n"); - fprintf(stderr, "out123; geiger with actvity %g\n", geiger_activity); + fprintf(stderr, ME ": geiger with actvity %g\n", geiger_activity); } - return; + goto setup_waver_end; + } + else if(strcmp(signal_source, "wave")) + { + if(!quiet) + merror("unknown signal source: %s", signal_source); + safe_exit(132); } - if(strcmp(signal_source, "wave")) - return; - + // The big default code block is for wave setup. + // Exceptions jump over it. if(wave_freqs) { char *tok; @@ -704,31 +726,34 @@ static void setup_wavegen(void) } } - waver = syn123_new(rate, channels, inputenc, wave_limit, &synerr); - if(waver) - synerr = syn123_setup_waves( waver, count - , id, freq_real, phase, backwards, &common ); - if(!waver || synerr) + synerr = syn123_setup_waves( waver, count + , id, freq_real, phase, backwards, &common ); + if(synerr) { - error1("setting up wave generator: %s\n", syn123_strerror(synerr)); + if(!quiet) + merror("setting up wave generator: %s\n", syn123_strerror(synerr)); safe_exit(132); } if(verbose) { - if(common) - fprintf(stderr, "out123: periodic signal table of %" SIZE_P " samples\n", common); - else - fprintf(stderr, "out123: live signal generation\n"); if(count) for(i=0; i= 0) { if(offset >= timelimit) @@ -749,7 +775,7 @@ int play_frame(void) else if(timelimit < offset+get_samples) get_samples = (off_t)timelimit-offset; } - if(waver) + if(generate) got_samples = syn123_read(waver, inaudio, get_samples*pcminframe)/pcminframe; else got_samples = fread(inaudio, pcminframe, get_samples, input); @@ -760,31 +786,15 @@ int play_frame(void) size_t got_bytes = 0; if(inaudio != audio) { - // Convert audio. - if(inputch == channels) // Just encoding + if(mixmat) { - int err; - if(mixaudio) - { - err = syn123_conv( mixaudio, mixenc, pcmblock*pcmmixframe - , inaudio, inputenc, got_samples*pcminframe, &got_bytes ); - if(!err) - err = syn123_conv( audio, encoding, pcmblock*pcmframe - , mixaudio, mixenc, got_bytes, &got_bytes ); - } - else - err = syn123_conv( audio, encoding, got_samples*pcmframe - , inaudio, inputenc, got_samples*pcminframe, &got_bytes ); - if(err) - { - error1("conversion failure: %s", syn123_strerror(err)); - safe_exit(135); - } - } - else + check_fatal_syn(syn123_mix( audio, encoding, channels + , inaudio, inputenc, inputch, mixmat, got_samples, waver )); + got_bytes = pcmframe * got_samples; + } else { - error("mixing not implemented yet"); - safe_exit(120); + check_fatal_syn(syn123_conv( audio, encoding, got_samples*pcmframe + , inaudio, inputenc, got_samples*pcminframe, &got_bytes, waver )); } } else @@ -793,17 +803,10 @@ int play_frame(void) { size_t clipped = syn123_clip(audio, encoding, got_samples*channels); if(verbose > 1 && clipped) - fprintf(stderr, "out123: clipped %"SIZE_P" samples\n", clipped); - } - if(out123_play(ao, audio, got_bytes) < (int)got_bytes) - { - if(!quiet) - { - error2( "out123 error %i: %s" - , out123_errcode(ao), out123_strerror(ao) ); - } - safe_exit(133); + fprintf(stderr, ME ": clipped %"SIZE_P" samples\n", clipped); } + mdebug("playing %zu bytes", got_bytes); + check_fatal_output(out123_play(ao, audio, got_bytes) < (int)got_bytes); if(also_stdout && fwrite(audio, pcmframe, got_samples, stdout) < got_samples) { if(!quiet && errno != EINTR) @@ -828,7 +831,8 @@ static void *fatal_malloc(size_t bytes) void *buf; if(!(buf = malloc(bytes))) { - error("OOM"); + if(!quiet) + error("OOM"); safe_exit(1); } return buf; @@ -969,25 +973,46 @@ int main(int sys_argc, char ** sys_argv) } if(!inputch) inputch = channels; - // Up to 24 bit integer is mixed using float, anything above using double. - // Except 32 bit float, of course. - mixenc = ((encoding != MPG123_ENC_FLOAT_32 && out123_encsize(encoding) > 3) || - (inputenc != MPG123_ENC_FLOAT_32 && out123_encsize(inputenc) > 3)) - ? MPG123_ENC_FLOAT_64 - : MPG123_ENC_FLOAT_32; - pcmmixframe = out123_encsize(mixenc)*channels; pcminframe = out123_encsize(inputenc)*inputch; pcmframe = out123_encsize(encoding)*channels; audio = fatal_malloc(pcmblock*pcmframe); - if(inputenc != encoding) + // Full mixing is initiated if channel counts differ or a non-empty + // mixing matrix has been specified. + if(1 || inputch != channels || (mixmat_string && mixmat_string[0])) { - inaudio = fatal_malloc(pcmblock*pcminframe); - if(!(inputenc & MPG123_ENC_FLOAT || encoding & MPG123_ENC_FLOAT)) - mixaudio = fatal_malloc(pcmblock*pcmmixframe); + mixmat = fatal_malloc(sizeof(double)*inputch*channels); + size_t mmcount = mytok_count(mixmat_string); + // Special cases of trivial down/upmixing need no user input. + if(mmcount == 0 && inputch == 1) + { + for(int oc=0; oc