summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2024-02-15 03:41:45 +0100
committerMitya Selivanov <automainint@guattari.tech>2024-02-15 03:41:45 +0100
commit70dd5c9fe395a3007133e12cddf69f638dcb19e4 (patch)
tree118de510ecc4feeefd12a57eb1d0fc601f06905b
parentd7edc7ee544fbf7fe38d0149afb3563f21f450ff (diff)
downloadsaw-70dd5c9fe395a3007133e12cddf69f638dcb19e4.zip
Sampler: basic sound
-rw-r--r--source/saw/main.c212
1 files 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:;