aboutsummaryrefslogtreecommitdiff
path: root/src/sounds.c
diff options
context:
space:
mode:
authorspl3g <spleefer6@yandex.ru>2025-10-02 18:53:07 +0300
committerspl3g <spleefer6@yandex.ru>2025-10-02 19:02:59 +0300
commit06aada0bec32d889c0f0ce6f2ecf193bbece3c73 (patch)
tree20410d05a7c2d0ee6d05dec52f53976009c9e3ce /src/sounds.c
parent5fad25abc6ab0632b8594c84f59fa5c1eec06d2f (diff)
Add envelopes
Diffstat (limited to 'src/sounds.c')
-rw-r--r--src/sounds.c177
1 files changed, 133 insertions, 44 deletions
diff --git a/src/sounds.c b/src/sounds.c
index fe6b3d3..9ca88c1 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -14,48 +14,113 @@ int mqueue_get(message_queue *q, synth_message *msg) {
return 0;
}
-/* osc_triangle_gen */
+int mqueue_push(message_queue *q, synth_message msg) {
+ pthread_mutex_lock(&(q)->lock);
+ size_t next = ((q)->head + 1) % MESSAGE_QUEUE_SIZE;
-void osc_sine_gen(float *buffer, size_t sample_count, float *phase,
- float phase_inc) {
- for (size_t i = 0; i < sample_count; i++) {
- buffer[i] += sinf(*phase);
- *phase += phase_inc;
- if (*phase >= 2 * M_PI)
- *phase -= 2 * M_PI;
+ if ((q)->tail == next) {
+ pthread_mutex_unlock(&(q)->lock);
+ return 1;
}
+
+ (q)->buffer[(q)->head] = msg;
+ (q)->head = next;
+
+ pthread_mutex_unlock(&(q)->lock);
+ return 0;
}
-void osc_square_gen(float *buffer, size_t sample_count, float *phase,
- float phase_inc) {
- for (size_t i = 0; i < sample_count; i++) {
- buffer[i] += sinf(*phase) ? 1 : -1;
- *phase += phase_inc;
- if (*phase >= 2 * M_PI)
- *phase -= 2 * M_PI;
+float envelope_next(envelope *env) {
+ float value;
+ bool next_state = false;
+
+ env->counter++;
+
+ switch (env->state) {
+ case ENV_OFF: {
+ return 0;
+ }
+ case ENV_ATTACK: {
+ if (env->counter >= env->attack_time) {
+ next_state = true;
+ }
+ value = env->increases[0] * (float)env->counter;
+ break;
+ }
+ case ENV_DECAY: {
+ if (env->counter >= env->decay_time) {
+ next_state = true;
+ }
+ value = 1.0 - env->increases[1] * (float)env->counter;
+ break;
+ }
+ case ENV_SUSTAIN: {
+ value = env->sustain_level;
+ break;
+ }
+ case ENV_RELEASE: {
+ if (env->counter >= env->release_time) {
+ env->state = ENV_OFF;
+ env->counter = 0;
+ return 0;
+ }
+ value = env->sustain_level - env->increases[2] * (float)env->counter;
+ break;
+ }
}
-}
-void osc_saw_gen(float *buffer, size_t sample_count, float *phase,
- float phase_inc) {
- for (size_t i = 0; i < sample_count; i++) {
- buffer[i] += (*phase / M_PI) - 1;
- *phase += phase_inc;
- if (*phase >= 2 * M_PI)
- *phase -= 2 * M_PI;
+ if (next_state) {
+ env->counter = 0;
+ env->state++;
}
+ return value;
+}
+
+void envelope_init(envelope *env) {
+ env->state = ENV_OFF;
+ env->counter = 0;
+ env->attack_time = 0.005 * SAMPLE_RATE;
+ env->decay_time = 0.0010 * SAMPLE_RATE;
+ env->sustain_level = 0.7;
+ env->release_time = 1.000 * SAMPLE_RATE;
+
+ env->increases[0] = 1.0 / (float)env->attack_time;
+ env->increases[1] = (1.0 - env->sustain_level) / (float)env->decay_time;
+ env->increases[2] = env->sustain_level / (float)env->release_time;
+}
+
+void envelope_note_on(envelope *env) {
+ env->state = ENV_ATTACK;
+ env->counter = 0;
+}
+
+void envelope_note_off(envelope *env) {
+ env->state = ENV_RELEASE;
+ env->counter = 0;
+}
+
+float osc_sine_get(float phase) {
+ return sinf(phase);
+}
+
+float osc_square_get(float phase) {
+ return sinf(phase) > 0 ? 1 : -1;
+}
+
+float osc_saw_get(float phase) {
+ return (phase / M_PI) - 1;
}
oscilator_func osc_get(oscilator_type type) {
switch (type) {
case OSC_SINE:
- return osc_sine_gen;
+ return osc_sine_get;
case OSC_SAW:
- return osc_saw_gen;
+ return osc_saw_get;
case OSC_SQUARE:
- return osc_square_gen;
+ return osc_square_get;
default:
- return osc_sine_gen;
+ return osc_sine_get;
}
}
@@ -63,6 +128,7 @@ void set_note_on(synth_voices *voices, size_t note_id) {
if (note_id >= voices->size) {
return;
}
+ envelope_note_on(&voices->buffer[note_id].envelope);
voices->buffer[note_id].active = true;
}
@@ -70,7 +136,7 @@ void set_note_off(synth_voices *voices, size_t note_id) {
if (note_id >= voices->size) {
return;
}
- voices->buffer[note_id].active = false;
+ envelope_note_off(&voices->buffer[note_id].envelope);
}
void set_all_notes_off(synth_voices *voices) {
@@ -94,8 +160,7 @@ void set_param(synth_params *params, param_type type, float value) {
}
}
-void generate_voices(synth_voices *voices, synth_params *params, float *buffer,
- size_t buffer_size) {
+void generate_voices(synth_voices *voices, synth_params *params, float *buffer, size_t buffer_size) {
oscilator_func oscilator = osc_get(params->oscilator_type);
for (size_t i = 0; i < voices->size; i++) {
synth_voice *voice = &voices->buffer[i];
@@ -103,10 +168,23 @@ void generate_voices(synth_voices *voices, synth_params *params, float *buffer,
continue;
}
- if (voice->phase_inc == 0) {
- voice->phase_inc = 2 * M_PI * voice->freq / SAMPLE_RATE;
- }
- oscilator(buffer, buffer_size, &voice->phase, voice->phase_inc);
+ if (voice->phase_inc == 0) {
+ voice->phase_inc = 2 * M_PI * voice->freq / SAMPLE_RATE;
+ }
+
+ for (size_t j = 0; j < buffer_size; j++) {
+ float env_value = envelope_next(&voice->envelope);
+ if (env_value == 0) {
+ voice->active = false;
+ break;
+ }
+
+ buffer[j] += oscilator(voice->phase) * env_value;
+
+ voice->phase += voice->phase_inc;
+ if (voice->phase >= 2 * M_PI)
+ voice->phase -= 2 * M_PI;
+ }
}
}
@@ -126,14 +204,12 @@ void prepare_output(float *scratch_buffer, short *output_buffer,
void sound_loop_start(snd_pcm_t *pcm, message_queue *queue,
synth_voices *voices, synth_params *params) {
- bool should_stop = false;
-
short output_buffer[PERIOD_SIZE];
float scratch_buffer[PERIOD_SIZE];
- while (!should_stop) {
+ while (true) {
synth_message msg;
- while (mqueue_get(queue, &msg) == 0 && !should_stop) {
+ while (mqueue_get(queue, &msg) == 0) {
switch (msg.type) {
case MSG_NOTE_ON: {
size_t note_id = msg.note.note_id;
@@ -156,7 +232,7 @@ void sound_loop_start(snd_pcm_t *pcm, message_queue *queue,
break;
}
case MSG_STOP: {
- should_stop = true;
+ goto stop;
}
}
}
@@ -169,21 +245,34 @@ void sound_loop_start(snd_pcm_t *pcm, message_queue *queue,
/* post_process(params, scratch_buffer, PERIOD_SIZE); */
prepare_output(scratch_buffer, output_buffer, PERIOD_SIZE);
- snd_pcm_sframes_t written = snd_pcm_writei(pcm, output_buffer, PERIOD_SIZE);
- if (written < 0) {
- printf("xrun\n");
- snd_pcm_prepare(pcm); // recover from xrun
- }
+ int period_size = PERIOD_SIZE;
+ short *ptr = output_buffer;
+
+ while (period_size > 0) {
+ snd_pcm_sframes_t written = snd_pcm_writei(pcm, ptr, period_size);
+ if (written < 0) {
+ printf("xrun\n");
+ snd_pcm_prepare(pcm); // recover from xrun
+ break;
+ }
+ ptr += written;
+ period_size -= written;
+ }
}
+stop:
+ return;
}
void fill_voices(synth_voice *voices, float *freqs, size_t freqs_amount) {
+ envelope env = {0};
+ envelope_init(&env);
for (size_t i = 0; i < freqs_amount; i++) {
voices[i] = (synth_voice){
.active = false,
.freq = freqs[i],
.phase = 0,
.phase_inc = 0,
+ .envelope = env,
};
}
}