diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2024-02-15 20:14:53 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2024-02-15 20:14:53 +0100 |
commit | 045dc058f39278b56515a5629be465cadacaac22 (patch) | |
tree | e85c7e54ad346ac72bb8cc15d4f41ab466d4395e | |
parent | 22533808360e515fec9bc373ea3e7b2cb2c99b33 (diff) | |
download | saw-045dc058f39278b56515a5629be465cadacaac22.zip |
Sampler crossfading
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | source/saw/main.c | 137 |
2 files changed, 101 insertions, 38 deletions
@@ -12,7 +12,6 @@ To-Do list - Simple tonal synth - Kick, snare, cymbal - EQ, delay, reverb, compressor, limiter - - Sampler - Sample rendering - Internal sample rate for time values @@ -58,6 +57,7 @@ Done - Sample loading - WAV import - Drag & drop audio files + - Sampler - UI - Piano roll diff --git a/source/saw/main.c b/source/saw/main.c index cc2bfa1..02d94c4 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -51,6 +51,8 @@ // Constants // +#define TIME_EPS 0.0001 + enum { // TODO // @@ -187,8 +189,6 @@ typedef struct { f64 begin; f64 end; f64 tail; - f64 fade_in; - f64 fade_out; f64 crossfade; f64 base_frequency; f64 volume; @@ -547,6 +547,9 @@ static void saw_audio_render(void) { case INSTRUMENT_SAMPLER: { saw_sampler_t *sam = &track->sampler; + if (sam->begin + sam->crossfade + TIME_EPS >= sam->end) + break; + f64 frequency = saw_voices[n].frequency; f64 amplitude = saw_voices[n].amplitude; @@ -561,14 +564,86 @@ static void saw_audio_render(void) { sustain, duration, release); - saw_playback_temp[i * CHANNEL_COUNT] += - (f32) (saw_sampler(sam, 0, frequency, - saw_voices[n].time) * - a); - saw_playback_temp[i * CHANNEL_COUNT + 1] += - (f32) (saw_sampler(sam, 1, frequency, - saw_voices[n].time) * - a); + f64 crossfade = sam->crossfade; + f64 sample_begin = sam->begin; + f64 sample_end = sam->end; + f64 sample_duration = sample_end - sample_begin - + crossfade; + f64 sample_tail = sam->tail; + f64 body_end = duration + crossfade; + f64 tail_begin = duration; + f64 tail_end = tail_begin + sample_tail; + + if (t <= body_end) { + // Play the body + // + + f64 q = t; + assert(sample_duration > TIME_EPS); + while (q >= sample_duration) + q -= sample_duration; + q += sample_begin; + + f64 u0 = 1.; + if (t > tail_begin && crossfade > TIME_EPS) + u0 = 1. - (t - tail_begin) / crossfade; + + if (t >= sample_duration && q < crossfade && + crossfade > TIME_EPS) { + // Play the body crossfade + // + + f64 u = u0 * (1. - q / crossfade); + f64 r = sample_begin + sample_duration + q; + i64 k = (i64) floor(r * SAMPLE_RATE + .5); + + saw_playback_temp[i * CHANNEL_COUNT] += + (f32) (saw_sampler(sam, 0, frequency, k) * a * + u); + saw_playback_temp[i * CHANNEL_COUNT + 1] += + (f32) (saw_sampler(sam, 1, frequency, k) * a * + u); + } + + { + // Play the body main part + // + + f64 u = u0; + if (t >= sample_duration && q < crossfade && + crossfade > TIME_EPS) + u *= q / crossfade; + + f64 r = sample_begin + q; + i64 k = (i64) floor(r * SAMPLE_RATE + .5); + + saw_playback_temp[i * CHANNEL_COUNT] += + (f32) (saw_sampler(sam, 0, frequency, k) * a * + u); + saw_playback_temp[i * CHANNEL_COUNT + 1] += + (f32) (saw_sampler(sam, 1, frequency, k) * a * + u); + } + } + + if (t >= tail_begin && t <= tail_end) { + // Play the tail + // + + f64 q = t - tail_begin; + + f64 u = 1.; + if (q < crossfade && crossfade > TIME_EPS) + u = q / crossfade; + + f64 r = sample_end + q; + i64 k = (i64) floor(r * SAMPLE_RATE + .5); + + saw_playback_temp[i * CHANNEL_COUNT] += + (f32) (saw_sampler(sam, 0, frequency, k) * a * u); + saw_playback_temp[i * CHANNEL_COUNT + 1] += + (f32) (saw_sampler(sam, 1, frequency, k) * a * u); + } saw_voices[n].time++; @@ -1517,8 +1592,6 @@ static void saw_ui_choose_instrument(saw_track_t *track, i64 x0, track->sampler.begin = 0.; track->sampler.end = 1.; track->sampler.tail = .2; - track->sampler.fade_in = 0.f; - track->sampler.fade_out = 0.; track->sampler.crossfade = .01; track->sampler.base_frequency = 440.; track->sampler.volume = .5; @@ -1667,7 +1740,12 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, nvgTextAlign(saw_nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); nvgText(saw_nvg, x0 + width / 2, frame_height - y0 - height + sample_height / 2, - "Drop a WAV file here", NULL); +#ifdef __EMSCRIPTEN__ + "Not implemented for the web yet", +#else + "Drop a WAV file here", +#endif + NULL); } saw_ui_value_float(x, y0 + height - sample_height - text_height, w, @@ -1680,18 +1758,12 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, w, text_height, TINT_WHITE, SZ("Tail"), 100000, 0., 60., &sampler->tail); saw_ui_value_float(x, y0 + height - sample_height - text_height * 4, - w, text_height, TINT_WHITE, SZ("Fade in"), - 100000, 0., 60., &sampler->fade_in); - saw_ui_value_float(x, y0 + height - sample_height - text_height * 5, - w, text_height, TINT_WHITE, SZ("Fade out"), - 100000, 0., 60., &sampler->fade_out); - saw_ui_value_float(x, y0 + height - sample_height - text_height * 6, w, text_height, TINT_WHITE, SZ("Crossfade"), 100000, 0., 60., &sampler->crossfade); - saw_ui_value_float(x, y0 + height - sample_height - text_height * 7, + saw_ui_value_float(x, y0 + height - sample_height - text_height * 5, w, text_height, TINT_WHITE, SZ("Base freq."), 500, 1., 44100., &sampler->base_frequency); - saw_ui_value_float(x, y0 + height - sample_height - text_height * 8, + saw_ui_value_float(x, y0 + height - sample_height - text_height * 6, w, text_height, TINT_WHITE, SZ("Volume."), 10000, 0., 2., &sampler->volume); @@ -1701,23 +1773,22 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, nvgTextAlign(saw_nvg, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE); nvgText(saw_nvg, x, frame_height - y0 - height + sample_height + - (text_height * 19) / 2, + (text_height * 15) / 2, "Envelope", NULL); - saw_ui_value_float(x, - y0 + height - sample_height - text_height * 11, + saw_ui_value_float(x, y0 + height - sample_height - text_height * 9, w, text_height, TINT_WHITE, SZ("Sustain"), 10000, 0., 1., &sampler->envelope.sustain); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 12, + y0 + height - sample_height - text_height * 10, w, text_height, TINT_WHITE, SZ("Attack"), 100000, 0., 6., &sampler->envelope.attack); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 13, + y0 + height - sample_height - text_height * 11, w, text_height, TINT_WHITE, SZ("Decay"), 100000, 0., 6., &sampler->envelope.decay); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 14, + y0 + height - sample_height - text_height * 12, w, text_height, TINT_WHITE, SZ("Release"), 100000, 0., 6., &sampler->envelope.release); } @@ -2555,14 +2626,12 @@ static void saw_init(void) { sam->outline.values[i] = fabs(v[k] + v[k + 1]) * .5; } - i64 begin, end, tail, fade_in, fade_out, crossfade, bfreq, - volume, sustain, attack, decay, release; + i64 begin, end, tail, crossfade, bfreq, volume, sustain, + attack, decay, release; SCAN_(" begin %lld", 1, &begin); SCAN_(" end %lld", 1, &end); SCAN_(" tail %lld", 1, &tail); - SCAN_(" fade_in %lld", 1, &fade_in); - SCAN_(" fade_out %lld", 1, &fade_out); SCAN_(" crossfade %lld", 1, &crossfade); SCAN_(" base_frequency %lld", 1, &bfreq); SCAN_(" volume %lld", 1, &volume); @@ -2574,8 +2643,6 @@ static void saw_init(void) { sam->begin = begin * .0001; sam->end = end * .0001; sam->tail = tail * .0001; - sam->fade_in = fade_in * .0001; - sam->fade_out = fade_out * .0001; sam->crossfade = crossfade * .0001; sam->base_frequency = bfreq * .0001; sam->volume = volume * .0001; @@ -2862,10 +2929,6 @@ static void saw_cleanup(void) { (i64) floor(sam->end * 10000 + .5)); fprintf(f, "tail %lld\n", (i64) floor(sam->tail * 10000 + .5)); - fprintf(f, "fade_in %lld\n", - (i64) floor(sam->fade_in * 10000 + .5)); - fprintf(f, "fade_out %lld\n", - (i64) floor(sam->fade_out * 10000 + .5)); fprintf(f, "crossfade %lld\n", (i64) floor(sam->crossfade * 10000 + .5)); fprintf(f, "base_frequency %lld\n", |