From 47349d3bf7a0c5207a0359abcc952daf0da278b9 Mon Sep 17 00:00:00 2001 From: sandyx Date: Mon, 23 Sep 2024 12:35:16 -0500 Subject: [PATCH] sound --- makefile | 26 +++++++ src/main.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100755 makefile create mode 100644 src/main.c diff --git a/makefile b/makefile new file mode 100755 index 0000000..877e45d --- /dev/null +++ b/makefile @@ -0,0 +1,26 @@ +SHELL=/bin/sh +CC=gcc + +APP_NAME=synthesizer + +SRC=src +BUILD=build + +C_FILES := $(shell find $(SRC) -name '*.c') +O_FILES += $(patsubst $(SRC)/%.c, $(BUILD)/%.o, $(C_FILES)) + +CFLAGS := -O0 -march=native -std=gnu99 +INCLUDE := +LIB := -lm -lraylib + + +synthesizer: $(APP_NAME) + +$(APP_NAME): $(O_FILES) + $(CC) $(CFLAGS) $(O_FILES) -o $(APP_NAME) $(LIB) + +$(BUILD)/%.o: $(SRC)/%.c + $(CC) $(INCLUDE) $(CFLAGS) -c $< -o $@ + +clean: + rm $(BUILD)/*.o diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..baed5a6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NOTES 26 + +const int window_width = 640; +const int window_height = 480; +char *window_title = "synthesizer"; + +//44100 samples per second +const int sample_rate = 44100; +float duration = 1.0f; +float pcm_volume = 10000.0f; +const float pitch_standard = 440.0; //A440 +int active_notes = 0; + +//convert semitone starting from pitch_standard to frequency +float semitone(float semitone) { + float a = powf(2.0f, (1.0f/12.0f)); + float n = pitch_standard * powf(a, semitone); + return n; +} + +typedef struct note { + float frequency; + float index; + float step; + float volume; + bool released; +} note; + +note scale[MAX_NOTES]; + +float sine_wave(float idx) { + return sinf(2 * PI * idx); +} + +float square_wave(float idx) { + if ( sine_wave(idx) > 0.0f ) + return 0.5f; + + if ( sine_wave(idx) < 0.0f ) + return -0.5f; +} + +static inline int count_active_notes(void) { + int active = 0; + + for (size_t i = 0; i < MAX_NOTES; i++) { + if (scale[i].volume > 0.0f) + active++; + } + + return active; +} + +static inline float add_notes(void) { + //add each notes index + float m = 0; + int active = count_active_notes(); + + for (size_t i = 0; i < MAX_NOTES; i++) { + m += square_wave(scale[i].index) * scale[i].volume; + } + + //don't divide by zero + + m = active ? m/active : 0.0f; //compression + //m = m + (1.0f - m) * 0.95f; + return m; +} + +static inline float calc_note_step(note *n) { + return n->frequency/sample_rate; +} + +static inline void reset_index(void) { + for (size_t i = 0; i < MAX_NOTES; i++) { + + scale[i].index += scale[i].step; + if (scale[i].index > 1.0f && scale[i].volume > 0.0f) { + scale[i].index -= 1.0f; + } + } +} + +void AudioInputCallback2(void *buffer, uint32_t frames) { + //audio_frequency = frequency + (audio_frequency - frequency) * 0.95f; + short *d = (short *)buffer; + + for (size_t i = 0; i < frames; i++) { + d[i] = (signed short)(pcm_volume * add_notes());; + reset_index(); + } + +} + +const float volume_release_step = 0.01f; + +void key_press(int key, note *n) { + if (IsKeyPressed(key)) { + //keys_pressed++; + n->released = false; + } + + if (IsKeyReleased(key)) { + //keys_pressed--; + n->released = true; + } + + //printf("%s\n", "wow"); +} + +int main(int argc, char *argv[]) { + InitWindow(window_width, window_height, window_title); + SetTargetFPS(60); + + InitAudioDevice(); + SetAudioStreamBufferSizeDefault(4096); + + AudioStream stream = LoadAudioStream(44100, 16, 1); + SetAudioStreamCallback(stream, AudioInputCallback2); + + PlayAudioStream(stream); + + const float volume_attack_step = 0.08f; + const float volume_release_step = 0.08f; + + //init scale + for (int i = 0; i < MAX_NOTES; i++) { + scale[i].frequency = semitone(i-24); + scale[i].index = 0.0f; + scale[i].volume = 0.0f; + scale[i].step = calc_note_step(&scale[i]); + scale[i].released = true; + } + + while(!WindowShouldClose()) { + key_press(KEY_Z, &scale[0]); + key_press(KEY_X, &scale[1]); + key_press(KEY_C, &scale[2]); + key_press(KEY_V, &scale[3]); + key_press(KEY_B, &scale[4]); + key_press(KEY_N, &scale[5]); + key_press(KEY_M, &scale[6]); + key_press(KEY_A, &scale[7]); + key_press(KEY_S, &scale[8]); + key_press(KEY_D, &scale[9]); + key_press(KEY_F, &scale[10]); + key_press(KEY_G, &scale[11]); + key_press(KEY_H, &scale[12]); + key_press(KEY_J, &scale[13]); + key_press(KEY_K, &scale[14]); + key_press(KEY_L, &scale[15]); + key_press(KEY_Q, &scale[16]); + key_press(KEY_W, &scale[17]); + key_press(KEY_E, &scale[18]); + key_press(KEY_R, &scale[19]); + key_press(KEY_T, &scale[20]); + key_press(KEY_Y, &scale[21]); + key_press(KEY_U, &scale[22]); + key_press(KEY_I, &scale[23]); + key_press(KEY_O, &scale[24]); + key_press(KEY_P, &scale[25]); + + for (int i = 0; i < MAX_NOTES; i++) { + if (scale[i].released) { + scale[i].volume -= volume_release_step; + if (scale[i].volume < 0.0f) { + scale[i].volume = 0.0f; + scale[i].index = 0.0f; + } + } + } + + for (int i = 0; i < MAX_NOTES; i++) { + if (!scale[i].released) { + scale[i].volume += volume_attack_step; + if (scale[i].volume > 1.0f) { + scale[i].volume = 1.0f; + } + } + } + + + ClearBackground(BLACK); + + BeginDrawing(); + + EndDrawing(); + } + + CloseWindow(); + return 0; +} +