aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/defines.h2
-rw-r--r--src/main.c35
-rw-r--r--src/sounds.c150
-rw-r--r--src/sounds.h45
4 files changed, 132 insertions, 100 deletions
diff --git a/src/defines.h b/src/defines.h
index 88e5c46..3d4bc31 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -4,7 +4,7 @@
#define MESSAGE_QUEUE_SIZE 128
#define SAMPLE_RATE 48000
#define PERIOD_SIZE 480
-#define DISPLAY_SAMPLES 960
+#define DISPLAY_SAMPLES (PERIOD_SIZE * 2)
static const int SCREEN_FPS = 60;
static const int SCREEN_TICKS_PER_FRAME = 1000 / SCREEN_FPS;
diff --git a/src/main.c b/src/main.c
index f254074..1b398d1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -223,7 +223,7 @@ int init_ui(app_state *state) {
return 0;
}
-KeyState keys[12] = {
+static KeyState keys[] = {
{'Z', SDL_SCANCODE_Z, 0, 0}, {'S', SDL_SCANCODE_S, 0, 0},
{'X', SDL_SCANCODE_X, 0, 0}, {'D', SDL_SCANCODE_D, 0, 0},
{'C', SDL_SCANCODE_C, 0, 0},
@@ -231,6 +231,14 @@ KeyState keys[12] = {
{'B', SDL_SCANCODE_B, 0, 0}, {'H', SDL_SCANCODE_H, 0, 0},
{'N', SDL_SCANCODE_N, 0, 0}, {'J', SDL_SCANCODE_J, 0, 0},
{'M', SDL_SCANCODE_M, 0, 0},
+
+ {'Q', SDL_SCANCODE_Q, 0, 0}, {'2', SDL_SCANCODE_2, 0, 0},
+ {'W', SDL_SCANCODE_W, 0, 0}, {'3', SDL_SCANCODE_3, 0, 0},
+ {'E', SDL_SCANCODE_E, 0, 0},
+ {'R', SDL_SCANCODE_R, 0, 0}, {'6', SDL_SCANCODE_5, 0, 0},
+ {'T', SDL_SCANCODE_T, 0, 0}, {'7', SDL_SCANCODE_6, 0, 0},
+ {'Y', SDL_SCANCODE_Y, 0, 0}, {'8', SDL_SCANCODE_7, 0, 0},
+ {'U', SDL_SCANCODE_U, 0, 0},
};
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
@@ -241,7 +249,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
memset(state, 0, sizeof(app_state));
*appstate = state;
state->keys = keys;
- state->keys_amount = 12;
+ state->keys_amount = sizeof(keys)/sizeof(keys[0]);
if (init_ui(state) != 0) {
printf("Couldn't initialize UI: %s", SDL_GetError());
@@ -255,6 +263,8 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
return SDL_APP_CONTINUE;
}
+float old_wave_buffer[DISPLAY_SAMPLES];
+
SDL_AppResult SDL_AppIterate(void *appstate) {
app_state *state = appstate;
Arena *arena = &state->frame_arena;
@@ -279,18 +289,17 @@ SDL_AppResult SDL_AppIterate(void *appstate) {
}
}
- // clamp cycle length if it goes past the end
- size_t cycle_len = SAMPLE_RATE / state->wave_data.freq;
- size_t display_len = cycle_len;
- if (buffer_start + cycle_len > DISPLAY_SAMPLES) {
- display_len = DISPLAY_SAMPLES - buffer_start;
+ if (buffer_start < 100) {
+ ui_data->wave_buffer = &wave_buffer[buffer_start];
+ ui_data->wave_buffer_size = DISPLAY_SAMPLES - 100;
+ memcpy(old_wave_buffer, &wave_buffer[buffer_start], (DISPLAY_SAMPLES - 100) * sizeof(float));
+ } else {
+ ui_data->wave_buffer = old_wave_buffer;
+ ui_data->wave_buffer_size = DISPLAY_SAMPLES - 100;
}
-
- ui_data->wave_buffer = &wave_buffer[buffer_start];
- ui_data->wave_buffer_size = display_len;
ui_data->keys = state->keys;
- ui_data->keys_amount = 12;
+ ui_data->keys_amount = state->keys_amount;
ui_data->knob_settings = &state->knob_settings;
ui_data->scale = dimensions.width / DEFAULT_DIMENSIONS_WIDTH;
@@ -332,7 +341,7 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
return SDL_APP_SUCCESS;
}
case SDL_EVENT_KEY_DOWN: {
- for (int i = 0; i < 12; i++) {
+ for (size_t i = 0; i < state->keys_amount; i++) {
if (event->key.scancode == state->keys[i].keycode) {
if (state->keys[i].keyboard_pressed) {
break;
@@ -349,7 +358,7 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
}
case SDL_EVENT_KEY_UP: {
- for (int i = 0; i < 12; i++) {
+ for (size_t i = 0; i < state->keys_amount; i++) {
if (event->key.scancode == state->keys[i].keycode) {
if (!state->keys[i].keyboard_pressed) {
break;
diff --git a/src/sounds.c b/src/sounds.c
index 904b76b..89da2b0 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -1,8 +1,60 @@
#define MIDI_FREQS_LIST
#include "sounds.h"
+typedef enum {
+ ENV_OFF,
+ ENV_ATTACK,
+ ENV_DECAY,
+ ENV_SUSTAIN,
+ ENV_RELEASE,
+} EnvelopeState;
+
+typedef struct {
+ int attack_time;
+ int decay_time;
+ float sustain_level;
+ int release_time;
+} EnvelopeParams;
+
+typedef struct {
+ EnvelopeState state;
+ int counter;
+ float current_inc;
+ float current_value;
+ EnvelopeParams params;
+} Envelope;
+
+typedef struct {
+ bool active;
+ float freq;
+ float phase;
+ float phase_inc;
+ Envelope envelope;
+} SynthVoice;
+
+typedef struct {
+ SynthVoice *buffer;
+ size_t size;
+} SynthVoices;
+
+typedef struct {
+ OscilatorType oscilator_type;
+ float master_volume;
+ EnvelopeParams envelope_params;
+} SynthParams;
+
+typedef struct {
+ SynthParams params;
+ SynthVoice *last_voice;
+
+ MessageQueue *queue;
+ WaveData *wave_data;
+ SynthVoices *voices;
+} SynthState;
+
+typedef float (*OscilatorFunc)(float phase);
+
float envelope_next(Envelope *env) {
- float value;
bool next_state = false;
env->counter++;
@@ -17,10 +69,10 @@ float envelope_next(Envelope *env) {
}
if (env->current_inc == 0) {
- env->current_inc = (1.0 - env->release_value) / (float)env->params.attack_time;
+ env->current_inc = (1.0 - env->current_value) / (float)env->params.attack_time;
}
- value = env->release_value + env->current_inc * (float)env->counter;
+ env->current_value += env->current_inc;
break;
}
case ENV_DECAY: {
@@ -32,26 +84,31 @@ float envelope_next(Envelope *env) {
env->current_inc = (1.0 - env->params.sustain_level) / (float)env->params.decay_time;
}
- value = 1.0 - env->current_inc * (float)env->counter;
+ env->current_value -= env->current_inc;
break;
}
case ENV_SUSTAIN: {
- value = env->params.sustain_level;
+ env->current_value = env->params.sustain_level;
break;
}
case ENV_RELEASE: {
if (env->counter >= env->params.release_time) {
env->state = ENV_OFF;
- env->counter = env->current_inc = 0;
+ env->counter = env->current_inc = env->current_value = 0;
return 0;
}
if (env->current_inc == 0) {
- env->current_inc = env->params.sustain_level / (float)env->params.release_time;
+ float from_level;
+ if (env->params.sustain_level > env->current_value) {
+ from_level = (env->params.sustain_level - env->current_value);
+ }else {
+ from_level = env->current_value;
+ }
+ env->current_inc = from_level / (float)env->params.release_time;
}
- value = env->params.sustain_level - env->current_inc * (float)env->counter;
- env->release_value = value;
+ env->current_value -= env->current_inc;
break;
}
}
@@ -60,18 +117,20 @@ float envelope_next(Envelope *env) {
env->counter = env->current_inc = 0;
env->state++;
}
- return value;
+ return env->current_value;
}
void envelope_note_on(EnvelopeParams params, Envelope *env) {
env->state = ENV_ATTACK;
env->counter = 0;
+ env->current_inc = 0;
env->params = params;
}
void envelope_note_off(Envelope *env) {
env->state = ENV_RELEASE;
env->counter = 0;
+ env->current_inc = 0;
}
float osc_sine_get(float phase) {
@@ -105,7 +164,6 @@ void set_note_on(SynthParams *params, SynthVoices *voices, size_t note_id) {
}
envelope_note_on(params->envelope_params, &voices->buffer[note_id].envelope);
voices->buffer[note_id].active = true;
- params->last_freq = voices->buffer[note_id].freq;
}
void set_note_off(SynthVoices *voices, size_t note_id) {
@@ -210,8 +268,28 @@ void prepare_output(float *scratch_buffer, short *output_buffer,
}
}
-void sound_loop_start(snd_pcm_t *pcm, MessageQueue *queue, WaveData *wave_data, SynthVoices *voices) {
- SynthParams params;
+void play_output_buffer(snd_pcm_t *pcm, short *output_buffer) {
+ 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;
+ }
+}
+
+void sound_loop(snd_pcm_t *pcm, SynthState *state) {
+ MessageQueue *queue = state->queue;
+ WaveData *wave_data = state->wave_data;
+ SynthVoices *voices = state->voices;
+ SynthParams params = state->params;
+
short output_buffer[PERIOD_SIZE];
float scratch_buffer[PERIOD_SIZE];
float display_buffer[DISPLAY_SAMPLES];
@@ -224,6 +302,7 @@ void sound_loop_start(snd_pcm_t *pcm, MessageQueue *queue, WaveData *wave_data,
case MSG_NOTE_ON: {
size_t note_id = msg.note.note_id;
set_note_on(&params, voices, note_id);
+ state->last_voice = &voices->buffer[note_id];
break;
}
case MSG_NOTE_OFF: {
@@ -255,33 +334,15 @@ void sound_loop_start(snd_pcm_t *pcm, MessageQueue *queue, WaveData *wave_data,
post_process(&params, scratch_buffer, PERIOD_SIZE);
prepare_output(scratch_buffer, output_buffer, PERIOD_SIZE);
- if (display_write_pos + PERIOD_SIZE > DISPLAY_SAMPLES) {
- size_t diff = DISPLAY_SAMPLES - display_write_pos;
- memcpy(&display_buffer[display_write_pos], scratch_buffer, diff * sizeof(float));
- memcpy(display_buffer, &scratch_buffer[diff], (PERIOD_SIZE - diff) * sizeof(float));
- } else {
- memcpy(&display_buffer[display_write_pos], scratch_buffer, PERIOD_SIZE * sizeof(float));
- }
-
+ memcpy(&display_buffer[display_write_pos], scratch_buffer, PERIOD_SIZE * sizeof(float));
display_write_pos = (display_write_pos + PERIOD_SIZE) % DISPLAY_SAMPLES;
- memcpy(wave_data->buffers[wave_data->write_index], display_buffer, DISPLAY_SAMPLES * sizeof(float));
- atomic_store(&wave_data->write_index, 1 - atomic_load(&wave_data->write_index));
- wave_data->freq = params.last_freq;
-
- 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;
+ if (display_write_pos == 0) {
+ memcpy(wave_data->buffers[atomic_load(&wave_data->write_index)], display_buffer, DISPLAY_SAMPLES * sizeof(float));
+ atomic_store(&wave_data->write_index, 1 - atomic_load(&wave_data->write_index));
}
+
+ play_output_buffer(pcm, output_buffer);
}
stop:
return;
@@ -302,15 +363,22 @@ void fill_voices(SynthVoice *voices, MidiNote *freqs, size_t freqs_amount) {
void *sound_thread_start(void *ptr) {
SoundThreadMeta *meta = ptr;
- SynthVoice buffer[12];
- fill_voices(buffer, &midi_freqs[49], 12);
+ SynthVoice buffer[24];
+ fill_voices(buffer, &midi_freqs[48], 24);
SynthVoices voices = {
.buffer = buffer,
- .size = 12,
+ .size = 24,
+ };
+
+ SynthState state = {
+ .params = {0},
+ .queue = meta->queue,
+ .wave_data = meta->wave_data,
+ .voices = &voices,
};
- sound_loop_start(meta->pcm, meta->queue, meta->wave_data, &voices);
+ sound_loop(meta->pcm, &state);
check(snd_pcm_drop(meta->pcm));
return NULL;
diff --git a/src/sounds.h b/src/sounds.h
index 639ce80..0a61487 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -27,51 +27,6 @@ typedef struct {
WaveData *wave_data;
} SoundThreadMeta;
-typedef enum {
- ENV_OFF,
- ENV_ATTACK,
- ENV_DECAY,
- ENV_SUSTAIN,
- ENV_RELEASE,
-} EnvelopeState;
-
-typedef struct {
- int attack_time;
- int decay_time;
- float sustain_level;
- int release_time;
-} EnvelopeParams;
-
-typedef struct {
- EnvelopeState state;
- int counter;
- float current_inc;
- float release_value;
- EnvelopeParams params;
-} Envelope;
-
-typedef struct {
- bool active;
- float freq;
- float phase;
- float phase_inc;
- Envelope envelope;
-} SynthVoice;
-
-typedef struct {
- SynthVoice *buffer;
- size_t size;
-} SynthVoices;
-
-typedef struct {
- OscilatorType oscilator_type;
- float master_volume;
- EnvelopeParams envelope_params;
- float last_freq;
-} SynthParams;
-
-typedef float (*OscilatorFunc)(float phase);
-
void *sound_thread_start(void *ptr);
int set_hw_params(snd_pcm_t *pcm);