diff --git a/amy/test.py b/amy/test.py index 82876a33..50cdf8b0 100644 --- a/amy/test.py +++ b/amy/test.py @@ -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) diff --git a/src/oscillators.c b/src/oscillators.c index 3a2784be..9b1f03a2 100644 --- a/src/oscillators.c +++ b/src/oscillators.c @@ -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. diff --git a/tests/ref/TestWavetable.wav b/tests/ref/TestWavetable.wav index abad789d..8d40a5e0 100644 Binary files a/tests/ref/TestWavetable.wav and b/tests/ref/TestWavetable.wav differ