From 70dd5c9fe395a3007133e12cddf69f638dcb19e4 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Thu, 15 Feb 2024 03:41:45 +0100 Subject: Sampler: basic sound --- source/saw/main.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 197 insertions(+), 15 deletions(-) diff --git a/source/saw/main.c b/source/saw/main.c index 246867c..cc2bfa1 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -191,6 +191,7 @@ typedef struct { f64 fade_out; f64 crossfade; f64 base_frequency; + f64 volume; saw_envelope_t envelope; } saw_sampler_t; @@ -356,6 +357,9 @@ static void saw_play_voice(saw_track_t *track, saw_roll_t *roll, saw_voices[n] = saw_voices[n - 1]; switch (track->instrument) { + // FIXME + // Apply volume during sound generation. + case INSTRUMENT_OSCILLATOR: { saw_oscillator_t *osc = &track->oscillator; @@ -375,6 +379,20 @@ static void saw_play_voice(saw_track_t *track, saw_roll_t *roll, }; } break; + case INSTRUMENT_SAMPLER: { + saw_sampler_t *sam = &track->sampler; + + saw_voices[0] = (saw_voice_t) { + .enabled = 1, + .time = 0, + .duration = (f64) duration / (f64) SAMPLE_RATE, + .frequency = saw_pitch_frequency(pitch), + .amplitude = saw_pitch_amplitude(pitch) * sam->volume, + .phase = { 0., 0., }, + .track = roll->track, + }; + } break; + default:; } } @@ -409,6 +427,21 @@ static f64 saw_oscillator(i32 type, f64 frequency, f64 phase, return 0.; } +static f64 saw_sampler(saw_sampler_t *sam, i32 channel, f64 frequency, + i64 t) { + // FIXME + // Implement proper frequency shift. + + i64 i = (i64) floor((t * frequency) / sam->base_frequency + .5) * + 2 + + channel; + + if (i < 0 || i >= sam->data.size) + return 0.; + + return sam->data.values[i]; +} + static void saw_audio_render(void) { // FIXME // Improve performance. @@ -449,6 +482,7 @@ static void saw_audio_render(void) { switch (saw_tracks[roll->track].instrument) { case INSTRUMENT_OSCILLATOR: + case INSTRUMENT_SAMPLER: saw_play_voice(saw_tracks + roll->track, roll, note->pitch, note->duration); break; @@ -461,7 +495,7 @@ static void saw_audio_render(void) { ++saw_playback_frame; } - // Oscillators + // Sound generation // { for (i32 n = 0; n < VOICE_COUNT; n++) { @@ -471,6 +505,9 @@ static void saw_audio_render(void) { saw_track_t *track = saw_tracks + saw_voices[n].track; switch (track->instrument) { + // FIXME + // Unify similar logic for Oscillators and Samplers. + case INSTRUMENT_OSCILLATOR: { saw_oscillator_t *osc = &track->oscillator; @@ -507,6 +544,38 @@ static void saw_audio_render(void) { saw_voices[n].enabled = 0; } break; + case INSTRUMENT_SAMPLER: { + saw_sampler_t *sam = &track->sampler; + + f64 frequency = saw_voices[n].frequency; + f64 amplitude = saw_voices[n].amplitude; + + f64 attack = sam->envelope.attack; + f64 decay = sam->envelope.decay; + f64 sustain = sam->envelope.sustain; + f64 duration = saw_voices[n].duration; + f64 release = sam->envelope.release; + + f64 t = (f64) saw_voices[n].time / (f64) SAMPLE_RATE; + f64 a = amplitude * saw_envelope(t, attack, decay, + 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); + + saw_voices[n].time++; + + if (t > duration + release) + saw_voices[n].enabled = 0; + } break; + default:; } } @@ -1452,6 +1521,7 @@ static void saw_ui_choose_instrument(saw_track_t *track, i64 x0, track->sampler.fade_out = 0.; track->sampler.crossfade = .01; track->sampler.base_frequency = 440.; + track->sampler.volume = .5; track->sampler.envelope = (saw_envelope_t) { .sustain = .15, @@ -1620,7 +1690,10 @@ static void saw_ui_sampler(saw_sampler_t *sampler, i64 x0, i64 y0, 100000, 0., 60., &sampler->crossfade); saw_ui_value_float(x, y0 + height - sample_height - text_height * 7, w, text_height, TINT_WHITE, SZ("Base freq."), - 10000, 1., 44100., &sampler->base_frequency); + 500, 1., 44100., &sampler->base_frequency); + saw_ui_value_float(x, y0 + height - sample_height - text_height * 8, + w, text_height, TINT_WHITE, SZ("Volume."), 10000, + 0., 2., &sampler->volume); nvgFontSize(saw_nvg, text_height); nvgFontFaceId(saw_nvg, saw_font_text); @@ -1628,23 +1701,23 @@ 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 * 17) / 2, + (text_height * 19) / 2, "Envelope", NULL); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 10, + y0 + height - sample_height - text_height * 11, w, text_height, TINT_WHITE, SZ("Sustain"), 10000, 0., 1., &sampler->envelope.sustain); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 11, + y0 + height - sample_height - text_height * 12, w, text_height, TINT_WHITE, SZ("Attack"), 100000, 0., 6., &sampler->envelope.attack); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 12, + y0 + height - sample_height - text_height * 13, w, text_height, TINT_WHITE, SZ("Decay"), 100000, 0., 6., &sampler->envelope.decay); saw_ui_value_float(x, - y0 + height - sample_height - text_height * 13, + y0 + height - sample_height - text_height * 14, w, text_height, TINT_WHITE, SZ("Release"), 100000, 0., 6., &sampler->envelope.release); } @@ -2439,6 +2512,79 @@ static void saw_init(void) { osc->envelope.release = release * 0.0001; } break; + case INSTRUMENT_SAMPLER: { + saw_sampler_t *sam = &track->sampler; + + DA_INIT(sam->data, 0, NULL); + DA_INIT(sam->outline, SAMPLER_OUTLINE_SIZE, NULL); + + if (track->sampler.outline.size != SAMPLER_OUTLINE_SIZE) { + printf("Bad alloc\n"); + fflush(stdout); + track->sampler.outline.size = 0; + } + + i64 data_size; + + SCAN_(" data %lld", 1, &data_size); + + if (data_size > 0) { + DA_RESIZE(sam->data, data_size * CHANNEL_COUNT); + + assert(sam->data.size == data_size * CHANNEL_COUNT); + + if (sam->data.size == data_size * CHANNEL_COUNT) { + for (i64 l, r, i = 0; i < data_size; i++) { + SCAN_(" %lld %lld", 2, &l, &r); + sam->data.values[i * 2] = (f32) (.0001 * l); + sam->data.values[i * 2 + 1] = (f32) (.0001 * r); + } + } else { + printf("Bad alloc\n"); + fflush(stdout); + + for (i64 l, r, i = 0; i < data_size; i++) + SCAN_(" %lld %lld", 2, &l, &r); + } + } + + if (sam->outline.size == SAMPLER_OUTLINE_SIZE) + for (i64 i = 0; i < SAMPLER_OUTLINE_SIZE; i++) { + f32 *v = sam->data.values; + i64 k = i * data_size / SAMPLER_OUTLINE_SIZE; + 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; + + 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); + SCAN_(" sustain %lld", 1, &sustain); + SCAN_(" attack %lld", 1, &attack); + SCAN_(" decay %lld", 1, &decay); + SCAN_(" release %lld", 1, &release); + + 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; + sam->envelope.sustain = sustain * .0001; + sam->envelope.attack = attack * .0001; + sam->envelope.decay = decay * .0001; + sam->envelope.release = release * .0001; + } break; + default: memset(track, 0, sizeof *track); track->instrument = INSTRUMENT_NONE; @@ -2683,21 +2829,57 @@ static void saw_cleanup(void) { fprintf(f, "wave %d\n", osc->wave); fprintf(f, "warp %lld\n", - (i64) ((1. + osc->warp) * 10000 + .5)); + (i64) floor((1. + osc->warp) * 10000 + .5)); fprintf(f, "phase %lld\n", - (i64) (osc->phase * 10000 + .5)); + (i64) floor(osc->phase * 10000 + .5)); fprintf(f, "stereo_width %lld\n", - (i64) (osc->stereo_width * 10000 + .5)); + (i64) floor(osc->stereo_width * 10000 + .5)); fprintf(f, "volume %lld\n", - (i64) (osc->volume * 10000 + .5)); + (i64) floor(osc->volume * 10000 + .5)); fprintf(f, "sustain %lld\n", - (i64) (osc->envelope.sustain * 10000 + .5)); + (i64) floor(osc->envelope.sustain * 10000 + .5)); fprintf(f, "attack %lld\n", - (i64) (osc->envelope.attack * 10000 + .5)); + (i64) floor(osc->envelope.attack * 10000 + .5)); fprintf(f, "decay %lld\n", - (i64) (osc->envelope.decay * 10000 + .5)); + (i64) floor(osc->envelope.decay * 10000 + .5)); fprintf(f, "release %lld\n\n", - (i64) (osc->envelope.release * 10000 + .5)); + (i64) floor(osc->envelope.release * 10000 + .5)); + } break; + + case INSTRUMENT_SAMPLER: { + saw_sampler_t *sam = &track->sampler; + + fprintf(f, "data %lld ", + (i64) sam->data.size / CHANNEL_COUNT); + for (i64 i = 0; i < sam->data.size; i++) + fprintf(f, " %lld", + (i64) floor(sam->data.values[i] * 10000 + .5)); + fprintf(f, "\n"); + + fprintf(f, "begin %lld\n", + (i64) floor(sam->begin * 10000 + .5)); + fprintf(f, "end %lld\n", + (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", + (i64) floor(sam->base_frequency * 10000 + .5)); + fprintf(f, "volume %lld\n", + (i64) floor(sam->volume * 10000 + .5)); + fprintf(f, "sustain %lld\n", + (i64) floor(sam->envelope.sustain * 10000 + .5)); + fprintf(f, "attack %lld\n", + (i64) floor(sam->envelope.attack * 10000 + .5)); + fprintf(f, "decay %lld\n", + (i64) floor(sam->envelope.decay * 10000 + .5)); + fprintf(f, "release %lld\n\n", + (i64) floor(sam->envelope.release * 10000 + .5)); } break; default:; -- cgit v1.2.3