Initial commit of mpg123-1.29.3

This commit is contained in:
Sam Lantinga
2022-05-07 15:47:18 -07:00
commit 01b6013145
352 changed files with 159933 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# Module for non-recursive mpg123 build system.
# This bulds libsyn123, a basic library for some sound synthesis
# and format conversion for the mpg123 project.
lib_LTLIBRARIES += src/libsyn123/libsyn123.la
src_libsyn123_libsyn123_la_CFLAGS = @LIB_CFLAGS@
src_libsyn123_libsyn123_la_LDFLAGS = \
-no-undefined \
-version-info @LIBSYN123_VERSION@ \
-export-symbols-regex '^syn123_'
src_libsyn123_libsyn123_la_LIBADD = \
src/compat/libcompat_str.la \
@LIBSYN123_LIBS@
src_libsyn123_libsyn123_la_SOURCES = \
src/libsyn123/syn123_int.h \
src/libsyn123/g711_impl.h \
src/libsyn123/pinknoise.c \
src/libsyn123/geiger.c \
src/libsyn123/libsyn123.c \
src/libsyn123/volume.c \
src/libsyn123/resample.c \
src/libsyn123/filter.c \
src/libsyn123/sampleconv.c
EXTRA_DIST += src/libsyn123/syn123.h.in
nodist_include_HEADERS += \
src/libsyn123/syn123.h

373
src/libsyn123/filter.c Normal file
View File

@@ -0,0 +1,373 @@
/*
filter: apply IIR/FIR filters using Direct Form II
copyright 2019 by the mpg123 project
licensed under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
Since I spent a lot of time tuning filters for my resampler, I figured
I should add generic digital filters to the syn123 API. There are
multiple independent implementations of filters inside the resampler,
using hardcoded parameters. They should be functionally equivalent to
this generic code. Let it serve as a reference.
The code should be straightforward to read, but is messed up a bit
by the task to be runtime-tunable for single or double precision.
Also, the coefficients are stored multiple times in a shifted manner
to avoid shifting the filter history around and keeping efficient
coefficient access. I guess this is a pretty standard optimization
which worked well for the resampler.
I use macros. Don't be scared.
*/
#define NO_GROW_BUF
#define NO_SMAX
#define NO_SMIN
#include "syn123_int.h"
// 0 ... ORDER
// b0 stored
// a0 == 1
// ringbuffer-optimized storage of the others
// b[order][order]
// a[order][order]
// historic values, ringbuffer
// w[channels][order]
// So, one piece of storage of size order*(2*order+channels).
// Theoretical limit on maximum order: data block must fit into size_t.
// Checking overflow of order*order*2 + order*channels, in that order.
#define ORDER_TOO_BIG(order, channels) \
((order) == 0 || (channels) == 0) \
? 0 \
: ( (order) > SIZE_MAX/2/(order) \
? 1 \
: ( (order) > (SIZE_MAX-(order)*(order)*2)/(channels) \
? 1 \
: 0 \
)\
)
// size of filter data block
#define F_DATABLOCK(f, channels) ((f).order*(2*(f).order+channels))
// base addresses of b and a coefficient sets, ringbuffered
#define Fb(f) ((f).data)
#define Fa(f) ((f).data+(f).order*(f).order)
// address of currently active coefficient set
// the base is either f.b or f.a (which may be NULL)
#define Fc(f, base) ((base) ? ((base)+(f).order*(f).n1) : NULL)
// base address of current channel's history values
#define Fw(f, c) ((f).data+F_DATABLOCK((f), (c)))
#define FILTER_STRUCT(name, type) \
struct name \
{ \
int flow; /* if the filter has had first data and is in flow now */ \
unsigned int order; /* filter order */ \
unsigned int n1; /* current position of most recent past */ \
type init_scale; /* scale for first sample as endless history */ \
type b0; /* coefficient b_0 */ \
type *data; /* dynamic coefficient and history data */ \
type *b; /* b coefficients b_1 ... b_N, pointer into the above */ \
type *a; /* same for a, or NULL if non-recursive */ \
};
FILTER_STRUCT(f_filter, float)
FILTER_STRUCT(d_filter, double)
// Initialization of Direct Form II history.
// Return a constant value for w[n] that represents an endless history of given insample.
// Given the recursive definition: w[n] = x[n] - sum_i(a[i] * w[n-i])
// Given my ansatz of endless stream of x[n-i] = x[n] and hence w[n-i] = w[n], it follows
// that
// w[n] = x[n] - sum_i(a[i] * w[n]) = x[n] - sum_i(a[i]) * w[n]
// <=> (1 + sum_i(a[i])) w[n] = x[n]
// <=> w[n] = 1 / (1 + sum_i(a[i])) * x[n]
// Looks simple. Just a scale value.
#define INIT_SCALE(name, type) \
static type name(unsigned int order, type *filter_a) \
{ \
type asum = 1.; \
if(filter_a) \
for(unsigned int i=0; i<order; ++i) \
asum += filter_a[i]; \
return (asum > 1e-12 || asum < -1e-12) ? (type)1./asum : 0; \
}
INIT_SCALE(d_init_scale, double)
INIT_SCALE(f_init_scale, float)
// Static ring buffer index used for filter history and coefficients.
// i: offset
// n1: current position of the first entry
// s: size of the ring buffer
#define RING_INDEX(i, n1, s) ( ((n1)+(i)) % s )
// In the context of a filter: stored offset n1, configured order.
#define F_RING_INDEX(f, i) RING_INDEX(i, (f).n1, (f).order)
int attribute_align_arg
syn123_setup_filter( syn123_handle *sh
, int append, unsigned int order, double *b, double *a
, int mixenc, int channels, int init_firstval )
{
if(!sh)
return SYN123_BAD_HANDLE;
if(!append)
{
syn123_drop_filter(sh, sh->fc.count);
if(sh->fc.count)
return SYN123_WEIRD;
}
if(sh->fc.count)
{
if(!mixenc)
mixenc = sh->fc.mixenc;
if(!channels)
channels = sh->fc.channels;
}
if(channels < 1)
return SYN123_BAD_FMT;
if(mixenc != MPG123_ENC_FLOAT_32 && mixenc != MPG123_ENC_FLOAT_64)
return SYN123_BAD_ENC;
if(!b)
return SYN123_NO_DATA;
if(a && a[0] != 1.)
return SYN123_BAD_DATA;
if(ORDER_TOO_BIG(order, channels) || sh->fc.count == SIZE_MAX)
return SYN123_OVERFLOW;
if(sh->fc.maxcount == sh->fc.count)
{
#define EXPAND_FILTERS(list, stype) \
{ \
struct stype *tmp = realloc( list \
, sizeof(struct stype)*(sh->fc.maxcount+1) ); \
if(!tmp) \
return SYN123_DOOM; \
list = tmp; \
}
if(mixenc == MPG123_ENC_FLOAT_32)
EXPAND_FILTERS(sh->fc.ff, f_filter)
else
EXPAND_FILTERS(sh->fc.df, d_filter)
// Storage for filter structs increased, nobody can take that away now.
#undef EXPAND_FILTERS
++sh->fc.maxcount;
}
// Got enough struct storage, try to fill the filter.
#define MAKE_FILTER(list, stype, scaler, type) \
{ \
struct stype *tmp = list+sh->fc.count; \
tmp->flow = 0; \
tmp->order = order; \
tmp->b0 = b[0]; \
tmp->data = malloc(sizeof(type)*F_DATABLOCK(*tmp, channels)); \
if(!tmp->data) \
return SYN123_DOOM; \
tmp->b = Fb(*tmp); \
tmp->a = a ? Fa(*tmp) : NULL; \
for(tmp->n1=0; tmp->n1<order; ++tmp->n1) \
{ \
type *f_b = Fc(*tmp, tmp->b); \
type *f_a = Fc(*tmp, tmp->a); \
for(unsigned int o=0; o<order; ++o) \
{ \
unsigned int ri = F_RING_INDEX(*tmp, o); \
f_b[ri] = b[o+1]; \
if(f_a) \
f_a[ri] = a[o+1]; \
} \
} \
tmp->n1 = 0; \
tmp->init_scale = init_firstval ? scaler(order, tmp->a) : 0.; \
if(tmp->init_scale == 0.) \
{ \
for(int c=0; c<channels; ++c) \
{ \
type *w = Fw(*tmp, c); \
for(unsigned int o=0; o<order; ++o) \
w[o] = 0.; \
} \
tmp->flow = 1; \
} \
}
if(mixenc == MPG123_ENC_FLOAT_32)
MAKE_FILTER(sh->fc.ff, f_filter, f_init_scale, float)
else
MAKE_FILTER(sh->fc.df, d_filter, d_init_scale, double)
#undef MAKE_FILTER
// Got fresh filter with memory. Count it.
++sh->fc.count;
// Finish off things that cannot error out.
sh->fc.mixenc = mixenc;
sh->fc.channels = channels;
return SYN123_OK;
}
int attribute_align_arg
syn123_query_filter( syn123_handle *sh, size_t position
, size_t *count, unsigned int *order, double *b, double *a
, int *mixenc, int *channels, int *init_firstval )
{
if(!sh)
return SYN123_BAD_HANDLE;
if( (order || b || a || mixenc || channels || init_firstval)
&& position >= sh->fc.count )
return SYN123_NO_DATA;
if(count)
*count = sh->fc.count;
if(!sh->fc.count)
return SYN123_OK;
if(channels)
*channels = sh->fc.channels;
if(mixenc)
*mixenc = sh->fc.mixenc;
#define FILTER_INFO(filter, type) \
{ \
if(order) \
*order = filter.order; \
if(init_firstval) \
*init_firstval = filter.init_scale != 0. ? 1 : 0; \
if(b || a) \
{ \
if(b) \
b[0] = filter.b0; \
if(a) \
a[0] = 1.; \
for(unsigned int o=0; o<filter.order; ++o) \
{ \
if(b) \
b[1+o] = filter.b[o]; \
if(a) \
a[1+o] = filter.a ? filter.a[o] : 0.; \
} \
} \
}
if(sh->fc.mixenc == MPG123_ENC_FLOAT_32)
FILTER_INFO(sh->fc.ff[position], float)
else
FILTER_INFO(sh->fc.df[position], double)
#undef FILTER_INFO
return SYN123_OK;
}
// This just forgets the filter's own storage, not the storage for
// the filter structs. That would need realloc(), which might fail.
void attribute_align_arg
syn123_drop_filter(syn123_handle *sh, size_t count)
{
if(!sh)
return;
if(count > sh->fc.count)
count = sh->fc.count;
// Free data of indicated filters.
for(size_t i=0; i<count; ++i)
free( sh->fc.mixenc == MPG123_ENC_FLOAT_32
? (void*)(sh->fc.ff[--sh->fc.count].data)
: (void*)(sh->fc.df[--sh->fc.count].data) );
// count decreased now, maxcount still there
}
#define APPLY_ONE_FILTER(name, stype, type) \
static void name( struct stype *f \
, int channels, type *audio, size_t samples ) \
{ \
if(!samples) \
return; \
if(!f->flow) \
{ \
for(int c=0; c<channels; ++c) \
{ \
type iv = f->init_scale*audio[c]; \
type *w = Fw(*f, c); \
for(unsigned int o=0; o<f->order; ++o) \
w[o] = iv; \
} \
f->n1 = 0; \
f->flow = 1; \
} \
/* Leave loop ordering and mono/stereo optimization to the future.*/ \
/* This is what is most logical for interleaved audio and the n1 logic */ \
if(f->a) /* IIR, possibly */ \
{ \
for(size_t s=0; s<samples; ++s) \
{ \
/* Working with the current value of n1. */ \
type *b = Fc(*f, f->b); \
type *a = Fc(*f, f->a); \
unsigned int next_n1 = F_RING_INDEX(*f, f->order-1); \
for(int c=0; c<channels; ++c) \
{ \
type ny = 0; \
type nw = 0; \
type *w = Fw(*f, c); \
for(unsigned int i=0; i<f->order; ++i) \
{ \
ny += w[i]*b[i]; \
nw -= w[i]*a[i]; \
} \
nw += audio[c]; \
ny += f->b0 * nw; \
w[next_n1] = nw; \
audio[c] = ny; \
} \
f->n1 = next_n1; \
audio += channels; \
} \
} else /* FIR */ \
{ \
for(size_t s=0; s<samples; ++s) \
{ \
/* Working with the current value of n1. */ \
type *b = Fc(*f, f->b); \
unsigned int next_n1 = F_RING_INDEX(*f, f->order-1); \
for(int c=0; c<channels; ++c) \
{ \
type ny = 0; \
type nw = audio[c]; \
type *w = Fw(*f, c); \
for(unsigned int i=0; i<f->order; ++i) \
ny += w[i]*b[i]; \
ny += f->b0 * nw; \
w[next_n1] = nw; \
audio[c] = ny; \
} \
f->n1 = next_n1; \
audio += channels; \
} \
} \
}
APPLY_ONE_FILTER(apply_filter_float, f_filter, float)
APPLY_ONE_FILTER(apply_filter_double, d_filter, double)
// Finally, the filter application.
int attribute_align_arg
syn123_filter(syn123_handle *sh, void* buf, int encoding, size_t samples)
{
if(!sh)
return SYN123_BAD_HANDLE;
if(!sh->fc.count) // No filter means no change, all good.
return SYN123_OK;
#define APPLY_FILTERS(list, func) \
for(unsigned int i=0; i<sh->fc.count; ++i) \
func(list+i, sh->fc.channels, buf, samples)
if(encoding == sh->fc.mixenc)
{
if(sh->fc.mixenc == MPG123_ENC_FLOAT_32)
APPLY_FILTERS(sh->fc.ff, apply_filter_float);
else
APPLY_FILTERS(sh->fc.df, apply_filter_double);
} else
{
return SYN123_BAD_ENC;
}
#undef APPLY_FILTERS
return SYN123_OK;
}

246
src/libsyn123/g711_impl.h Normal file
View File

@@ -0,0 +1,246 @@
// TODO: Avoid shifting signed integers. Not well-defined behaviour.
/*
* This source code is a product of Sun Microsystems, Inc. and is provided
* for unrestricted use. Users may copy or modify this source code without
* charge.
*
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun source code is provided with no support and without any obligation on
* the part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* g711.c
*
* u-law, A-law and linear PCM conversions.
*/
/*
* January 28, 2018:
* Stripped out the direct tables again and made things static for
* use in syn123 for basic ulaw/alaw conversion support. Also changed
* to more explicit integer types.
* Thomas Orgis, thomas@orgis.org
*
*/
/*
* December 30, 1994:
* Functions linear2alaw, linear2ulaw have been updated to correctly
* convert unquantized 16 bit values.
* Tables for direct u- to A-law and A- to u-law conversions have been
* corrected.
* Borge Lindberg, Center for PersonKommunikation, Aalborg University.
* bli@cpk.auc.dk
*
*/
static const unsigned char sign_bit = 0x80; /* Sign bit for a A-law byte. */
static const unsigned char quant_mask = 0xf; /* Quantization field mask. */
static const unsigned char seg_shift = 4; /* Left shift for segment number. */
static const unsigned char seg_mask = 0x70; /* Segment field mask. */
static int16_t seg_aend[8] = {
0x01F, 0x03F, 0x07F, 0x0FF,
0x1FF, 0x3FF, 0x7FF, 0xFFF
};
static int16_t seg_uend[8] = {
0x03F, 0x07F, 0x0FF, 0x01FF,
0x3FF, 0x7FF, 0xFFF, 0x1FFF
};
static int search(int16_t val, int16_t *table, int size)
{
int i;
for (i = 0; i < size; i++) {
if (val <= *table++)
return (i);
}
return (size);
}
/*
* linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
*
* linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
*
* Linear Input Code Compressed Code
* ------------------------ ---------------
* 0000000wxyza 000wxyz
* 0000001wxyza 001wxyz
* 000001wxyzab 010wxyz
* 00001wxyzabc 011wxyz
* 0001wxyzabcd 100wxyz
* 001wxyzabcde 101wxyz
* 01wxyzabcdef 110wxyz
* 1wxyzabcdefg 111wxyz
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
static unsigned char linear2alaw(int16_t pcm_val)
{
int16_t mask;
int seg;
int aval; /* int is comfortable, even if bigger than needed */
pcm_val = pcm_val >> 3;
if (pcm_val >= 0) {
mask = 0xD5; /* sign (7th) bit = 1 */
} else {
mask = 0x55; /* sign bit = 0 */
pcm_val = -pcm_val - 1;
}
/* Convert the scaled magnitude to segment number. */
seg = search(pcm_val, seg_aend, 8);
/* Combine the sign, segment, and quantization bits. */
if (seg >= 8) /* out of range, return maximum value. */
return (unsigned char)(0x7F ^ mask);
else {
aval = seg << seg_shift;
if (seg < 2)
aval |= (pcm_val >> 1) & quant_mask;
else
aval |= (pcm_val >> seg) & quant_mask;
return (unsigned char)(aval ^ mask);
}
}
/*
* alaw2linear() - Convert an A-law value to 16-bit linear PCM
*
*/
int16_t alaw2linear(unsigned char a_val)
{
int16_t t;
int seg;
a_val ^= 0x55;
t = (a_val & quant_mask) << 4;
seg = (a_val & seg_mask) >> seg_shift;
switch (seg) {
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
}
return ((a_val & sign_bit) ? t : -t);
}
static const int16_t bias = 0x84; /* Bias for linear code. */
static const int16_t clip = 8159;
/*
* linear2ulaw() - Convert a linear PCM value to u-law
*
* In order to simplify the encoding process, the original linear magnitude
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
* (33 - 8191). The result can be seen in the following encoding table:
*
* Biased Linear Input Code Compressed Code
* ------------------------ ---------------
* 00000001wxyza 000wxyz
* 0000001wxyzab 001wxyz
* 000001wxyzabc 010wxyz
* 00001wxyzabcd 011wxyz
* 0001wxyzabcde 100wxyz
* 001wxyzabcdef 101wxyz
* 01wxyzabcdefg 110wxyz
* 1wxyzabcdefgh 111wxyz
*
* Each biased linear code has a leading 1 which identifies the segment
* number. The value of the segment number is equal to 7 minus the number
* of leading 0's. The quantization interval is directly available as the
* four bits wxyz. * The trailing bits (a - h) are ignored.
*
* Ordinarily the complement of the resulting code word is used for
* transmission, and so the code word is complemented before it is returned.
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
static unsigned char linear2ulaw(int16_t pcm_val)
{
int16_t mask;
int seg;
int uval;
/* Get the sign and the magnitude of the value. */
pcm_val = pcm_val >> 2;
if (pcm_val < 0) {
pcm_val = -pcm_val;
mask = 0x7F;
} else {
mask = 0xFF;
}
if ( pcm_val > clip ) pcm_val = clip; /* clip the magnitude */
pcm_val += (bias >> 2);
/* Convert the scaled magnitude to segment number. */
seg = search(pcm_val, seg_uend, 8);
/*
* Combine the sign, segment, quantization bits;
* and complement the code word.
*/
if (seg >= 8) /* out of range, return maximum value. */
return (unsigned char)(0x7F ^ mask);
else {
uval = (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
return (unsigned char)(uval ^ mask);
}
}
/*
* ulaw2linear() - Convert a u-law value to 16-bit linear PCM
*
* First, a biased linear code is derived from the code word. An unbiased
* output can then be obtained by subtracting 33 from the biased code.
*
* Note that this function expects to be passed the complement of the
* original code word. This is in keeping with ISDN conventions.
*/
static int16_t ulaw2linear(unsigned char u_val)
{
int t;
/* Complement to obtain normal u-law value. */
u_val = ~u_val;
/*
* Extract and bias the quantization bits. Then
* shift up by the segment number and subtract out the bias.
*/
t = ((u_val & quant_mask) << 3) + bias;
t <<= (u_val & seg_mask) >> seg_shift;
return ((u_val & sign_bit) ? (bias - t) : (t - bias));
}

285
src/libsyn123/geiger.c Normal file
View File

@@ -0,0 +1,285 @@
/*
Geiger-Mueller counter simulator.
(c)2016-2018 Thomas Orgis <thomas@orgis.org>, licensed MIT-style.
Essentially: This is just a bit of code that you can use as you please.
See full license text at the bottom. As part of the mpg123 distribution,
the GNU LPGL version 2.1 can be used instead.
This generates random pulses with a configured likelihood (event
activity) that drive a simulated speaker to emit the characteristic
ticking sound (as opposed to a perfect digital click).
This is adapted from a stand-alone demonstration program to be
directly available to out123 and others via libsyn123.
*/
#include "config.h"
#define FILL_PERIOD
#define RAND_XORSHIFT32
#define NO_SMAX
#define NO_SMIN
#include "syn123_int.h"
static void white_generator(syn123_handle *sh, int samples)
{
for(int i=0; i<samples; ++i)
sh->workbuf[1][i] = 2*rand_xorshift32(&sh->seed);
}
int attribute_align_arg
syn123_setup_white(syn123_handle *sh, unsigned long seed, size_t *period)
{
if(!sh)
return SYN123_BAD_HANDLE;
syn123_setup_silence(sh);
sh->seed = seed;
sh->generator = white_generator;
int ret = fill_period(sh);
sh->seed = seed;
if(ret != SYN123_OK)
syn123_setup_silence(sh);
if(period)
*period = sh->samples;
return ret;
}
/*
Brain dump for the speaker model:
Simulating a simple speaker as dampened oscillator.
This should result in somewhat interesting impulse response.
I do not bother with a lower frequency bound (people may wish to
apply a highpass filter to simulate a small tinny speaker).
F = - k x
There is a mass attached, carrying the momentum. A new Geiger
impulse just adds a big load of momentum and the oscillator
works with that. A simple Euler scheme should be enough here.
dx = v dt
dv = F/m dt
We need some friction here. Let's use simple quadratic (air)
friction with coefficient r, (r v^2) being a force opposing the
current movement.
dv = - (k x + sign(v) r v^2) / m dt
About the impulse from the counter ... it needs to be incorporated
as an additional force G that is applied over a certain time period.
I need an impulse shape. How does the cascade look again?
Does a gauss curve do the trick? Or a jump with linear decay? Or simply
a box impulse? I'll try with a box impulse, relating to a certain
energy of a gas discharge. then, there's a dead time interval where
new particles are not detected at all. After that, a time period where
new discharges are weaker (linear ramp as simplest model).
Let's use that model and solve it in the simplest manner:
dx = v dt
dv = (G(t) - k x(t) - sign(v(t)) r v(t)^2) / m dt
Some fixed friction goes on top to bring the speaker really down to
zero.
*/
struct geigerspace
{
// Time is counted in sampling intervals of this size.
// (inverse of sampling rate).
double time_interval;
// Strength of the last event, compared to full scale.
// This is for capturing an event detected in the
// recovery period.
double event_strength;
// Age of last event in intervals. Based on this, the discharge
// force is applied or not, strength of a new discharge computed
// (if event_age > dead time).
// Scaling of discharge force, in relation to speaker movement.
double force_scale;
// Intervals since last event, if it is recent.
long event_age;
// Number of intervals for that a new event is not detected.
// This is also the duration of the discharge.
double dead_s;
long dead_time;
// Number of intervals till recovery after dead time.
long recover_time;
// Threshold for random value to jump over.
float thres;
// Oscillator properties.
double mass; // mass (inertia) of speaker in kg
double spring; // spring constant k in N/m
double friction; // friction coefficient in kg/(s m)
double friction_fixed; // Speed-independent friction.
// State variables.
double pos; // position of speaker [-1:1]
double speed; // speed of speaker movement
};
static double sign(double val)
{
return val < 0 ? -1. : +1;
}
// Advance speaker position by one time interval, applying the given force.
static double speaker(struct geigerspace *gs, double force)
{
double dx, dv;
// Some maximal numeric time step to work nicely for small sampling
// rates. Unstable numerics make a sound, too.
double euler_step = 1e-5;
long steps = 0;
do
{
double step = gs->time_interval-steps*euler_step;
if(step > euler_step)
step = euler_step;
// dx = v dt
// dv = (G(t) - k x(t) - sign(v(t)) r v(t)^2) / m dt
dx = gs->speed*step;
dv = ( force - gs->spring*gs->pos
- sign(gs->speed)*gs->friction*gs->speed*gs->speed )
/ gs->mass * step;
gs->pos += dx;
gs->speed += dv;
// Apply some fixed friction to get down to zero.
if(gs->speed)
{
double ff = -sign(gs->speed)*gs->friction_fixed/gs->mass*step;
if(sign(gs->speed+ff) == sign(gs->speed))
gs->speed += ff;
else
gs->speed /= 2;
}
} while(++steps*euler_step < gs->time_interval);
return gs->pos;
}
// The logic of the Geiger-Mueller counter.
// Given an event (or absence of) for this time interval, return
// the discharge force to apply to the speaker.
static double discharge_force(struct geigerspace *gs, int event)
{
double strength = 0.;
if(gs->event_age >= 0) // If there was a recent event.
{
// Apply current event force until dead time is reached.
if(++gs->event_age > gs->dead_time)
{
long newtime = gs->event_age - gs->dead_time;
if(newtime < gs->recover_time)
{
gs->event_strength = 1.*newtime/gs->recover_time;
}
else // Possible event after full recovery.
{
gs->event_age = -1;
gs->event_strength = 1.;
}
}
else // Still serving the old event.
{
strength = gs->event_strength;
event = 0;
}
}
if(event)
{
gs->event_age = 0;
strength = gs->event_strength;
}
return strength*gs->force_scale;
}
static void geiger_init(struct geigerspace *gs, double activity, long rate)
{
if(activity < 0)
activity = 0.;
gs->time_interval = 1./rate;
gs->event_strength = 1.;
gs->event_age = -1;
// In the order of 100 us. Chose a large time to produce sensible results down
// to 8000 Hz sampling rate.
gs->dead_s = 0.0002;
gs->dead_time = (long)(gs->dead_s*rate+0.5);
gs->recover_time = 2*gs->dead_time;
// Let's artitrarily define maximum speaker displacement
// as 1 mm and specify values accordingly.
gs->pos = 0.;
gs->speed = 0.;
// Mass and spring control the self-oscillation frequency.
gs->mass = 0.02;
gs->spring = 1000000;
// Some dynamic friction is necessary to keep the unstable numeric
// solution in check. A kind of lowpass filter, too, of course.
gs->friction = 0.02;
// Some hefty fixed friction prevents self-oscillation of speaker
// (background sine wave). But you want _some_ of it.
gs->friction_fixed = 20000;
// Experimenting, actually. Some relation to the speaker.
gs->force_scale = 50000.*gs->mass*0.001/(4.*gs->dead_s*gs->dead_s);
float event_likelihood = activity*gs->time_interval;
if(event_likelihood > 1.)
event_likelihood = 1.;
gs->thres = 1.-event_likelihood;
}
static void geiger_generator(syn123_handle *sh, int samples)
{
struct geigerspace *gs = sh->handle;
for(int i=0; i<samples; ++i)
sh->workbuf[1][i] = speaker( gs
, discharge_force(gs, (rand_xorshift32(&sh->seed)+0.5)>gs->thres) );
// Soft clipping as speaker property. It can only move so far.
// Of course this could be produced by a nicely nonlinear force, too.
syn123_soft_clip( sh->workbuf[1], MPG123_ENC_FLOAT_64, samples
, 1., 0.1, NULL );
}
int attribute_align_arg
syn123_setup_geiger( syn123_handle *sh, double activity, unsigned long seed
, size_t *period )
{
int ret = SYN123_OK;
if(!sh)
return SYN123_BAD_HANDLE;
syn123_setup_silence(sh);
struct geigerspace *handle = malloc(sizeof(*handle));
if(!handle)
return SYN123_DOOM;
sh->seed = seed;
geiger_init(handle, activity, sh->fmt.rate);
sh->handle = handle;
sh->generator = geiger_generator;
// Fill period buffer, re-init generator for cleanliness.
ret = fill_period(sh);
if(ret)
goto setup_geiger_end;
if(sh->samples)
{
sh->seed = seed;
geiger_init(handle, activity, sh->fmt.rate);
}
setup_geiger_end:
if(ret != SYN123_OK)
syn123_setup_silence(sh);
if(period)
*period = sh->samples;
return ret;
}
// Full license text:
// Copyright (c) 2016-2018 Thomas Orgis
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

917
src/libsyn123/libsyn123.c Normal file
View File

@@ -0,0 +1,917 @@
/*
libsyn123: libsyn123 entry code and wave generators
copyright 2017-2018 by the mpg123 project
licensed under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
This code directly contains wave generators and the generic entry
code for libsyn123. The waves started the whole thing and stay here
for now. Other signal generators go into separate files.
This code is testament to the ultimate programmer hybris: Just add
something because it looks easy, a fun dirty hack. Then spend days
doing it properly, including fun like squeezing multiple waves of
differing shapes into periodic packets. Oh, and caring about
vectorization for the heck of it. Offering sample formats down to
8 bit alaw. Madness.
*/
#include "syn123_int.h"
#include "sample.h"
#include "debug.h"
static const double freq_error = 1e-4;
/* For our precisions, that value will always be good enough. */
static const double twopi = 2.0*3.14159265358979323846;
static struct syn123_wave defwave = { SYN123_WAVE_SINE, FALSE, 440., 0. };
/* round floating point to size_t */
static size_t round2size(double a)
{
return a < 0 ? 0 : (size_t)(a+0.5);
}
/* fractional part, relating to frequencies (so long matches) */
static double myfrac(double a)
{
return a-(long)a;
}
/* arbitrary phase -> [0..1) */
static double phasefrac(double p)
{
return p-floor(p);
}
/*
Given a set of wave frequencies, compute an approximate common
period for the combined signal. Invalid frequencies are set to
the error bound for some sanity.
TODO: sort waves and begin with highest freq for stable/best result
*/
static double common_samples_per_period( long rate, size_t count
, struct syn123_wave *waves, size_t size_limit )
{
double spp = 0;
size_t i;
for(i=0; i<count; ++i)
{
double sppi;
size_t periods = 1;
/* Limiting sensible frequency range. */
if(waves[i].freq < freq_error)
waves[i].freq = freq_error;
if(waves[i].freq > rate/2)
waves[i].freq = rate/2;
sppi = fabs((double)rate/waves[i].freq);
debug2("freq=%g sppi=%g", waves[i].freq, sppi);
if(spp == 0)
spp = sppi;
while
(
(periods+1)*spp <= size_limit &&
fabs( myfrac(periods*spp / sppi) ) > freq_error
)
periods++;
spp*=periods;
debug3( "samples_per_period + %f Hz = %g (%" SIZE_P " periods)"
, waves[i].freq, spp, periods );
}
return spp;
}
/* Compute a good size of a table covering the common period for all waves. */
static size_t tablesize( long rate, size_t count
, struct syn123_wave *waves, size_t size_limit )
{
size_t ts, nts;
double fts, tolerance;
double samples_per_period;
size_t periods;
samples_per_period = common_samples_per_period( rate, count
, waves, size_limit );
tolerance = freq_error*samples_per_period;
periods = 0;
do
{
periods++;
fts = periods*samples_per_period;
ts = round2size(fts);
nts = round2size((periods+1)*samples_per_period);
}
while(fabs(fts-ts) > periods*tolerance && nts <= size_limit);
/* Ensure size limit. Even it is ridiculous. */
ts = smin(ts, size_limit);
debug1("table size: %" SIZE_P, ts);
return ts;
}
/* The wave functions. Argument is the phase normalised to the period. */
/* The argument is guaranteed to be 0 <= p < 1. */
/* _________ */
/* */
static double wave_none(double p)
{
return 0;
}
/* __ */
/* / \ */
/* \__/ */
static double wave_sine(double p)
{
return sin(twopi*p);
}
/* ___ */
/* ___| */
static double wave_square(double p)
{
return (p < 0.5 ? -1 : 1);
}
/* 1234 Avoid jump from zero at beginning. */
/* /\ */
/* \/ */
static double wave_triangle(double p)
{
return 4*p < 1
? 4*p /* 1 */
: ( 4*p < 3
? 2.-4*p /* 2 and 3 */
: -4+4*p /* 4 */
);
}
/* /| Avoid jump from zero ... */
/* |/ */
static double wave_sawtooth(double p)
{
return 2*p < 1 ? 2*p : -2+2*p;
}
/* _ */
/* __/ \__ */
/* */
static double wave_gauss(double p)
{
double v = p-0.5;
return exp(-30*v*v);
}
/* _ */
/* _/ -___ */
/* */
/* p**2*exp(-a*p**2) */
/* Scaling: maximum at sqrt(1/a), value 1/a*exp(-1). */
static double wave_pulse(double p)
{
return p*p*exp(-50*p*p)/0.00735758882342885;
}
/* _ */
/* / -___ */
/* */
/* p**2*exp(-a*p) */
/* Scaling: maximum at 4/a, value 4/a**2*exp(-2). */
static double wave_shot(double p)
{
return p*p*exp(-100*p)/5.41341132946451e-05;
}
// Actual wave worker function, to be used to give up to
// bufblock samples in one go. This takes a vector of phases
// and multiplies the resulting amplitudes into the output buffer.
static void evaluate_wave( double outbuf[bufblock], int samples
, enum syn123_wave_id id, double phase[bufblock] )
{
// Ensuring that the inner loop is inside the switch.
// Compilers might be smart enough, but it is not hard
// to write it down the right way from the beginning.
#define PHASE phase[pi]
#define PI_LOOP( code ) \
for(int pi=0; pi<samples; ++pi) \
outbuf[pi] *= code;
switch(id)
{
case SYN123_WAVE_FLAT:
PI_LOOP( wave_none(PHASE) )
break;
case SYN123_WAVE_SINE:
PI_LOOP( wave_sine(PHASE) )
break;
case SYN123_WAVE_SQUARE:
PI_LOOP( wave_square(PHASE) )
break;
case SYN123_WAVE_TRIANGLE:
PI_LOOP( wave_triangle(PHASE) )
break;
case SYN123_WAVE_SAWTOOTH:
PI_LOOP( wave_sawtooth(PHASE) )
break;
case SYN123_WAVE_GAUSS:
PI_LOOP( wave_gauss(PHASE) )
break;
case SYN123_WAVE_PULSE:
PI_LOOP( wave_pulse(PHASE) )
break;
case SYN123_WAVE_SHOT:
PI_LOOP( wave_shot(PHASE) )
break;
default:
PI_LOOP( wave_none(PHASE) )
}
#undef PI_LOOP
#undef PHASE
}
static const char* wave_names[] =
{
"flat", "sine", "square", "triangle"
, "sawtooth", "gauss", "pulse", "shot"
};
const char* attribute_align_arg
syn123_wave_name(int id)
{
if(id < 0 || id >= sizeof(wave_names)/sizeof(char*))
return "???";
else
return wave_names[id];
}
int attribute_align_arg
syn123_wave_id(const char *name)
{
if(name)
for(int i=0; i<sizeof(wave_names)/sizeof(char*); ++i)
if(!strcmp(name, wave_names[i]))
return i;
return SYN123_WAVE_INVALID;
}
const char* syn123_strerror(int errcode)
{
switch(errcode)
{
case SYN123_OK:
return "no error";
case SYN123_BAD_HANDLE:
return "bad handle";
case SYN123_BAD_FMT:
return "bad format";
case SYN123_BAD_ENC:
return "bad encoding";
case SYN123_BAD_CONV:
return "unsupported conversion";
case SYN123_BAD_SIZE:
return "wrong buffer size";
case SYN123_BAD_BUF:
return "bad buffer pointer";
case SYN123_BAD_CHOP:
return "byte count not matching sample boundaries";
case SYN123_DOOM:
return "out of memory";
case SYN123_WEIRD:
return "Call the Ghostbusters!";
case SYN123_BAD_FREQ:
return "Bad signal frequency given.";
case SYN123_BAD_SWEEP:
return "Invalid sweep curve given.";
case SYN123_OVERFLOW:
return "An integer overflow occured.";
case SYN123_NO_DATA:
return "Not enough data.";
case SYN123_BAD_DATA:
return "Bad data given.";
default:
return "unkown error";
}
}
// I first introduced the vectorization-friendly aproach with a precomputed
// buffer of phases as a (premature) optimization, but it turns out that
// this separation is really useful for computing frequency sweeps.
// The computation of phases, including varying frequency, really is a separate
// task from actually evaluating the wave functions.
// The code is actually smaller and better abstracted that way.
static void add_some_wave( double outbuf[bufblock], int samples
, enum syn123_wave_id id, double pps, double phase
, double workbuf[bufblock] )
{
for(int pi=0; pi<samples; ++pi)
workbuf[pi] = phasefrac(pi*pps+phase);
evaluate_wave(outbuf, samples, id, workbuf);
}
// Fit waves into given table size.
static void wave_fit_table( size_t samples
, long rate, struct syn123_wave *wave )
{
double pps = wave->freq/rate;
debug3("wave_fit_table %" SIZE_P " %ld %g", samples, rate, wave->freq);
size_t periods = smax(round2size(pps*samples), 1);
pps = (double)periods/samples;
wave->freq = pps*rate;
debug4( "final wave: %c %i @ %g Hz + %g"
, wave->backwards ? '<' : '>', wave->id, wave->freq, wave->phase );
}
// Evaluate an additional wave into the given buffer (multiplying
// with existing values in output.
static void wave_add_buffer( double outbuf[bufblock], size_t samples
, long rate, struct syn123_wave *wave, double workbuf[bufblock] )
{
double pps = wave->freq/rate;
debug3("wave_add_buffer %" SIZE_P " %ld %g", samples, rate, wave->freq);
debug4( "adding wave: %c %i @ %g Hz + %g"
, wave->backwards ? '<' : '>', wave->id, wave->freq, wave->phase );
if(wave->backwards)
pps = -pps;
add_some_wave( outbuf, samples
, wave->id, pps, wave->phase, workbuf );
// Advance the wave.
wave->phase = phasefrac(wave->phase+samples*pps);
}
// The most basic generator of all.
static void silence_generator(syn123_handle *sh, int samples)
{
for(int i=0; i<samples; ++i)
sh->workbuf[1][i] = 0;
}
// Clear the handle of generator data structures.
// Well, except the one generating silence.
int attribute_align_arg
syn123_setup_silence(syn123_handle *sh)
{
if(!sh)
return SYN123_BAD_HANDLE;
sh->generator = silence_generator;
if(sh->wave_count && sh->waves)
free(sh->waves);
sh->waves = NULL;
sh->wave_count = 0;
if(sh->handle)
free(sh->handle);
sh->handle = NULL;
sh->samples = 0;
sh->offset = 0;
return SYN123_OK;
}
// The generator function is called upon to fill sh->workbuf[1] with the
// given number of samples in double precision. It may use sh->workbuf[0]
// for its own purposes.
static void wave_generator(syn123_handle *sh, int samples)
{
/* Initialise to zero amplitude. */
for(int i=0; i<samples; ++i)
sh->workbuf[1][i] = 1;
/* Add individual waves. */
for(int c=0; c<sh->wave_count; ++c)
wave_add_buffer( sh->workbuf[1], samples, sh->fmt.rate, sh->waves+c
, sh->workbuf[0] );
}
/* Build internal table, allocate external table, convert to that one, */
/* adjusting sample storage format and channel count. */
int attribute_align_arg
syn123_setup_waves( syn123_handle *sh, size_t count
, int *id, double *freq, double *phase, int *backwards
, size_t *common_period )
{
int ret = SYN123_OK;
if(!sh)
return SYN123_BAD_HANDLE;
syn123_setup_silence(sh);
if(!count)
{
count = 1;
id = NULL;
freq = NULL;
phase = NULL;
backwards = NULL;
}
sh->waves = malloc(sizeof(struct syn123_wave)*count);
if(!sh->waves)
return SYN123_DOOM;
for(size_t c=0; c<count; ++c)
{
sh->waves[c].id = id ? id[c] : defwave.id;
sh->waves[c].backwards = backwards ? backwards[c] : defwave.backwards;
sh->waves[c].freq = freq ? freq[c] : defwave.freq;
sh->waves[c].phase = phase ? phase[c] : defwave.phase;
}
sh->wave_count = count;
sh->generator = wave_generator;
if(sh->maxbuf)
{
// 1. Determine buffer size to use.
size_t samplesize = MPG123_SAMPLESIZE(sh->fmt.encoding);
size_t size_limit = sh->maxbuf / samplesize;
size_t buffer_samples = tablesize(sh->fmt.rate, count, sh->waves
, size_limit);
// 2. Actually allocate the buffer.
grow_buf(sh, buffer_samples*samplesize);
if(sh->bufs/samplesize < buffer_samples)
{
ret = SYN123_DOOM;
goto setup_wave_end;
}
// 2. Adjust the waves to fit into the buffer.
for(size_t c=0; c<count; ++c)
{
wave_fit_table(buffer_samples, sh->fmt.rate, sh->waves+c);
if(freq)
freq[c] = sh->waves[c].freq;
}
// 3. fill the buffer using workbuf as intermediate. As long as
// the buffer is not ready, we can just use syn123_read() to fill it.
// Just need to ensure mono storage. Once sh->samples is set, the
// buffer is used.
int outchannels = sh->fmt.channels;
sh->fmt.channels = 1;
size_t buffer_bytes = syn123_read(sh, sh->buf, buffer_samples*samplesize);
sh->fmt.channels = outchannels;
// 4. Restore wave phases to the beginning, for tidyness.
for(size_t c=0; c<count; ++c)
sh->waves[c].phase = phase ? phase[c] : defwave.phase;
// 5. Last check for sanity.
if(buffer_bytes != buffer_samples*samplesize)
{
ret = SYN123_WEIRD;
goto setup_wave_end;
}
sh->samples = buffer_samples;
}
setup_wave_end:
if(ret != SYN123_OK)
syn123_setup_silence(sh);
else
{
if(common_period)
*common_period = sh->samples;
}
return ret;
}
int attribute_align_arg
syn123_query_waves( syn123_handle *sh, size_t *count
, int *id, double *freq, double *phase, int *backwards
, size_t *common_period )
{
if(!sh)
return SYN123_BAD_HANDLE;
if(count)
*count = sh->wave_count;
if((id || freq || phase || backwards || common_period) && !sh->waves)
return SYN123_NO_DATA;
for(size_t c=0; c<sh->wave_count; ++c)
{
if(id)
id[c] = sh->waves[c].id;
if(backwards)
backwards[c] = sh->waves[c].backwards;
if(freq)
freq[c] = sh->waves[c].freq;
if(phase)
phase[c] = sh->waves[c].phase;
}
if(common_period)
*common_period = sh->samples;
return SYN123_OK;
}
// Given time normalized to the sweep duration, return the
// current frequency.
// With the power of analytical integration, we can compute
// the proper phase instead of error-prone summing.
// Given the normalized time: t = i/d
// Wallclock time: w = i/r (100 samples with rate 100 Hz = 1 s time)
// Ratio: w = i/r = t*d/r
// dw = d/r dt
// dp = f(t) * dw = d/r f(t) dt
// p = (d-1)/r int_0^t f(t) dt = d/r (F(t) - F(0))
// linear: f(t) = f1 + t*(f2-f1); F = f1*t + t**2 * (f2-f1)/2
// quad: f(t) = f1 + t**2*(f2-f1); F = f1*t + t**3 * (f2-f1)/3
// exp: f(t) = exp(log(f1) + t*(log(f2)-log(f1)))
// F(t) = 1/(log(f2)-log(f1)) exp(log(f1) + t*(log(f2)-log(f1)))
// These are the unscaled expressions, just F(t)-F(0).
static double sweep_phase_lin(double t, double f1, double f2)
{
return f1*t + t*t*0.5*(f2-f1);
}
static double sweep_phase_quad(double t, double f1, double f2)
{
return f1*t+t*t*t*(1./3)*(f2-f1);
}
// Be sure to only call if f2-f1 has some minimal value.
static double sweep_phase_exp(double t, double logf1, double logf2)
{
return 1./(logf2-logf1) * (exp(logf1 + t*(logf2-logf1)) - exp(logf1));
}
// Return phases after given offset of samples from now, including
// phase recovery.
// This is the central logic for periodic sweeps.
static void sweep_phase( syn123_handle *sh, size_t off
, double buf[bufblock], int count )
{
struct syn123_sweep *sw = sh->handle;
// Working on a local offset, not touching sw->i.
mdebug("computing position: (%zu + %zu) %% (%zu + %zu)", sw->i, off, sw->d, sw->post);
size_t pos = (sw->i + off) % (sw->d + sw->post);
int boff = 0;
while(count)
{
// Finish the current sweep+post cycle.
// 0 <= sw->i < sw->d+sw->post
// 1. Fill phases for actual sweep, if some of it is left.
// This includes the endpoint, one sample after configured length,
// if so many samples are desired. Note that pos == sw->d will not
// happen unless sw->post > 0.
int sweep_s = pos <= sw->d
? (int)smin(sw->d+1 - pos, count)
: 0;
// normalized time here to reduce code redundancy
for(int i=0; i<sweep_s; ++i)
buf[boff+i] = (double)pos++/sw->d;
// actual phase computation, with inner loops for auto-vectorization
switch(sw->id)
{
case SYN123_SWEEP_LIN:
for(int i=0; i<sweep_s; ++i)
buf[boff+i] = sweep_phase_lin(buf[boff+i], sw->f1, sw->f2);
break;
case SYN123_SWEEP_QUAD:
for(int i=0; i<sweep_s; ++i)
buf[boff+i] = sweep_phase_quad(buf[boff+i], sw->f1, sw->f2);
break;
case SYN123_SWEEP_EXP:
for(int i=0; i<sweep_s; ++i)
buf[boff+i] = sweep_phase_exp(buf[boff+i], sw->f1, sw->f2);
break;
default:
for(int i=0; i<sweep_s; ++i)
buf[boff+i] = 0;
}
// scaling from normalized time to sampled proper time
for(int i=0; i<sweep_s; ++i)
buf[boff++] *= (double)sw->d/sh->fmt.rate;
count -= sweep_s;
// 2. Fill phases for post sweep phase, if reached.
if(pos > sw->d)
{
int post_s = (int)smin(sw->d + sw->post - pos, count);
double pps = sw->wave.freq/sh->fmt.rate;
for(int i=0; i<post_s; ++i)
buf[boff++] = sw->endphase - sw->wave.phase + (pos++ - sw->d)*pps;
count -= post_s;
}
// Now we can possibly wrap around. Actually, we have to
// hit the boundary exactly.
if(pos == sw->d + sw->post)
pos = 0;
}
// Turn all phases into fractions and invert if going backwards.
if(sw->wave.backwards)
for(int i=0; i<boff; ++i)
buf[i] = phasefrac(-buf[i]-sw->wave.phase);
else
for(int i=0; i<boff; ++i)
buf[i] = phasefrac(buf[i]+sw->wave.phase);
}
static void sweep_generator(syn123_handle *sh, int samples)
{
struct syn123_sweep *sw = sh->handle;
// Precompute phases into work buffer.
sweep_phase(sh, 0, sh->workbuf[0], samples);
// Initialise output to zero amplitude and multiply by the wave.
for(int i=0; i<samples; ++i)
sh->workbuf[1][i] = 1.;
evaluate_wave(sh->workbuf[1], samples, sw->wave.id, sh->workbuf[0]);
// Advance.
sw->i = (sw->i+samples) % (sw->d + sw->post);
}
int attribute_align_arg
syn123_setup_sweep( syn123_handle* sh
, int wave_id, double phase, int backwards
, int sweep_id, double *f1, double *f2, int smooth, size_t duration
, double *endphase, size_t *period, size_t *buffer_period )
{
double real_f1;
int ret = SYN123_OK;
struct syn123_sweep *sw;
if(!sh)
return SYN123_BAD_HANDLE;
syn123_setup_silence(sh);
if(!duration) // Empty sweep is silence.
{
if(endphase)
*endphase = phase;
if(period)
*period = 0;
return SYN123_OK;
}
if( sweep_id != SYN123_SWEEP_LIN && sweep_id != SYN123_SWEEP_QUAD &&
sweep_id != SYN123_SWEEP_EXP )
return SYN123_BAD_SWEEP;
sh->handle = sw = malloc(sizeof(struct syn123_sweep));
if(!sh->handle)
{
ret = SYN123_DOOM;
goto setup_sweep_end;
}
sw->f1 = (!f1 || *f1 <= 0.) ? defwave.freq : *f1;
real_f1 = sw->f1;
sw->f2 = (!f2 || *f2 <= 0.) ? defwave.freq : *f2;
if( sw->f1 < freq_error || sw->f2 < freq_error )
{
ret = SYN123_BAD_FREQ;
goto setup_sweep_end;
}
// Also, don't try exp sweep for very small difference.
// We need 1/log(f2/f1) ...
if(sweep_id == SYN123_SWEEP_EXP && (fabs(sw->f2 - sw->f1) < freq_error))
{
ret = SYN123_BAD_FREQ;
goto setup_sweep_end;
}
sw->wave.id = wave_id;
sw->wave.backwards = 0; // Set to true value later.
sw->wave.freq = sw->f2; // We'll use that later for continuation.
sw->wave.phase = phase; // Beginning phase offset, not updated.
sw->id = sweep_id;
// Store the logarithms for exponential sweep.
if(sweep_id == SYN123_SWEEP_EXP)
{
sw->f1 = log(sw->f1);
sw->f2 = log(sw->f2);
}
sw->i = 0;
sw->d = duration;
sw->post = 1; // Needed to be set for end phases.
if(sw->d + sw->post < sw->d || sw->d + sw->post < sw->post)
{
ret = SYN123_OVERFLOW;
goto setup_sweep_end;
}
// The last phase served by the sweep, and the one after that that
// tryly concludes the sweep (f==f2, not infinitesimally smaller).
mdebug("computing endphase, with 2 points from %zu - 1 on", duration);
sweep_phase(sh, duration-1, sh->workbuf[0], 2);
double before_endphase = sh->workbuf[0][0];
sw->endphase = sh->workbuf[0][1];
sw->post = 0; // Reset that again, only increasing if really needed.
// The phase that would smoothly continue the sweep, one sample
// after the last one.
// We want confirmed zero crossing between before_endphase and
// endphase. This only matters for resolved frequencies, though.
if(smooth && 2*sw->f2 < sh->fmt.rate &&
phasefrac(sw->endphase-phase) > phasefrac(before_endphase-phase) )
{
// Start from before_endphase on, to reach the actual point where
// the signal truly reaches f2. Anything on top is with constant
// f2.
double poststeps = phasefrac(1.-before_endphase+phase)
* ((double)sh->fmt.rate/sw->wave.freq);
// We want the integer part, except in the unlikely
// case that it is an exact integer: Then we want one less. The
// actual zero crossing is _after_ the post samples.
size_t prepos = (size_t)poststeps;
if(prepos && prepos == poststeps)
prepos--;
sw->post = prepos;
}
size_t fulldur = sw->d + sw->post;
if(fulldur < sw->d || fulldur < sw->post)
{
ret = SYN123_OVERFLOW;
goto setup_sweep_end;
}
// Now we can go backwards. Otherwise the above computation would
// be more confusing.
sw->wave.backwards = backwards;
sh->generator = sweep_generator;
size_t samplesize = MPG123_SAMPLESIZE(sh->fmt.encoding);
if(sh->maxbuf && sh->maxbuf >= samplesize*fulldur)
{
grow_buf(sh, samplesize*fulldur);
if(sh->bufs/samplesize < fulldur)
{
ret = SYN123_DOOM; // Actually, also overflow.
goto setup_sweep_end;
}
// Fill the buffer using workbuf as intermediate. As long as
// the buffer is not ready, we can just use syn123_read() to fill it.
// Just need to ensure mono storage. Once sh->samples is set, the
// buffer is used.
int outchannels = sh->fmt.channels;
sh->fmt.channels = 1;
size_t buffer_bytes = syn123_read(sh, sh->buf, fulldur*samplesize);
sh->fmt.channels = outchannels;
// Restore sweep state.
sw->i = 0;
// Last check for sanity.
if(buffer_bytes != fulldur*samplesize)
{
ret = SYN123_WEIRD;
goto setup_sweep_end;
}
sh->samples = fulldur;
}
setup_sweep_end:
if(ret != SYN123_OK)
syn123_setup_silence(sh);
else
{
mdebug("done with sweep setup %g %g", real_f1, sw->wave.freq);
if(period)
*period = fulldur;
if(endphase)
*endphase = sw->endphase;
if(f1)
*f1 = real_f1;
if(f2)
*f2 = sw->wave.freq;
if(buffer_period)
*buffer_period = sh->samples;
}
return ret;
}
syn123_handle* attribute_align_arg
syn123_new(long rate, int channels, int encoding
, size_t maxbuf, int *err)
{
int myerr = SYN123_OK;
syn123_handle *sh = NULL;
size_t sbytes = MPG123_SAMPLESIZE(encoding);
if(!sbytes)
{
myerr = SYN123_BAD_ENC;
goto syn123_new_end;
}
if(rate < 1 || channels < 1)
{
myerr = SYN123_BAD_FMT;
goto syn123_new_end;
}
sh = malloc(sizeof(syn123_handle));
if(!sh){ myerr = SYN123_DOOM; goto syn123_new_end; }
sh->fmt.rate = rate;
sh->fmt.channels = channels;
sh->fmt.encoding = encoding;
sh->buf = NULL;
sh->bufs = 0;
sh->maxbuf = maxbuf;
sh->samples = 0;
sh->offset = 0;
sh->wave_count = 0;
sh->waves = NULL;
sh->handle = NULL;
syn123_setup_silence(sh);
sh->rd = NULL;
sh->dither = 0;
sh->do_dither = 0;
sh->dither_seed = 0;
sh->fc.count = sh->fc.maxcount = 0;
sh->fc.df = NULL;
sh->fc.ff = NULL;
syn123_new_end:
if(err)
*err = myerr;
if(myerr)
{
syn123_del(sh);
sh = NULL;
}
return sh;
}
void attribute_align_arg
syn123_del(syn123_handle* sh)
{
if(!sh)
return;
syn123_setup_silence(sh);
syn123_setup_resample(sh, 0, 0, 0, 0, 0);
syn123_drop_filter(sh, sh->fc.count);
if(sh->fc.ff)
free(sh->fc.ff);
if(sh->fc.df)
free(sh->fc.df);
if(sh->buf)
free(sh->buf);
free(sh);
}
// Copy from period buffer or generate on the fly.
size_t attribute_align_arg
syn123_read( syn123_handle *sh, void *dest, size_t dest_bytes )
{
char *cdest = dest; /* Want to do arithmetic. */
size_t samplesize, framesize;
size_t dest_samples;
size_t extracted = 0;
if(!sh)
return 0;
samplesize = MPG123_SAMPLESIZE(sh->fmt.encoding);
framesize = samplesize*sh->fmt.channels;
dest_samples = dest_bytes/framesize;
if(sh->samples) // Got buffered samples to work with.
{
while(dest_samples)
{
size_t block = smin(dest_samples, sh->samples - sh->offset);
debug3( "offset: %"SIZE_P" block: %" SIZE_P" out of %"SIZE_P
, sh->offset, block, sh->samples );
syn123_mono2many(cdest, (char*)sh->buf+sh->offset*samplesize
, sh->fmt.channels, samplesize, block );
cdest += framesize*block;
sh->offset += block;
sh->offset %= sh->samples;
dest_samples -= block;
extracted += block;
}
}
else // Compute directly, employing the work buffers.
{
while(dest_samples)
{
int block = (int)smin(dest_samples, bufblock);
debug2( "out offset: %ld block: %i"
, (long)(cdest-(char*)dest)/framesize, block );
// Compute data into workbuf[1], possibly using workbuf[0]
// in the process.
// TODO for the future: Compute only in single precision if
// it is enough.
sh->generator(sh, block);
// Convert to external format, mono. We are abusing workbuf[0] here,
// because it is big enough.
// The converter does not use workbuf if converting from float. Dither is
// added on the fly.
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);
break;
}
syn123_mono2many( cdest, sh->workbuf[0]
, sh->fmt.channels, samplesize, block );
cdest += framesize*block;
dest_samples -= block;
extracted += block;
}
}
debug1("extracted: %" SIZE_P, extracted);
return extracted*framesize;
}
int attribute_align_arg
syn123_dither(syn123_handle *sh, int dither, unsigned long *seed)
{
if(!sh)
return SYN123_BAD_HANDLE;
// So far we only know 1 or 0 as choices.
sh->dither = dither ? 1 : 0;
sh->dither_seed = (seed && *seed) ? *seed : 2463534242UL;
if(seed)
*seed = sh->dither_seed;
return SYN123_OK;
}

180
src/libsyn123/pinknoise.c Normal file
View File

@@ -0,0 +1,180 @@
/*
pinknoise: pink noise generator for libsyn123
based on:
patest_pink.c
Generate Pink Noise using Gardner method.
Optimization suggested by James McCartney uses a tree
to select which random value to replace.
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
Tree is generated by counting trailing zeros in an increasing index.
When the index is zero, no random number is selected.
This program uses the Portable Audio library which is under development.
For more information see: http://www.audiomulch.com/portaudio/
Author: Phil Burk, http://www.softsynth.com
Revision History:
Copyleft 1999 Phil Burk - No rights reserved.
*/
#define NO_SMAX
#define NO_SMIN
#include "syn123_int.h"
#include <math.h>
// Calculate pseudo-random 32 bit number based on linear congruential method.
// ... or via xor after all? That looks a lot better when not
// faking out with 64 bit long.
static int32_t GenerateRandomNumber(uint32_t *seed)
{
#if 0
*seed = *seed * 196314165 + 907633515;
#else
*seed ^= (*seed<<13);
*seed ^= (*seed>>17);
*seed ^= (*seed<<5);
#endif
return (int32_t)*seed; // Implementaton-defined ...
}
#define PINK_MAX_RANDOM_ROWS (30)
#define PINK_RANDOM_BITS (24)
#define PINK_RANDOM_SHIFT ((sizeof(int32_t)*8)-PINK_RANDOM_BITS-1)
typedef struct
{
int32_t pink_Rows[PINK_MAX_RANDOM_ROWS];
int32_t pink_RunningSum; /* Used to optimize summing of generators. */
int pink_Index; /* Incremented each sample. */
int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
uint32_t rand_value; /* The state of the random number generator. */
} PinkNoise;
/* Setup PinkNoise structure for N rows of generators. */
static void InitializePinkNoise( PinkNoise *pink, int numRows )
{
int i;
int32_t pmax;
pink->pink_Index = 0;
pink->pink_IndexMask = (1<<numRows) - 1;
/* Calculate maximum possible random value. Extra 1 for white noise always added. */
pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
pink->pink_Scalar = 1.0f / pmax;
/* Initialize rows. */
for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
pink->pink_RunningSum = 0;
}
/* Generate Pink noise values between -1.0 and +1.0 */
static float GeneratePinkNoise( PinkNoise *pink )
{
int32_t newRandom;
int32_t sum;
float output;
/* Increment and mask index. */
pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
/* If index is zero, don't update any random values. */
if( pink->pink_Index != 0 )
{
/* Determine how many trailing zeros in PinkIndex. */
/* This algorithm will hang if n==0 so test first. */
int numZeros = 0;
int n = pink->pink_Index;
while( (n & 1) == 0 )
{
n = n >> 1;
numZeros++;
}
/* Replace the indexed ROWS random value.
* Subtract and add back to RunningSum instead of adding all the random
* values together. Only one changes each time.
*/
pink->pink_RunningSum -= pink->pink_Rows[numZeros];
newRandom = GenerateRandomNumber(&pink->rand_value) >> PINK_RANDOM_SHIFT;
pink->pink_RunningSum += newRandom;
pink->pink_Rows[numZeros] = newRandom;
}
/* Add extra white noise value. */
newRandom = GenerateRandomNumber(&pink->rand_value) >> PINK_RANDOM_SHIFT;
sum = pink->pink_RunningSum + newRandom;
/* Scale to range of -1.0 to 0.9999. */
output = pink->pink_Scalar * sum;
return output;
}
static void pink_generator(syn123_handle *sh, int samples)
{
for(int i=0; i<samples; ++i)
sh->workbuf[1][i] = GeneratePinkNoise(sh->handle);
}
int attribute_align_arg
syn123_setup_pink(syn123_handle *sh, int rows, unsigned long seed, size_t *period)
{
int ret = SYN123_OK;
if(!sh)
return SYN123_BAD_HANDLE;
syn123_setup_silence(sh);
if(rows < 1)
rows = 22;
if(rows > PINK_MAX_RANDOM_ROWS)
rows = PINK_MAX_RANDOM_ROWS;
PinkNoise *handle = malloc(sizeof(PinkNoise));
if(!handle)
return SYN123_DOOM;
handle->rand_value = seed;
InitializePinkNoise(handle, rows);
sh->handle = handle;
sh->generator = pink_generator;
if(sh->maxbuf)
{
size_t samplesize = MPG123_SAMPLESIZE(sh->fmt.encoding);
size_t buffer_samples = sh->maxbuf/samplesize;
grow_buf(sh, buffer_samples*samplesize);
if(buffer_samples > sh->bufs/samplesize)
{
ret = SYN123_DOOM;
goto setup_pink_end;
}
int outchannels = sh->fmt.channels;
sh->fmt.channels = 1;
size_t buffer_bytes = syn123_read(sh, sh->buf, buffer_samples*samplesize);
sh->fmt.channels = outchannels;
InitializePinkNoise(handle, rows);
if(buffer_bytes != buffer_samples*samplesize)
{
ret = SYN123_WEIRD;
goto setup_pink_end;
}
sh->samples = buffer_samples;
}
if(sh->samples)
{
handle->rand_value = seed;
InitializePinkNoise(handle, rows);
}
setup_pink_end:
if(ret != SYN123_OK)
syn123_setup_silence(sh);
if(period)
*period = sh->samples;
return ret;
}

2550
src/libsyn123/resample.c Normal file

File diff suppressed because it is too large Load Diff

1029
src/libsyn123/sampleconv.c Normal file

File diff suppressed because it is too large Load Diff

1118
src/libsyn123/syn123.h.in Normal file

File diff suppressed because it is too large Load Diff

187
src/libsyn123/syn123_int.h Normal file
View File

@@ -0,0 +1,187 @@
/*
syn123_int: internal header for libsyn123
copyright 2018-2020 by the mpg123 project,
licensed under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
*/
#ifndef _MPG123_SYN123_INT_H_
#define _MPG123_SYN123_INT_H_
#define _ISOC99_SOURCE
#include "config.h"
#include "intsym.h"
#include "compat.h"
#include "abi_align.h"
/* export DLL symbols */
#if defined(WIN32) && defined(DYNAMIC_BUILD)
#define BUILD_MPG123_DLL
#endif
#define SYN123_NO_LARGEFUNC
#include "syn123.h"
// Generally, a number of samples we work on in one go to
// allow the compiler to know our loops.
// An enum is the best integer constant you can define in plain C.
// This sets an upper limit to the number of channels in some functions.
enum { bufblock = 512 };
struct syn123_wave
{
enum syn123_wave_id id;
int backwards; /* TRUE or FALSE */
double freq; /* actual frequency */
double phase; /* current phase */
};
struct syn123_sweep
{
struct syn123_wave wave;
double f1, f2; // begin/end frequencies (or logarithms of the same)
enum syn123_sweep_id id;
size_t i; // position counter
size_t d; // duration
size_t post; // amount of samples after sweep to finish period
double endphase; // phase for continuing, just after sweep end
};
// Only a forward declaration of the resampler state. An instance
// is allocated and a pointer stored if it is configured. The
// resampler is pretty disjunct from the other parts of syn123.
// Not sure if synergies will emerge eventually.
struct resample_data;
struct filter_chain
{
int mixenc; // double or float
int channels;
size_t count; // of filters
size_t maxcount; // storage allocated for that many, to avoid
// realloc in syn123_drop_filter()
// Only one type is configured!
struct d_filter *df; // double precision filters
struct f_filter *ff; // single precision filters
};
struct syn123_struct
{
// Temporary storage in internal precision.
// This is a set of two to accomodate x and y=function(x, y).
// Working in blocks reduces function call overhead and gives
// chance of vectorization.
// This may also be used as buffer for data with output encoding,
// exploiting the fact that double is the biggest data type we
// handle, also with the biggest alignment.
double workbuf[2][bufblock];
struct mpg123_fmt fmt;
int dither; // if dithering is activated for the handle
int do_dither; // flag for recursive calls of syn123_conv()
uint32_t dither_seed;
// Pointer to a generator function that writes a bit of samples
// into workbuf[1], possibly using workbuf[0] internally.
// Given count of samples <= bufblock!
void (*generator)(syn123_handle*, int);
// Generator configuration.
// wave generator
size_t wave_count;
struct syn123_wave* waves;
// pink noise, maybe others: simple structs that can be
// simply free()d
void* handle;
uint32_t seed; // random seed for some RNGs
// Extraction of initially-computed waveform from buffer.
void *buf; // period buffer
size_t bufs; // allocated size of buffer in bytes
size_t maxbuf; // maximum period buffer size in bytes
size_t samples; // samples (PCM frames) in period buffer
size_t offset; // offset in buffer for extraction helper
struct resample_data *rd; // resampler data, if initialized
struct filter_chain fc;
};
#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.
static void grow_buf(syn123_handle *sh, size_t bytes)
{
if(sh->bufs >= bytes)
return;
if(sh->buf)
free(sh->buf);
sh->buf = NULL;
if(bytes && bytes <= sh->maxbuf)
sh->buf = malloc(bytes);
sh->bufs = sh->buf ? bytes : 0;
}
#endif
#ifdef FILL_PERIOD
static int fill_period(syn123_handle *sh)
{
sh->samples = 0;
if(!sh->maxbuf)
return SYN123_OK;
size_t samplesize = MPG123_SAMPLESIZE(sh->fmt.encoding);
size_t buffer_samples = sh->maxbuf/samplesize;
grow_buf(sh, buffer_samples*samplesize);
if(buffer_samples > sh->bufs/samplesize)
return SYN123_DOOM;
int outchannels = sh->fmt.channels;
sh->fmt.channels = 1;
size_t buffer_bytes = syn123_read(sh, sh->buf, buffer_samples*samplesize);
sh->fmt.channels = outchannels;
if(buffer_bytes != buffer_samples*samplesize)
return SYN123_WEIRD;
sh->samples = buffer_samples;
return SYN123_OK;
}
#endif
#ifdef RAND_XORSHIFT32
// Borrowing the random number algorithm from the libmpg123 dither code.
// xorshift random number generator
// See http://www.jstatsoft.org/v08/i14/paper on XOR shift random number generators.
static float rand_xorshift32(uint32_t *seed)
{
union
{
uint32_t i;
float f;
} fi;
fi.i = *seed;
fi.i ^= (fi.i<<13);
fi.i ^= (fi.i>>17);
fi.i ^= (fi.i<<5);
*seed = fi.i;
/* scale the number to [-0.5, 0.5] */
#ifdef IEEE_FLOAT
fi.i = (fi.i>>9)|0x3f800000;
fi.f -= 1.5f;
#else
fi.f = (double)fi.i / 4294967295.0;
fi.f -= 0.5f;
#endif
return fi.f;
}
#endif
#endif

128
src/libsyn123/volume.c Normal file
View File

@@ -0,0 +1,128 @@
/*
volume: libsyn123 audio volume handling
copyright 2018 by the mpg123 project
licensed under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
*/
#define NO_GROW_BUF
#define NO_SMAX
#include "syn123_int.h"
#include "debug.h"
// Avoid conflict with pragmas in isnan() and friends.
#undef warning
static const double db_min = -SYN123_DB_LIMIT;
static const double db_max = SYN123_DB_LIMIT;
double attribute_align_arg syn123_db2lin(double db)
{
if(isnan(db) || isless(db, db_min))
db = db_min;
else if(isgreater(db, db_max))
db = db_max;
return pow(10, db/20);
}
double attribute_align_arg syn123_lin2db(double volume)
{
double db;
if(isnan(volume) || islessequal(volume, 0.))
db = db_min;
else
{
db = 20*log10(volume);
if(isgreater(db, db_min))
{
if(isgreater(db, db_max))
db = db_max;
}
else
db = db_min;
}
return db;
}
int attribute_align_arg
syn123_amp( void* buf, int encoding, size_t samples
, double volume, double offset, size_t *clipped, syn123_handle *sh )
{
size_t clips = 0;
int err = 0;
if(!buf)
{
err = SYN123_BAD_BUF;
goto amp_end;
}
switch(encoding)
{
// This is close to FMA, but only that. It's FAM.
#define AMP_LOOP(type) \
for(size_t i=0; i<samples; ++i) \
((type*)buf)[i] = (type)volume * (((type*)buf)[i] + (type)offset);
case MPG123_ENC_FLOAT_32:
AMP_LOOP(float)
goto amp_end;
case MPG123_ENC_FLOAT_64:
AMP_LOOP(double)
goto amp_end;
#undef AMP_LOOP
}
if(!sh)
{
err = SYN123_BAD_ENC;
goto amp_end;
}
else
{
char *cbuf = buf;
int mixenc = syn123_mixenc(encoding, encoding);
int mixframe = MPG123_SAMPLESIZE(mixenc);
int inframe = MPG123_SAMPLESIZE(encoding);
if(!mixenc || !mixframe || !inframe)
{
err = SYN123_BAD_CONV;
goto amp_end;
}
// Use the whole workbuf, both halves.
int mbufblock = 2*bufblock*sizeof(double)/mixframe;
mdebug("mbufblock=%i (enc %i)", mbufblock, mixenc);
while(samples)
{
int block = (int)smin(samples, mbufblock);
int err = syn123_conv(
sh->workbuf, mixenc, sizeof(sh->workbuf)
, cbuf, encoding, inframe*block
, NULL, NULL, NULL );
if(!err)
{
err = syn123_amp( sh->workbuf, mixenc, block
, volume, offset, NULL, NULL );
if(err)
return err;
size_t clips_block = 0;
err = syn123_conv(
cbuf, encoding, inframe*block
, sh->workbuf, mixenc, mixframe*block
, NULL, &clips_block, NULL );
clips += clips_block;
}
if(err)
{
mdebug("conv error: %i", err);
err = SYN123_BAD_CONV; // SYN123_WEIRD also an option
goto amp_end;
}
cbuf += block*inframe;
samples -= block;
}
}
amp_end:
if(clipped)
*clipped = clips;
return err;
}