mirror of
https://github.com/sjlongland/atinysynth.git
synced 2025-09-13 10:03:15 +10:00
- Fixed fractional frequency
This commit is contained in:
parent
8381065e5b
commit
b6bdd2557c
@ -157,9 +157,11 @@ wish to use the ADSR envelope generator only to modulate lights.
|
||||
Configures the waveform generator to generate a square wave.
|
||||
|
||||
`sample` is initialised as `+amplitude`, and the half-period is
|
||||
computed as `period=SYNTH_FREQ/(2*freq)`. `period_remain` is
|
||||
computed as `period=SYNTH_FREQ/(2*freq)`. `period_remain` is
|
||||
initialised to `period`.
|
||||
|
||||
Fixed-point `12.4` format (16 bit) is used to store the period counters, to allow tuned notes on low sampling rates too.
|
||||
|
||||
Each sample, `period_remain` is decremented. When `period_remain`
|
||||
reaches zero:
|
||||
|
||||
|
16
synth.h
16
synth.h
@ -41,22 +41,6 @@ extern const uint16_t __attribute__((weak)) synth_freq;
|
||||
#define synth_freq SYNTH_FREQ
|
||||
#endif
|
||||
|
||||
#ifdef SYNTH_FREQ_SCALE
|
||||
/*!
|
||||
* Amount of scaling to apply to synthesizer output frequencies. This
|
||||
* right-shifts frequencies, allowing for some bits to be used to indicate
|
||||
* fractional components, trading off frequency range for precision.
|
||||
*
|
||||
* Precision is 1/(2^SYNTH_FREQ_SCALE) Hz.
|
||||
* Range is 0-((65536/(2^SYNTH_FREQ_SCALE))-1).
|
||||
*
|
||||
* The default of 0 applies no scaling, allowing (integer) frequencies between
|
||||
* 0 and 65.535 kHz. The value 1 multiplies everything by two, allowing
|
||||
* frequencies between 0 and 32.767kHz with 500mHz precision.
|
||||
*/
|
||||
#define SYNTH_FREQ_SCALE (0)
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Polyphonic synthesizer structure
|
||||
*/
|
||||
|
41
waveform.c
41
waveform.c
@ -33,6 +33,14 @@
|
||||
/* Amplitude scaling */
|
||||
#define VOICE_WF_AMP_SCALE (8)
|
||||
|
||||
/*!
|
||||
* Number of fractional bits for `period` and `period_remain`.
|
||||
* This allows tuned notes even in lower sampling frequencies.
|
||||
* The integer part (12 bits) is wide enough to render a 20Hz
|
||||
* note on the higher 48kHz sampling frequency.
|
||||
*/
|
||||
#define PERIOD_FP_SCALE (4)
|
||||
|
||||
int8_t voice_wf_next(struct voice_wf_gen_t* const wf_gen) {
|
||||
switch(wf_gen->mode) {
|
||||
case VOICE_MODE_DC:
|
||||
@ -48,12 +56,12 @@ int8_t voice_wf_next(struct voice_wf_gen_t* const wf_gen) {
|
||||
wf_gen->sample);
|
||||
break;
|
||||
case VOICE_MODE_SQUARE:
|
||||
if (!wf_gen->period_remain) {
|
||||
if ((wf_gen->period_remain >> PERIOD_FP_SCALE) == 0) {
|
||||
/* Swap value */
|
||||
wf_gen->sample = -wf_gen->sample;
|
||||
wf_gen->period_remain = wf_gen->period;
|
||||
wf_gen->period_remain += wf_gen->period;
|
||||
} else {
|
||||
wf_gen->period_remain--;
|
||||
wf_gen->period_remain -= (1 << PERIOD_FP_SCALE);
|
||||
}
|
||||
_DPRINTF("wf=%p mode=SQUARE amp=%d rem=%d "
|
||||
"→ sample=%d\n",
|
||||
@ -62,13 +70,13 @@ int8_t voice_wf_next(struct voice_wf_gen_t* const wf_gen) {
|
||||
wf_gen->sample);
|
||||
break;
|
||||
case VOICE_MODE_SAWTOOTH:
|
||||
if (!wf_gen->period_remain) {
|
||||
if ((wf_gen->period_remain >> PERIOD_FP_SCALE) == 0) {
|
||||
/* Back to -amplitude */
|
||||
wf_gen->sample = -wf_gen->amplitude;
|
||||
wf_gen->period_remain = wf_gen->period;
|
||||
wf_gen->period_remain += wf_gen->period;
|
||||
} else {
|
||||
wf_gen->sample += wf_gen->step;
|
||||
wf_gen->period_remain--;
|
||||
wf_gen->period_remain -= (1 << PERIOD_FP_SCALE);
|
||||
}
|
||||
_DPRINTF("wf=%p mode=SAWTOOTH amp=%d rem=%d step=%d "
|
||||
"→ sample=%d\n",
|
||||
@ -77,17 +85,17 @@ int8_t voice_wf_next(struct voice_wf_gen_t* const wf_gen) {
|
||||
wf_gen->sample);
|
||||
break;
|
||||
case VOICE_MODE_TRIANGLE:
|
||||
if (!wf_gen->period_remain) {
|
||||
if ((wf_gen->period_remain >> PERIOD_FP_SCALE) == 0) {
|
||||
/* Switch direction */
|
||||
if (wf_gen->step > 0)
|
||||
wf_gen->sample = wf_gen->amplitude;
|
||||
else
|
||||
wf_gen->sample = -wf_gen->amplitude;
|
||||
wf_gen->step = -wf_gen->step;
|
||||
wf_gen->period_remain = wf_gen->period;
|
||||
wf_gen->period_remain += wf_gen->period;
|
||||
} else {
|
||||
wf_gen->sample += wf_gen->step;
|
||||
wf_gen->period_remain--;
|
||||
wf_gen->period_remain -= (1 << PERIOD_FP_SCALE);
|
||||
}
|
||||
_DPRINTF("wf=%p mode=TRIANGLE amp=%d rem=%d step=%d "
|
||||
"→ sample=%d\n",
|
||||
@ -102,15 +110,8 @@ int8_t voice_wf_next(struct voice_wf_gen_t* const wf_gen) {
|
||||
|
||||
/* Compute frequency period (sawtooth wave) */
|
||||
static uint16_t voice_wf_calc_sawtooth_period(uint16_t freq) {
|
||||
#if SYNTH_FREQ_SCALE > 0
|
||||
/*
|
||||
* Cast synth_freq to 32-bits in case we overflow in the right-shift.
|
||||
* Yes, this is going to hurt!
|
||||
*/
|
||||
return (((uint32_t)(synth_freq << SYNTH_FREQ_SCALE)) / freq);
|
||||
#else
|
||||
return (synth_freq / freq);
|
||||
#endif
|
||||
/* Use 16-bit 12.4 fixed point */
|
||||
return (((uint32_t)(synth_freq << PERIOD_FP_SCALE)) / freq);
|
||||
}
|
||||
|
||||
/* Compute frequency period (square/triangle wave) */
|
||||
@ -146,7 +147,7 @@ void voice_wf_set_sawtooth(struct voice_wf_gen_t* const wf_gen,
|
||||
wf_gen->period = voice_wf_calc_sawtooth_period(freq);
|
||||
wf_gen->period_remain = wf_gen->period;
|
||||
wf_gen->amplitude = -wf_gen->sample;
|
||||
wf_gen->step = ((int32_t)(wf_gen->amplitude << 1)) / wf_gen->period;
|
||||
wf_gen->step = (wf_gen->amplitude / (wf_gen->period >> PERIOD_FP_SCALE)) << 1;
|
||||
_DPRINTF("wf=%p INIT mode=SAWTOOTH amp=%d per=%d step=%d rem=%d "
|
||||
"→ sample=%d\n",
|
||||
wf_gen, wf_gen->amplitude, wf_gen->period,
|
||||
@ -161,7 +162,7 @@ void voice_wf_set_triangle(struct voice_wf_gen_t* const wf_gen,
|
||||
wf_gen->period = voice_wf_calc_square_period(freq);
|
||||
wf_gen->period_remain = wf_gen->period;
|
||||
wf_gen->amplitude = -wf_gen->sample;
|
||||
wf_gen->step = ((int32_t)(wf_gen->amplitude << 1)) / wf_gen->period;
|
||||
wf_gen->step = (wf_gen->amplitude / (wf_gen->period >> PERIOD_FP_SCALE)) << 1;
|
||||
_DPRINTF("wf=%p INIT mode=TRIANGLE amp=%d per=%d step=%d rem=%d "
|
||||
"→ sample=%d\n",
|
||||
wf_gen, wf_gen->amplitude, wf_gen->period,
|
||||
|
@ -29,10 +29,10 @@ struct voice_wf_gen_t {
|
||||
int16_t sample;
|
||||
/*! Amplitude sample in fixed point */
|
||||
int16_t amplitude;
|
||||
/*! Samples to next waveform period */
|
||||
/*! Samples to next waveform period (12.4 fixed point) */
|
||||
uint16_t period_remain;
|
||||
/*!
|
||||
* Period duration in samples.
|
||||
* Period duration in samples (12.4 fixed point).
|
||||
* (Half period for SQUARE and TRIANGLE)
|
||||
*/
|
||||
uint16_t period;
|
||||
|
Loading…
Reference in New Issue
Block a user