diff options
| author | spl3g <spleefer6@yandex.ru> | 2025-10-02 18:53:07 +0300 |
|---|---|---|
| committer | spl3g <spleefer6@yandex.ru> | 2025-10-02 19:02:59 +0300 |
| commit | 06aada0bec32d889c0f0ce6f2ecf193bbece3c73 (patch) | |
| tree | 20410d05a7c2d0ee6d05dec52f53976009c9e3ce /src/sounds.c | |
| parent | 5fad25abc6ab0632b8594c84f59fa5c1eec06d2f (diff) | |
Add envelopes
Diffstat (limited to 'src/sounds.c')
| -rw-r--r-- | src/sounds.c | 177 |
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, }; } } |
