1
0
mirror of http://mpg123.de/trunk/.git synced 2025-10-25 04:37:34 +03:00

out123+libsyn123: getting crazy with mixing and conversion

git-svn-id: svn://scm.orgis.org/mpg123/trunk@4423 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
thor
2018-02-26 00:27:43 +00:00
parent bc5f1093f2
commit add6ac02ad
7 changed files with 478 additions and 149 deletions

6
NEWS
View File

@@ -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 …

View File

@@ -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

View File

@@ -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);

View File

@@ -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<samples; ++i) \
{ \
for(int dc=0; dc<dcc; ++dc) \
{ \
dst[SYN123_IOFF(i, dc, dcc)] = \
(type)mixmatrix[SYN123_IOFF(dc,0,scc)] * src[SYN123_IOFF(i,0,scc)]; \
for(int sc=1; sc<scc; ++sc) \
dst[SYN123_IOFF(i,dc,dcc)] += \
(type)mixmatrix[SYN123_IOFF(dc,sc,scc)] * src[SYN123_IOFF(i,sc,scc)]; \
} \
}
// I decided against optimizing for mixing factors of 1, unity matrix ...
// A floating point multiplication is not that expensive and there might
// be value in the runtime of the function not depending on the matrix
// values, only on channel setup and sample count.
// I might trust the compiler here to hardcode the case value for
// src/dst_channels. But why take the risk?
#ifndef SYN123_NO_CASES
#define SYN123_MIX_FUNC(type) \
switch(src_channels) \
{ \
case 1: \
switch(dst_channels) \
{ \
case 1: \
MIX_CODE(type,1,1) \
break; \
case 2: \
MIX_CODE(type,1,2) \
break; \
default: \
MIX_CODE(type,1,dst_channels) \
} \
break; \
case 2: \
switch(dst_channels) \
{ \
case 1: \
MIX_CODE(type,2,1) \
break; \
case 2: \
MIX_CODE(type,2,2) \
break; \
default: \
MIX_CODE(type,2,dst_channels) \
} \
break; \
default: \
MIX_CODE(type,src_channels, dst_channels) \
}
#else
#define SYN123_MIX_FUNC(type) \
MIX_CODE(type, src_channels, dst_channels)
#endif
static void syn123_mix_f32( float * MPG123_RESTRICT dst, int dst_channels
, float * MPG123_RESTRICT src, int src_channels
, const double * MPG123_RESTRICT mixmatrix
, size_t samples )
{
debug("syn123_mix_f32");
SYN123_MIX_FUNC(float)
}
static void syn123_mix_f64( double * MPG123_RESTRICT dst, int dst_channels
, double * MPG123_RESTRICT src, int src_channels
, const double * MPG123_RESTRICT mixmatrix
, size_t samples )
{
debug("syn123_mix_f64");
SYN123_MIX_FUNC(double)
}
int attribute_align_arg
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 )
{
if(src_channels < 1 || dst_channels < 1)
return SYN123_BAD_FMT;
if(!dst || !src || !mixmatrix)
return SYN123_BAD_BUF;
if(dst_enc == src_enc) switch(dst_enc)
{
case MPG123_ENC_FLOAT_32:
syn123_mix_f32( dst, dst_channels, src, src_channels
, mixmatrix, samples );
return SYN123_OK;
break;
case MPG123_ENC_FLOAT_64:
syn123_mix_f64( dst, dst_channels, src, src_channels
, mixmatrix, samples );
return SYN123_OK;
break;
}
// If still here: Some conversion needed.
if(!sh)
return SYN123_BAD_ENC;
else
{
debug("mix with conversion");
char *cdst = dst;
char *csrc = src;
int srcframe = MPG123_SAMPLESIZE(src_enc)*src_channels;
int dstframe = MPG123_SAMPLESIZE(dst_enc)*dst_channels;
if(!srcframe || !dstframe)
return SYN123_BAD_ENC;
int mixenc = syn123_mixenc(dst_enc);
int mixinframe = MPG123_SAMPLESIZE(mixenc)*src_channels;
int mixoutframe = MPG123_SAMPLESIZE(mixenc)*dst_channels;
int mixframe = mixinframe > 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. */

View File

@@ -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)

View File

@@ -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.

View File

@@ -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<count; ++i)
fprintf( stderr, "out123: wave %" SIZE_P ": %s @ %g Hz (%g Hz) p %g\n"
fprintf( stderr, ME ": wave %" SIZE_P ": %s @ %g Hz (%g Hz) p %g\n"
, i
, syn123_wave_name(id ? id[i] : SYN123_WAVE_SINE)
, freq[i], freq_real[i]
, phase ? phase[i] : 0 );
else
fprintf(stderr, "out123: default sine wave\n");
fprintf(stderr, ME ": default sine wave\n");
}
setup_waver_end:
if(verbose)
{
if(common)
fprintf(stderr, ME ": periodic signal table of %" SIZE_P " samples\n", common);
else
fprintf(stderr, ME ": live signal generation\n");
}
if(phase)
free(phase);
if(id)
@@ -742,6 +767,7 @@ int play_frame(void)
{
size_t got_samples;
size_t get_samples = pcmblock;
debug("play_frame");
if(timelimit >= 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<channels; ++oc)
mixmat[oc] = 1.;
}
else if(mmcount == 0 && channels == 1)
{
for(int ic=0; ic<inputch; ++ic)
mixmat[ic] = 1./inputch;
}
else if(mmcount != inputch*channels)
{
merror( "Need %i mixing matrix entries, got %zu."
, inputch*channels, mmcount );
safe_exit(1);
} else
{
char *next = mixmat_string;
for(int i=0; i<inputch*channels; ++i)
{
char *tok = mytok(&next);
mixmat[i] = tok ? atof(tok) : 0.;
}
}
}
// If converting or mixing, use separate input buffer.
if(inputenc != encoding || mixmat)
inaudio = fatal_malloc(pcmblock*pcminframe);
else
inaudio = audio;
check_fatal_output(out123_set_buffer(ao, buffer_kb*1024));
check_fatal_output(out123_open(ao, driver, device));
@@ -999,13 +1024,22 @@ int main(int sys_argc, char ** sys_argv)
if(inaudio != audio)
{
encname = out123_enc_name(inputenc);
fprintf( stderr, ME": input format: %li Hz, %i, channels, %s\n"
, rate, channels, encname ? encname : "???" );
}
if(mixaudio)
{
encname = out123_enc_name(mixenc);
fprintf( stderr, ME": mixing in %s\n", encname ? encname : "???" );
fprintf( stderr, ME": input format: %li Hz, %i channels, %s\n"
, rate, inputch, encname ? encname : "???" );
encname = out123_enc_name(syn123_mixenc(encoding));
if(mixmat)
{
fprintf( stderr, ME": mixing in %s\n", encname ? encname : "???" );
for(int oc=0; oc<channels; ++oc)
{
fprintf(stderr, ME": out ch %i mix:", oc);
for(int ic=0; ic<inputch; ++ic)
fprintf(stderr, " %4.2f", mixmat[SYN123_IOFF(oc,ic,inputch)]);
fprintf(stderr, "\n");
}
}
else
fprintf( stderr, ME": converting via %s\n", encname ? encname : "???" );
}
encname = out123_enc_name(encoding);
fprintf(stderr, ME": format: %li Hz, %i channels, %s\n"
@@ -1021,14 +1055,8 @@ int main(int sys_argc, char ** sys_argv)
input = stdin;
if(strcmp(signal_source, "file"))
{
setup_wavegen();
if(!waver)
{
error("unknown signal source");
safe_exit(133);
}
}
generate = TRUE;
setup_wavegen(); // Used also for conversion/mixing.
while(play_frame() && !intflag)
{
@@ -1208,7 +1236,8 @@ static void long_usage(int err)
fprintf(o," --longhelp give this long help listing\n");
fprintf(o," --version give name / version string\n");
fprintf(o,"\nSee the manpage out123(1) for more information.\n");
fprintf(o,"\nSee the manpage out123(1) for more information. Also, note that\n");
fprintf(o,"any numeric arguments are parsed in C locale (pi is 3.14, not 3,14).");
free(enclist);
safe_exit(err);
}