Blame src/waves.c

Packit c32a2d
/*
Packit c32a2d
	waves: some oscillators, for fun
Packit c32a2d
Packit c32a2d
	copyright 2017 by the mpg123 project, license: LGPL 2.1
Packit c32a2d
Packit c32a2d
	This code does not care about efficiency constructing the period buffer,
Packit c32a2d
	as that is done only once at the beginning. Double precision
Packit c32a2d
	computations should be fine with software emulation.
Packit c32a2d
Packit c32a2d
	There might be some slight pulsing from aliasing inside the buffer, where
Packit c32a2d
	there is a fractional number of samples per period and so some shidting
Packit c32a2d
	of sample point locations.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "waves.h"
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
/* Not depending on C99 math for these simple things, */
Packit c32a2d
Packit c32a2d
static const double freq_error = 1e-4;
Packit c32a2d
static const double twopi = 2.0*3.14159265358979323846;
Packit c32a2d
Packit c32a2d
/* absolute value */
Packit c32a2d
static double myabs(double a)
Packit c32a2d
{
Packit c32a2d
	return a < 0 ? -a : a;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* round floating point to size_t */
Packit c32a2d
static size_t round2size(double a)
Packit c32a2d
{
Packit c32a2d
	return a < 0 ? 0 : (size_t)(a+0.5);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* fractional part, relating to frequencies (so long matches) */
Packit c32a2d
static double myfrac(double a)
Packit c32a2d
{
Packit c32a2d
	return a-(long)a;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Given a set of wave frequencies, compute an approximate common
Packit c32a2d
	period for the combined signal. Invalid frequencies are set to
Packit c32a2d
	the error bound for some sanity.
Packit c32a2d
*/
Packit c32a2d
static double common_samples_per_period( long rate, size_t count
Packit c32a2d
,	double *freq, size_t size_limit )
Packit c32a2d
{
Packit c32a2d
	double spp = 0;
Packit c32a2d
	size_t i;
Packit c32a2d
	for(i=0; i
Packit c32a2d
	{
Packit c32a2d
		double sppi;
Packit c32a2d
		size_t periods = 1;
Packit c32a2d
		/* Limiting sensible frequency range. */
Packit c32a2d
		if(freq[i] < freq_error)
Packit c32a2d
			freq[i] = freq_error;
Packit c32a2d
		if(freq[i] > rate/2)
Packit c32a2d
			freq[i] = rate/2;
Packit c32a2d
		sppi = myabs((double)rate/freq[i]);
Packit c32a2d
		debug2("freq=%g sppi=%g", freq[i], sppi);
Packit c32a2d
		if(spp == 0)
Packit c32a2d
			spp = sppi;
Packit c32a2d
		while
Packit c32a2d
		(
Packit c32a2d
			periods*spp < size_limit &&
Packit c32a2d
			myabs( myfrac(periods*spp / sppi) ) > freq_error
Packit c32a2d
		)
Packit c32a2d
			periods++;
Packit c32a2d
		spp*=periods;
Packit c32a2d
		debug3( "samples_per_period + %f Hz = %g (%" SIZE_P " periods)"
Packit c32a2d
		,	freq[i], spp, periods );
Packit c32a2d
	}
Packit c32a2d
	return spp;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Compute a good size of a table covering the common period for all waves. */
Packit c32a2d
static size_t tablesize( long rate, size_t count
Packit c32a2d
,	double *freq, size_t size_limit )
Packit c32a2d
{
Packit c32a2d
	size_t ts;
Packit c32a2d
	double samples_per_period;
Packit c32a2d
	size_t periods;
Packit c32a2d
Packit c32a2d
	samples_per_period = common_samples_per_period( rate, count
Packit c32a2d
	,	freq, size_limit );
Packit c32a2d
	periods = 1;
Packit c32a2d
	while
Packit c32a2d
	(
Packit c32a2d
		myabs( (double)(ts =
Packit c32a2d
			round2size(periods*samples_per_period))
Packit c32a2d
			- periods*samples_per_period
Packit c32a2d
		) / periods > freq_error*samples_per_period
Packit c32a2d
		&& periods*samples_per_period < size_limit
Packit c32a2d
	)
Packit c32a2d
		periods++;
Packit c32a2d
	/* Ensure that we got an even number of samples to accomodate the minimal */
Packit c32a2d
	/* sampling of a period. */
Packit c32a2d
	ts += ts % 2;
Packit c32a2d
	debug1("table size: %" SIZE_P, ts);
Packit c32a2d
	return ts;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* The wave functions. Argument is the phase normalised to the period. */
Packit c32a2d
/* The argument is guaranteed to be 0 <= p < 1. */
Packit c32a2d
Packit c32a2d
const char *wave_pattern_default = "sine";
Packit c32a2d
const char *wave_pattern_list = "sine, square, triangle, sawtooth, gauss, pulse, shot";
Packit c32a2d
Packit c32a2d
/* _________ */
Packit c32a2d
/*           */
Packit c32a2d
static double wave_none(double p)
Packit c32a2d
{
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*   __       */
Packit c32a2d
/*  /  \      */
Packit c32a2d
/*      \__/  */
Packit c32a2d
static double wave_sine(double p)
Packit c32a2d
{
Packit c32a2d
	return sin(twopi*p);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*      ___   */
Packit c32a2d
/*  ___|      */
Packit c32a2d
static double wave_square(double p)
Packit c32a2d
{
Packit c32a2d
	return (myfrac(p) < 0.5 ? -1 : 1);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*    1234    Avoid jump from zero at beginning. */
Packit c32a2d
/*    /\      */
Packit c32a2d
/*      \/    */
Packit c32a2d
static double wave_triangle(double p)
Packit c32a2d
{
Packit c32a2d
	return 4*p < 1
Packit c32a2d
	?	4*p        /* 1 */
Packit c32a2d
	:	( 4*p < 3
Packit c32a2d
		?	2.-4*p  /* 2 and 3 */
Packit c32a2d
		:	-4+4*p  /* 4 */
Packit c32a2d
		);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*   /|    Avoid jump from zero ... */
Packit c32a2d
/*    |/   */
Packit c32a2d
static double wave_sawtooth(double p)
Packit c32a2d
{
Packit c32a2d
	return 2*p < 1 ? 2*p : -2+2*p;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*    _    */
Packit c32a2d
/* __/ \__ */
Packit c32a2d
/*         */
Packit c32a2d
static double wave_gauss(double p)
Packit c32a2d
{
Packit c32a2d
	double v = p-0.5;
Packit c32a2d
	return exp(-30*v*v);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*    _      */
Packit c32a2d
/*  _/ -___  */
Packit c32a2d
/*           */
Packit c32a2d
/* p**2*exp(-a*p**2) */
Packit c32a2d
/* Scaling: maximum at sqrt(1/a), value 1/a*exp(-1). */
Packit c32a2d
static double wave_pulse(double p)
Packit c32a2d
{
Packit c32a2d
	return p*p*exp(-50*p*p)/0.00735758882342885;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*  _     */
Packit c32a2d
/* / -___ */
Packit c32a2d
/*        */
Packit c32a2d
/* p**2*exp(-a*p) */
Packit c32a2d
/* Scaling: maximum at 4/a, value 4/a**2*exp(-2). */
Packit c32a2d
static double wave_shot(double p)
Packit c32a2d
{
Packit c32a2d
	return p*p*exp(-100*p)/5.41341132946451e-05;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Fill table with given periodic wave function. */
Packit c32a2d
static void add_table( double *table, size_t samples
Packit c32a2d
, long rate, double *freq, const char *wavetype, double phase)
Packit c32a2d
{
Packit c32a2d
	size_t i, periods;
Packit c32a2d
	double spp, phaseoff;
Packit c32a2d
	double (*wave)(double);
Packit c32a2d
	debug3("add_table %" SIZE_P " %ld %g", samples, rate, *freq);
Packit c32a2d
	periods = round2size((double)samples*(*freq)/rate);
Packit c32a2d
	if(!periods)
Packit c32a2d
		periods = 1;
Packit c32a2d
	spp     = (double)samples/periods;
Packit c32a2d
	/* 2 samples is absolute minimum. */
Packit c32a2d
	if(spp < 2)
Packit c32a2d
		spp = 2;
Packit c32a2d
Packit c32a2d
	/* The actual frequency after the rounding of samples per period. */
Packit c32a2d
	*freq = (double)rate/spp;
Packit c32a2d
Packit c32a2d
	/* Center samples in period, somewhat, to ensure getting extrema with very */
Packit c32a2d
	/* small sample counts (instead of zero at beginning and center). */
Packit c32a2d
	phaseoff = 1./(2*spp);
Packit c32a2d
Packit c32a2d
	if(!wavetype)
Packit c32a2d
		wave = wave_none;
Packit c32a2d
	else if(!strcasecmp(wavetype, "sine"))
Packit c32a2d
		wave = wave_sine;
Packit c32a2d
	else if(!strcasecmp(wavetype, "square"))
Packit c32a2d
		wave = wave_square;
Packit c32a2d
	else if(!strcasecmp(wavetype, "triangle"))
Packit c32a2d
		wave = wave_triangle;
Packit c32a2d
	else if(!strcasecmp(wavetype, "sawtooth"))
Packit c32a2d
		wave = wave_sawtooth;
Packit c32a2d
	else if(!strcasecmp(wavetype, "gauss"))
Packit c32a2d
		wave = wave_gauss;
Packit c32a2d
	else if(!strcasecmp(wavetype, "pulse"))
Packit c32a2d
		wave = wave_pulse;
Packit c32a2d
	else if(!strcasecmp(wavetype, "shot"))
Packit c32a2d
		wave = wave_shot;
Packit c32a2d
	else
Packit c32a2d
		wave = wave_none;
Packit c32a2d
Packit c32a2d
	debug3( "adding wave: %s @ %g Hz + %g"
Packit c32a2d
	,	wavetype ? wavetype : "none", *freq, phase );
Packit c32a2d
Packit c32a2d
	/*
Packit c32a2d
		compromise: smooth onset for low frequencies, but also good sampling
Packit c32a2d
		of high freqs extreme case: 2 samples per period should trigger
Packit c32a2d
		max/min amplitude, not both zero so, center the samples within one
Packit c32a2d
		period. That's achieved by adding 0.25 to the counter.
Packit c32a2d
	*/
Packit c32a2d
	if(phase >= 0)
Packit c32a2d
		for(i=0; i
Packit c32a2d
			table[i] *= wave(myfrac( ((double)i+phaseoff)/spp + phase));
Packit c32a2d
	else
Packit c32a2d
		for(i=0; i
Packit c32a2d
			table[i] *= wave(1.-myfrac( ((double)i+phaseoff)/spp - phase));
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Construct the prototype table in double precision. */
Packit c32a2d
static double* mytable( long rate, size_t count, double *freq
Packit c32a2d
,	const char** wavetype, double *phase
Packit c32a2d
,	size_t size_limit, size_t *samples)
Packit c32a2d
{
Packit c32a2d
	size_t i;
Packit c32a2d
	double *table = NULL;
Packit c32a2d
Packit c32a2d
	*samples = tablesize(rate, count, freq, size_limit);
Packit c32a2d
	debug1("computed table size: %" SIZE_P, *samples);
Packit c32a2d
	table = malloc(*samples*sizeof(double));
Packit c32a2d
	if(!table)
Packit c32a2d
	{
Packit c32a2d
		error("OOM!");
Packit c32a2d
		return NULL;
Packit c32a2d
	}
Packit c32a2d
	/* Initialise to zero. */
Packit c32a2d
	for(i=0; i<*samples; ++i)
Packit c32a2d
		table[i] = 1;
Packit c32a2d
	/* Add individual waves, with default parameters. */
Packit c32a2d
	for(i=0; i
Packit c32a2d
		add_table(
Packit c32a2d
			table, *samples, rate, freq+i
Packit c32a2d
		,	wavetype ? wavetype[i] : wave_pattern_default
Packit c32a2d
		,	phase    ? phase[i]    : 0
Packit c32a2d
		);
Packit c32a2d
	/* Amplification could have caused clipping. */
Packit c32a2d
	for(i=0; i<*samples; ++i)
Packit c32a2d
	{
Packit c32a2d
		if(table[i] >  1.0)
Packit c32a2d
			table[i] =  1.0;
Packit c32a2d
		if(table[i] < -1.0)
Packit c32a2d
			table[i] = -1.0;
Packit c32a2d
		debug2("table[%" SIZE_P "]=%f", i, table[i]);
Packit c32a2d
	}
Packit c32a2d
	return table;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Some trivial conversion functions. No need for a library. */
Packit c32a2d
Packit c32a2d
/* All symmetric, +/- 2^n-1. */
Packit c32a2d
#define CONV(name, type, maxval) \
Packit c32a2d
static type name(double d) \
Packit c32a2d
{ \
Packit c32a2d
	type imax = maxval; \
Packit c32a2d
	d *= imax; \
Packit c32a2d
	if(d>=0) \
Packit c32a2d
	{ \
Packit c32a2d
		d += 0.5; \
Packit c32a2d
		return d > imax \
Packit c32a2d
		?	imax \
Packit c32a2d
		:	(type)d; \
Packit c32a2d
	} \
Packit c32a2d
	else \
Packit c32a2d
	{ \
Packit c32a2d
		d -= 0.5; \
Packit c32a2d
		return d < -imax \
Packit c32a2d
		?	-imax \
Packit c32a2d
		:	(type)d; \
Packit c32a2d
	} \
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
CONV(d2s32, int32_t, 2147483647L)
Packit c32a2d
CONV(d2s16, int16_t, 32767)
Packit c32a2d
CONV(d2s8,   char,   127)
Packit c32a2d
Packit c32a2d
#define HANDLE_OOM(p, h, p2) \
Packit c32a2d
if(!(p)) \
Packit c32a2d
{ \
Packit c32a2d
	error("OOM!"); \
Packit c32a2d
	if((p2)) free((p2)); \
Packit c32a2d
	return wave_table_del((h)); \
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Build internal table, allocate external table, convert to that one, */
Packit c32a2d
/* adjusting sample storage format and channel count. */
Packit c32a2d
struct wave_table* wave_table_new(
Packit c32a2d
	long rate, int channels, int encoding
Packit c32a2d
,	size_t count, double *freq
Packit c32a2d
,	const char** wavetype, double *phase
Packit c32a2d
,	size_t size_limit
Packit c32a2d
){
Packit c32a2d
	struct wave_table *handle;
Packit c32a2d
	double *dtable;
Packit c32a2d
	int c,i;
Packit c32a2d
Packit c32a2d
	handle = malloc(sizeof(struct wave_table));
Packit c32a2d
	HANDLE_OOM(handle, handle, NULL)
Packit c32a2d
Packit c32a2d
	handle->buf = NULL;
Packit c32a2d
	handle->fmt.rate = rate;
Packit c32a2d
	handle->fmt.channels = channels;
Packit c32a2d
	handle->fmt.encoding = encoding;
Packit c32a2d
	handle->samples = 0;
Packit c32a2d
	handle->offset  = 0;
Packit c32a2d
	handle->count = count;
Packit c32a2d
	handle->freq = NULL;
Packit c32a2d
Packit c32a2d
	handle->freq = malloc(sizeof(double)*count);
Packit c32a2d
	HANDLE_OOM(handle->freq, handle, NULL)
Packit c32a2d
	for(i=0; i
Packit c32a2d
		handle->freq[i] = freq[i];
Packit c32a2d
Packit c32a2d
	dtable = mytable( rate, count, handle->freq, wavetype, phase
Packit c32a2d
	,	size_limit, &handle->samples );
Packit c32a2d
	HANDLE_OOM(dtable, handle, NULL)
Packit c32a2d
	handle->buf = malloc( handle->samples
Packit c32a2d
	            * MPG123_SAMPLESIZE(encoding) * channels );
Packit c32a2d
	HANDLE_OOM(handle->buf, handle, dtable)
Packit c32a2d
	/* Now convert. */
Packit c32a2d
	for(c=0; c
Packit c32a2d
	{
Packit c32a2d
		switch(encoding)
Packit c32a2d
		{
Packit c32a2d
			/* Samples are clipped. Rounding a double below 1.0 to float cannot */
Packit c32a2d
			/* get larger than 1.0 since that is exact in single precision. */
Packit c32a2d
			case MPG123_ENC_FLOAT_64:
Packit c32a2d
				for(i=0; i<handle->samples; ++i)
Packit c32a2d
					((double*)handle->buf)[i*channels+c] = dtable[i];
Packit c32a2d
			break;
Packit c32a2d
			case MPG123_ENC_FLOAT_32:
Packit c32a2d
				for(i=0; i<handle->samples; ++i)
Packit c32a2d
					((float*)handle->buf)[i*channels+c] = dtable[i];
Packit c32a2d
			break;
Packit c32a2d
			case MPG123_ENC_SIGNED_32:
Packit c32a2d
				for(i=0; i<handle->samples; ++i)
Packit c32a2d
					((int32_t*)handle->buf)[i*channels+c] = d2s32(dtable[i]);
Packit c32a2d
			break;
Packit c32a2d
			case MPG123_ENC_SIGNED_16:
Packit c32a2d
				for(i=0; i<handle->samples; ++i)
Packit c32a2d
					((int16_t*)handle->buf)[i*channels+c] = d2s16(dtable[i]);
Packit c32a2d
			break;
Packit c32a2d
			case MPG123_ENC_SIGNED_8:
Packit c32a2d
				for(i=0; i<handle->samples; ++i)
Packit c32a2d
					((char*)handle->buf)[i*channels+c] = d2s8(dtable[i]);
Packit c32a2d
			break;
Packit c32a2d
			default:
Packit c32a2d
				error("unsupported encoding, choose f64, f32, s32, s16 or s8");
Packit c32a2d
				free(dtable);
Packit c32a2d
				return wave_table_del(handle);
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	free(dtable);
Packit c32a2d
	return handle;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void* wave_table_del(struct wave_table* handle)
Packit c32a2d
{
Packit c32a2d
	if(handle)
Packit c32a2d
	{
Packit c32a2d
		if(handle->freq)
Packit c32a2d
			free(handle->freq);
Packit c32a2d
		if(handle->buf)
Packit c32a2d
			free(handle->buf);
Packit c32a2d
		free(handle);
Packit c32a2d
	}
Packit c32a2d
	return NULL;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Copy blocks from offset to end until desired amount is extracted. */
Packit c32a2d
size_t wave_table_extract( struct wave_table *handle
Packit c32a2d
,	void *dest, size_t dest_samples )
Packit c32a2d
{
Packit c32a2d
	char *cdest = (char*)dest; /* Want to do arithmetic. */
Packit c32a2d
	size_t framesize;
Packit c32a2d
	size_t extracted = 0;
Packit c32a2d
Packit c32a2d
	if(!handle)
Packit c32a2d
		return 0;
Packit c32a2d
Packit c32a2d
	framesize = MPG123_SAMPLESIZE(handle->fmt.encoding)
Packit c32a2d
	          * handle->fmt.channels;
Packit c32a2d
	while(dest_samples)
Packit c32a2d
	{
Packit c32a2d
		size_t block = dest_samples > handle->samples - handle->offset
Packit c32a2d
		?	handle->samples - handle->offset
Packit c32a2d
		:	dest_samples;
Packit c32a2d
		debug1("block: %" SIZE_P, block);
Packit c32a2d
		memcpy( cdest, (char*)handle->buf+handle->offset*framesize
Packit c32a2d
		,	framesize*block );
Packit c32a2d
		cdest  += framesize*block;
Packit c32a2d
		handle->offset += block;
Packit c32a2d
		handle->offset %= handle->samples;
Packit c32a2d
		dest_samples -= block;
Packit c32a2d
		extracted    += block;
Packit c32a2d
	}
Packit c32a2d
	debug1("extracted: %" SIZE_P, extracted);
Packit c32a2d
	return extracted;
Packit c32a2d
}