summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2025-10-16 00:47:21 +0300
committergrm <grm@eyesin.space>2025-10-16 00:47:21 +0300
commitf0a57d10dd93580ef0b553b8781185fdc09b2da0 (patch)
treeb96538bf209a55f13aa0fb98f2480d06ece7e58f /src
parent4248bf57800447e1abef618db9da4e0f1291d0f3 (diff)
downloadsynth-project-f0a57d10dd93580ef0b553b8781185fdc09b2da0.tar.gz
synth-project-f0a57d10dd93580ef0b553b8781185fdc09b2da0.tar.bz2
synth-project-f0a57d10dd93580ef0b553b8781185fdc09b2da0.zip
Add spinner for sound picking
Diffstat (limited to 'src')
-rw-r--r--src/os.c255
-rw-r--r--src/os.h7
-rw-r--r--src/osc.h2
-rw-r--r--src/osc_sound.c10
-rw-r--r--src/synth_gui.c206
5 files changed, 479 insertions, 1 deletions
diff --git a/src/os.c b/src/os.c
new file mode 100644
index 0000000..c9f4ee8
--- /dev/null
+++ b/src/os.c
@@ -0,0 +1,255 @@
+#include "os.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+// Function to check if a string ends with a specific suffix (case insensitive)
+int ends_with_wav(const char *str) {
+ size_t len = strlen(str);
+ if (len < 4) return 0;
+
+ const char *suffix = str + len - 4;
+ return (strcasecmp(suffix, ".wav") == 0);
+}
+
+// Function to extract the example name from folder format: "example name[2048-44.1khz-32bit]"
+char* extract_example_name(const char *folder_name) {
+ char *result = malloc(strlen(folder_name) + 1);
+ if (!result) return NULL;
+
+ strcpy(result, folder_name);
+
+ // Find the opening bracket and terminate the string there
+ char *bracket = strchr(result, '[');
+ if (bracket) {
+ *bracket = '\0';
+ }
+
+ // Remove trailing whitespace
+ int len = strlen(result);
+ while (len > 0 && (result[len-1] == ' ' || result[len-1] == '\t')) {
+ result[--len] = '\0';
+ }
+
+ return result;
+}
+
+char* scan_for_wav(const char *directory_path) {
+ DIR *dir;
+ struct dirent *entry;
+ struct stat statbuf;
+ char full_path[1024];
+ char *result = NULL;
+ size_t result_size = 0;
+ size_t result_capacity = 1024;
+ int first_entry = 1;
+
+ // Initialize result buffer
+ result = malloc(result_capacity);
+ if (!result) return NULL;
+ result[0] = '\0';
+
+ // Open directory
+ dir = opendir(directory_path);
+ if (!dir) {
+ free(result);
+ return NULL;
+ }
+
+ // Iterate through directory entries
+ while ((entry = readdir(dir)) != NULL) {
+ // Skip . and .. entries
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
+
+ // Create full path
+ snprintf(full_path, sizeof(full_path), "%s/%s", directory_path, entry->d_name);
+
+ // Check if it's a directory
+ if (stat(full_path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
+ // This is a subdirectory, scan for WAV files in it
+ DIR *subdir;
+ struct dirent *subentry;
+
+ subdir = opendir(full_path);
+ if (!subdir) continue;
+
+ // Extract example name from folder name
+ char *example_name = extract_example_name(entry->d_name);
+ if (!example_name) {
+ closedir(subdir);
+ continue;
+ }
+
+ // Look for WAV files in subdirectory
+ while ((subentry = readdir(subdir)) != NULL) {
+ if (ends_with_wav(subentry->d_name)) {
+ // Calculate needed space for new entry (full path)
+ size_t needed_space = strlen(full_path) + 1 + strlen(subentry->d_name) + 2; // +2 for ';' and '\0'
+ if (!first_entry) needed_space += 1; // for semicolon separator
+
+ // Resize buffer if needed
+ while (result_size + needed_space >= result_capacity) {
+ result_capacity *= 2;
+ char *new_result = realloc(result, result_capacity);
+ if (!new_result) {
+ free(result);
+ free(example_name);
+ closedir(subdir);
+ closedir(dir);
+ return NULL;
+ }
+ result = new_result;
+ }
+
+ // Add separator if not first entry
+ if (!first_entry) {
+ strcat(result, ";");
+ result_size += 1;
+ }
+
+ // Add the full path
+ strcat(result, full_path);
+ strcat(result, "/");
+ strcat(result, subentry->d_name);
+ result_size += strlen(full_path) + 1 + strlen(subentry->d_name);
+
+ first_entry = 0;
+ }
+ }
+
+ free(example_name);
+ closedir(subdir);
+ }
+ }
+
+ closedir(dir);
+
+ // If no WAV files found, return empty string
+ if (result_size == 0) {
+ strcpy(result, "");
+ }
+
+ return result;
+}
+
+
+// Main function to scan directory and return formatted string
+char* scan_for_wav_pretty(const char *directory_path) {
+ DIR *dir;
+ struct dirent *entry;
+ struct stat statbuf;
+ char full_path[1024];
+ char *result = NULL;
+ size_t result_size = 0;
+ size_t result_capacity = 1024;
+ int first_entry = 1;
+
+ // Initialize result buffer
+ result = malloc(result_capacity);
+ if (!result) return NULL;
+ result[0] = '\0';
+
+ // Open directory
+ dir = opendir(directory_path);
+ if (!dir) {
+ free(result);
+ return NULL;
+ }
+
+ // Iterate through directory entries
+ while ((entry = readdir(dir)) != NULL) {
+ // Skip . and .. entries
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
+
+ // Create full path
+ snprintf(full_path, sizeof(full_path), "%s/%s", directory_path, entry->d_name);
+
+ // Check if it's a directory
+ if (stat(full_path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
+ // This is a subdirectory, scan for WAV files in it
+ DIR *subdir;
+ struct dirent *subentry;
+
+ subdir = opendir(full_path);
+ if (!subdir) continue;
+
+ // Extract example name from folder name
+ char *example_name = extract_example_name(entry->d_name);
+ if (!example_name) {
+ closedir(subdir);
+ continue;
+ }
+
+ // Look for WAV files in subdirectory
+ while ((subentry = readdir(subdir)) != NULL) {
+ if (ends_with_wav(subentry->d_name)) {
+ // Calculate needed space for new entry
+ size_t needed_space = strlen(example_name) + 1 + strlen(subentry->d_name) + 2; // +2 for ';' and '\0'
+ if (!first_entry) needed_space += 1; // for semicolon separator
+
+ // Resize buffer if needed
+ while (result_size + needed_space >= result_capacity) {
+ result_capacity *= 2;
+ char *new_result = realloc(result, result_capacity);
+ if (!new_result) {
+ free(result);
+ free(example_name);
+ closedir(subdir);
+ closedir(dir);
+ return NULL;
+ }
+ result = new_result;
+ }
+
+ // Add separator if not first entry
+ if (!first_entry) {
+ strcat(result, ";");
+ result_size += 1;
+ }
+
+ // Add the formatted entry
+ strcat(result, example_name);
+ strcat(result, "/");
+ strcat(result, subentry->d_name);
+ result_size += strlen(example_name) + 1 + strlen(subentry->d_name);
+
+ first_entry = 0;
+ }
+ }
+
+ free(example_name);
+ closedir(subdir);
+ }
+ }
+
+ closedir(dir);
+
+ // If no WAV files found, return empty string
+ if (result_size == 0) {
+ strcpy(result, "");
+ }
+
+ return result;
+}
+
+/* // Example usage */
+/* int main() { */
+/* const char *test_dir = "/path/to/your/directory"; */
+/* char *result = scan_wav_directory(test_dir); */
+
+/* if (result) { */
+/* printf("Found WAV files: %s\n", result); */
+/* free(result); */
+/* } else { */
+/* printf("Error scanning directory or no WAV files found.\n"); */
+/* } */
+
+/* return 0; */
+/* } */
diff --git a/src/os.h b/src/os.h
new file mode 100644
index 0000000..78617bc
--- /dev/null
+++ b/src/os.h
@@ -0,0 +1,7 @@
+#ifndef OS_H
+#define OS_H
+
+char* scan_for_wav_pretty(const char *directory_path);
+char* scan_for_wav(const char *directory_path);
+
+#endif /* OS_H */
diff --git a/src/osc.h b/src/osc.h
index 9cd0472..ef85225 100644
--- a/src/osc.h
+++ b/src/osc.h
@@ -57,6 +57,8 @@ int osc_next_index(osc_t * osc, float offset);
int osc_load_wav(osc_t * osc, const char * path);
+void osc_sound_change_wavetable(char *path);
+
osc_t * make_tri(const char * name);
/***************/
diff --git a/src/osc_sound.c b/src/osc_sound.c
index 10da73b..fc06a26 100644
--- a/src/osc_sound.c
+++ b/src/osc_sound.c
@@ -34,6 +34,16 @@ osc_sound(float offset)
OSC_sound.data[osc_next_index(&OSC_sound, offset)]);
}
+void
+osc_sound_change_wavetable(char *path)
+{
+ float * old_data = OSC_sound.data;
+ osc_load_wav(&OSC_sound, path);
+ OSC_sound.start = wvt_size*0;
+ OSC_sound.len = OSC_sound.start + wvt_size;
+ free(old_data);
+}
+
float
osc_sound_next(float f, float offset)
{
diff --git a/src/synth_gui.c b/src/synth_gui.c
index 62a6ab7..3fa2536 100644
--- a/src/synth_gui.c
+++ b/src/synth_gui.c
@@ -3,6 +3,7 @@
#include <portaudio.h>
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
+#include "os.h"
//#include "raylib.h"
void
@@ -895,6 +896,205 @@ draw_audiomidisetup(synth_t *synth)
}
}
+char** split_string(const char* str, char delimiter, int* itemCount) {
+ if (!str || !itemCount) {
+ if (itemCount) *itemCount = 0;
+ return NULL;
+ }
+
+ // First pass: count delimiters to determine array size
+ int count = 1;
+ for (int i = 0; str[i]; i++) {
+ if (str[i] == delimiter) {
+ count++;
+ }
+ }
+
+ // Allocate array of string pointers
+ char** result = malloc(count * sizeof(char*));
+ if (!result) {
+ *itemCount = 0;
+ return NULL;
+ }
+
+ // Create a working copy of the string
+ char* str_copy = malloc(strlen(str) + 1);
+ if (!str_copy) {
+ free(result);
+ *itemCount = 0;
+ return NULL;
+ }
+ strcpy(str_copy, str);
+
+ // Second pass: split the string
+ int index = 0;
+ char* token = str_copy;
+ char* next_delim;
+
+ while ((next_delim = strchr(token, delimiter)) != NULL) {
+ *next_delim = '\0'; // Replace delimiter with null terminator
+
+ // Allocate memory for this substring and copy it
+ result[index] = malloc(strlen(token) + 1);
+ if (result[index]) {
+ strcpy(result[index], token);
+ }
+ index++;
+ token = next_delim + 1; // Move to next token
+ }
+
+ // Handle the last token (after the last delimiter or the whole string if no delimiters)
+ result[index] = malloc(strlen(token) + 1);
+ if (result[index]) {
+ strcpy(result[index], token);
+ }
+
+ free(str_copy);
+ *itemCount = count;
+ return result;
+}
+
+// Function to free the memory allocated by split_string
+void free_split_result(char** result, int itemCount) {
+ if (result) {
+ for (int i = 0; i < itemCount; i++) {
+ free(result[i]);
+ }
+ free(result);
+ }
+}
+
+int
+gui_string_spinner(Rectangle rect, char * text, int * index)
+{
+ int itemCount = 0;
+ char **items = NULL;
+ int change = 0;
+
+ float m = GetMouseWheelMove();
+ int x = 0;
+ if (m < 0) x = -1;
+ else if (m > 0) x = 1;
+ Vector2 p = GetMousePosition();
+
+ if (text != NULL) items = split_string(text, ';', &itemCount);
+
+ if (CheckCollisionPointRec(p, rect)) {
+ if (x > 0) {
+ if (IsKeyDown(KEY_LEFT_SHIFT)) {
+ (*index)+=10;
+ } else {
+ (*index)++;
+ }
+ if (*index >= itemCount) {
+ *index = itemCount - 1; // set to itemCound to loop
+ }
+ change = 1;
+ } else if (x < 0) {
+ if (IsKeyDown(KEY_LEFT_SHIFT)) {
+ (*index)-=10;
+ } else {
+ (*index)--;
+ }
+ if (*index <= -1) {
+ *index = 0; // set to itemCound to loop
+ }
+ change = 1;
+ }
+ }
+
+ if (GuiButton((Rectangle){rect.x, rect.y, rect.height, rect.height}, "<")) {
+ (*index)--;
+ if (*index <= -1) {
+ *index = 0; // set to itemCound to loop
+ }
+ change = 1;
+ }
+ float box_x = rect.x + (rect.height + 1);
+ float box_width = rect.width - (rect.height + 1) - (rect.height + 1);
+ DrawRectangleLines(box_x, rect.y, box_width, rect.height, WHITE);
+
+ int text_size = 10;
+
+ char buf[1024] = "";
+
+ sprintf(buf, "%d: %s", *index, items[*index]);
+ DrawText(buf,
+ box_x + box_width/2 - (float)MeasureText(buf, text_size) / 2,
+ rect.y + rect.height / 2 - (float)text_size / 2,
+ text_size,
+ WHITE);
+
+ if (GuiButton((Rectangle){rect.x + (rect.height + 1) + rect.width - (rect.height + 1) - (rect.height + 1), rect.y, rect.height, rect.height}, ">")) {
+ (*index)++;
+ if (*index >= itemCount) {
+ *index = itemCount - 1; // set to itemCound to loop
+ }
+ change = 1;
+ }
+
+ free_split_result(items, itemCount);
+
+ return change;
+}
+
+int prev_active = -1;
+
+int sound_file_picker_initialized = 0;
+char * wavs = NULL;
+char * wavs_full = NULL;
+
+void
+draw_sound_file_picker(Rectangle rect)
+{
+ static int scroll_index = 0; // Current scroll position
+ static int active_item = 0; // Currently selected item (-1 means none selected)
+
+ if (!sound_file_picker_initialized) {
+ wavs = scan_for_wav_pretty("/home/grm/code/synth-project-b/waves/Free Wavetables[2048]");
+ //wavs[strlen(wavs) - 1] = '\0'; // remove last ;
+ wavs_full = scan_for_wav("/home/grm/code/synth-project-b/waves/Free Wavetables[2048]");
+ //wavs_full[strlen(wavs_full) - 1] = '\0';
+ /* printf("strlen wavs: %ld\n", strlen(wavs)); */
+ /* printf("strlen wavs_full: %ld\n", strlen(wavs_full)); */
+ sound_file_picker_initialized = 1;
+ }
+
+ /* char aaa[10000] = ""; */
+ /* char *t = aaa; */
+ /* int i = 0; */
+ /* for (int j =0; j < 1000; j++) { */
+ /* sprintf(t, "%d;", i++); */
+ /* t += strlen(t); */
+ /* } */
+ /* aaa[strlen(aaa) - 1] = '\0'; */
+
+ if (gui_string_spinner(rect, wavs, &scroll_index)) {
+ char path[1024] = "";
+ get_nth_entry(wavs_full, scroll_index, path);
+ printf("changing wavetable to: %s\n", path);
+ osc_sound_change_wavetable(path);
+ }
+ prev_active = active_item;
+ //active_item = GuiListView(rect, aaa, &scroll_index, active_item);
+ /* if (prev_active != active_item && active_item != -1) { */
+ /* char path[1024] = ""; */
+ /* get_nth_entry(wavs_full, active_item, path); */
+ /* printf("changing wavetable to: %s\n", path); */
+ /* osc_sound_change_wavetable(path); */
+ /* // sound changed */
+ /* } */
+ /* if (GuiDropdownBox((Rectangle){WIDTH - 300 - 50, */
+ /* 12 + 24 + 6, 300, */
+ /* 24}, */
+ /* wavs, &wav_pick, edit_wav)) { */
+ /* edit_wav = !edit_wav; */
+ /* } */
+ //free(wavs);
+ //free(wavs_full);
+
+}
+
void
draw_main(synth_t *synth)
{
@@ -985,6 +1185,10 @@ draw_main(synth_t *synth)
// signals
draw_signals(synth, 20, 390, WIDTH - 2*20, 200);
+
+ if (synth->geni == 4)
+ draw_sound_file_picker((Rectangle){ WIDTH / 2.0 - 128, 150 + 122, 256 + 60, 24 });
+
//draw_signals(synth, 300, 390, WIDTH - 2*300, 200);
//DrawText("THE SYNTH!!!!!!!!!!!!!!!!!!1", WIDTH / 2 - 100, 50, 20, LIGHTGRAY);
@@ -1049,7 +1253,7 @@ rayrun(synth_t *synth){
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(BLACK);
-
+
if (synth->gui.screen == SCREEN_MAIN)
draw_main(synth);
else if (synth->gui.screen == SCREEN_AUDIOMIDISETUP)