aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sounds.c177
-rw-r--r--src/sounds.h90
2 files changed, 180 insertions, 87 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,
};
}
}
diff --git a/src/sounds.h b/src/sounds.h
index e58ae4f..4b2f78f 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -2,20 +2,20 @@
#define SOUNDS_H_
#include <alsa/asoundlib.h>
+#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
-#include <limits.h>
-#define check(ret) \
- do { \
- int res = (ret); \
- if (res < 0) { \
- fprintf(stderr, "%s:%d ERROR: %s (%d)\n", \
- __FILE__, __LINE__, snd_strerror(res), res); \
- exit(1); \
- } \
- } while (0)
+#define check(ret) \
+ do { \
+ int res = (ret); \
+ if (res < 0) { \
+ fprintf(stderr, "%s:%d ERROR: %s (%d)\n", __FILE__, __LINE__, \
+ snd_strerror(res), res); \
+ exit(1); \
+ } \
+ } while (0)
#define MESSAGE_QUEUE_SIZE 128
#define SAMPLE_RATE 48000
@@ -45,20 +45,19 @@ typedef struct {
synth_message_type type;
union {
- // NOTE_ON / NOTE_OFF
- struct {
- size_t note_id;
- } note;
-
- // SET_PARAM;
- struct {
- param_type param_type;
- float value;
- } param_change;
+ // NOTE_ON / NOTE_OFF
+ struct {
+ size_t note_id;
+ } note;
+
+ // SET_PARAM;
+ struct {
+ param_type param_type;
+ float value;
+ } param_change;
};
} synth_message;
-
typedef struct {
synth_message buffer[MESSAGE_QUEUE_SIZE];
size_t head;
@@ -71,11 +70,30 @@ typedef struct {
message_queue *queue;
} sound_thread_meta;
+typedef enum {
+ ENV_OFF,
+ ENV_ATTACK,
+ ENV_DECAY,
+ ENV_SUSTAIN,
+ ENV_RELEASE,
+} envelope_state;
+
+typedef struct {
+ envelope_state state;
+ int counter;
+ int attack_time;
+ int decay_time;
+ float sustain_level;
+ int release_time;
+ float increases[3];
+} envelope;
+
typedef struct {
bool active;
float freq;
float phase;
float phase_inc;
+ envelope envelope;
} synth_voice;
typedef struct {
@@ -88,30 +106,16 @@ typedef struct {
float master_volume;
} synth_params;
-typedef void (*oscilator_func)(float *buffer, size_t sample_count, float *phase, float phase_inc);
+typedef float (*oscilator_func)(float phase);
-#define mqueue_init(q) \
- do { \
- (q)->head = (q)->tail = 0; \
- pthread_mutex_init(&(q)->lock, NULL); \
- } while (0)
+int mqueue_get(message_queue *q, synth_message *msg);
+int mqueue_push(message_queue *q, synth_message msg);
-
-#define mqueue_push(q, msg) \
- do { \
- pthread_mutex_lock(&(q)->lock); \
- size_t next = ((q)->head + 1) % MESSAGE_QUEUE_SIZE; \
- \
- if ((q)->tail == next) { \
- pthread_mutex_unlock(&(q)->lock); \
- return 1; \
- } \
- \
- (q)->buffer[(q)->head] = msg; \
- (q)->head = next; \
- \
- pthread_mutex_unlock(&(q)->lock); \
- } while (0) \
+#define mqueue_init(q) \
+ do { \
+ (q)->head = (q)->tail = 0; \
+ pthread_mutex_init(&(q)->lock, NULL); \
+ } while (0)
/* #define mqueue_get(q, msg, ok) \ */
/* do { \ */