Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion amy/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,8 @@ class TestWavetable(AmyTest):
"""Simple exercise of the wavetable oscillator, using default wavetable."""

def run(self):
amy.send(time=0, osc=0, wave=amy.WAVETABLE, preset=0, duty='0.25,0,0,0,0.5', bp1='0,0,800,1,100,1', bp0='50,1,50,0')
amy.send(time=0, osc=0, wave=amy.WAVETABLE, duty='0.25,0,0,0,0.5', bp1='0,0,800,1,100,1', bp0='50,1,50,0')
# preset=19,
amy.send(time=50, note=50, vel=1)
amy.send(time=850, vel=0)

Expand Down
55 changes: 34 additions & 21 deletions src/oscillators.c
Original file line number Diff line number Diff line change
Expand Up @@ -807,39 +807,52 @@ void wavetable_note_on(uint16_t osc, float freq) {
}

// Structure of waveeditonlie wavetables.
const int CYCLES_PER_WAVETABLE = 64;
//const int CYCLES_PER_WAVETABLE = 64;
const int WAVETABLE_SAMPLES_PER_CYCLE = 256;
const int WAVETABLE_LOG2_SAMPLES_PER_CYCLE = 8;

#ifdef GAMMA9001
#define PCM_WAVETABLE_BASE 19
#else
#define PCM_WAVETABLE_BASE 11
#endif

SAMPLE render_wavetable(SAMPLE* buf, uint16_t osc) {
SAMPLE max_value = 0;
float freq = freq_of_logfreq(msynth[osc]->logfreq);
PHASOR step = F2P(freq / (float)AMY_SAMPLE_RATE); // cycles per sec / samples per sec -> cycles per sample
PHASOR step = F2P(freq / (float)AMY_SAMPLE_RATE);
SAMPLE amp = F2S(msynth[osc]->amp);
SAMPLE last_amp = F2S(msynth[osc]->last_amp);
//fprintf(stderr, "render_wavetable: time %f osc %d freq %f last_amp %f amp %f preset %d\n", amy_global.total_blocks*AMY_BLOCK_SIZE / (float)AMY_SAMPLE_RATE, osc, AMY_SAMPLE_RATE * P2F(step), S2F(last_amp), S2F(amp), synth[osc]->preset);
SAMPLE max_value;
float interp = MAX(0, MIN(CYCLES_PER_WAVETABLE - 1, (CYCLES_PER_WAVETABLE - 1) * msynth[osc]->duty)); // Don't try to interp beyond end of table. An N-waveform table can be interpolated from 0 to (N-1-eps).
int table = MIN((int)floor(interp), CYCLES_PER_WAVETABLE - 2); // always need both this wavetable and the next one.
interp = interp - table; // fractional part, normally < 1.0, but == 1.0 for very end of table.
int wavetable_preset = AMY_IS_SET(synth[osc]->preset)
? (int)synth[osc]->preset
: (int)pcm_wavetable_base;
int wavetable_samples_per_table = (pcm_wavetable_len > 0) ? (int)pcm_wavetable_len : 16384;
uint32_t sample_length = 0;
const int16_t *wavetable_sample_ram =
pcm_get_sample_ram_for_preset((uint16_t)wavetable_preset, &sample_length);
if ((wavetable_sample_ram == NULL || sample_length < (uint32_t)wavetable_samples_per_table) &&
wavetable_preset != (int)pcm_wavetable_base) {
wavetable_sample_ram = pcm_get_sample_ram_for_preset(pcm_wavetable_base, &sample_length);
}
if (wavetable_sample_ram == NULL || sample_length < (uint32_t)wavetable_samples_per_table) {
int16_t wavetable_preset = synth[osc]->preset;
if (AMY_IS_UNSET(wavetable_preset))
wavetable_preset = PCM_WAVETABLE_BASE;
uint32_t sample_length;
const int16_t *wavetable_samples = pcm_get_sample_ram_for_preset(wavetable_preset, &sample_length);
if (wavetable_samples == NULL || sample_length < (2 * WAVETABLE_SAMPLES_PER_CYCLE)) {
// We need at least two cycles to have something to crossfade.
return 0;
}
LUT wavetable_lut = {wavetable_sample_ram, WAVETABLE_SAMPLES_PER_CYCLE, WAVETABLE_LOG2_SAMPLES_PER_CYCLE, 0, 1.0f};
wavetable_lut.table += table * WAVETABLE_SAMPLES_PER_CYCLE;
int cycles_this_table = sample_length >> WAVETABLE_LOG2_SAMPLES_PER_CYCLE;
// Don't try to interp beyond end of table. An N-waveform table can be interpolated from 0 to (N-1-eps).
float interp = MAX(0,
MIN(cycles_this_table - 1,
(cycles_this_table - 1) * msynth[osc]->duty));
// always need both this wavetable and the next one.
int cycle = MIN((int)floor(interp),
cycles_this_table - 2);
// fractional part, normally < 1.0, but == 1.0 for very end of table.
interp = interp - cycle;
LUT wavetable_lut = {
wavetable_samples + cycle * WAVETABLE_SAMPLES_PER_CYCLE,
WAVETABLE_SAMPLES_PER_CYCLE,
WAVETABLE_LOG2_SAMPLES_PER_CYCLE,
0, /* highest harmonic, for choosing tables in set. */
1.0f, /* scale factor, for denormalizing. */
};
// don't update phase in the first call to render_lut, so second call uses the same phase
SAMPLE interp_a = F2S(1.0f - interp);
SAMPLE interp_b = F2S(interp);
SAMPLE interp_a = F2S(1.0f) - interp_b;
// If we used last_duty, we could actually smoothly interpolate the waveshape crossfade too (except across table boundaries).
render_lut(buf, synth[osc]->phase, step, SMULR7(last_amp, interp_a), SMULR7(amp, interp_a), &wavetable_lut, &max_value);
// Point to next cycle.
Expand Down
Binary file modified tests/ref/TestWavetable.wav
Binary file not shown.
Loading