diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 20 | ||||
| -rw-r--r-- | src/Config.h | 9 | ||||
| -rw-r--r-- | src/Main.c | 103 |
4 files changed, 134 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76781e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +profile.png
\ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f170b69 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +CC = cc +CFLAGS = -Wall -Wextra -O3 $(shell pkg-config --cflags sdl3 sdl3-image libpulse libpulse-simple) +LDFLAGS = $(shell pkg-config --libs sdl3 sdl3-image libpulse libpulse-simple) + +SRC_DIR = src +BIN_DIR = bin + +all: $(BIN_DIR)/pndacc + +$(BIN_DIR)/pndacc: $(SRC_DIR)/Main.c + mkdir -p $(BIN_DIR) + $(CC) $(CFLAGS) -o $@ $(SRC_DIR)/Main.c $(LDFLAGS) + +run: $(BIN_DIR)/pndacc + ./$(BIN_DIR)/pndacc + +clean: + rm -rf $(BIN_DIR) + +.PHONY: all run clean diff --git a/src/Config.h b/src/Config.h new file mode 100644 index 0000000..f74c3a8 --- /dev/null +++ b/src/Config.h @@ -0,0 +1,9 @@ +#define VOLUME_THRESHOLD 400.0f +#define MAX_VOLUME_REF 2500.0f +#define SAMPLE_RATE 44100 +#define BUFFER_SIZE 256 +#define IDLE_BOB_SPEED 0.004f +#define IDLE_BOB_AMP 15.0f +#define IDLE_TILT_AMP 3.5f +#define SQUASH_STRETCH_INTENSITY 0.25f +#define LERP_SPEED 0.25f diff --git a/src/Main.c b/src/Main.c new file mode 100644 index 0000000..d83511a --- /dev/null +++ b/src/Main.c @@ -0,0 +1,103 @@ +#include "Config.h" +#include <SDL3/SDL.h> +#include <SDL3_image/SDL_image.h> +#include <math.h> +#include <pulse/error.h> +#include <pulse/simple.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +SDL_AtomicInt is_speaking; +SDL_AtomicInt app_running; +float shared_rms = 0.0f; + +int SDLCALL audio_thread_func(void *data) { + (void)data; + pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, .rate = SAMPLE_RATE, .channels = 1}; + pa_buffer_attr attr = {.maxlength = -1, + .tlength = -1, + .prebuf = -1, + .minreq = -1, + .fragsize = BUFFER_SIZE * 2}; + int error; + pa_simple *s = pa_simple_new(NULL, "PNDacc", PA_STREAM_RECORD, NULL, "Record", + &ss, NULL, &attr, &error); + if (!s) + return -1; + int16_t buffer[BUFFER_SIZE]; + while (SDL_GetAtomicInt(&app_running)) { + if (pa_simple_read(s, buffer, sizeof(buffer), &error) < 0) + break; + double sum_squares = 0; + for (int i = 0; i < BUFFER_SIZE; i++) + sum_squares += (double)buffer[i] * buffer[i]; + float rms = (float)sqrt(sum_squares / BUFFER_SIZE); + shared_rms = rms; + SDL_SetAtomicInt(&is_speaking, rms > VOLUME_THRESHOLD ? 1 : 0); + } + pa_simple_free(s); + return 0; +} + +int main(void) { + SDL_SetAtomicInt(&app_running, 1); + if (!SDL_Init(SDL_INIT_VIDEO)) + return 1; + SDL_Window *window; + SDL_Renderer *renderer; + if (!SDL_CreateWindowAndRenderer("PNDacc", 600, 600, 0, &window, &renderer)) + return 1; + SDL_Texture *tex = IMG_LoadTexture(renderer, "profile.png"); + if (!tex) + fprintf(stderr, "[ERROR] 'profile.png' not found!\n"); + SDL_Thread *audio_thread = + SDL_CreateThread(audio_thread_func, "AudioThread", NULL); + float visual_rms = 0.0f; + while (SDL_GetAtomicInt(&app_running)) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_EVENT_QUIT) + SDL_SetAtomicInt(&app_running, 0); + } + float target_rms = shared_rms; + if (target_rms < VOLUME_THRESHOLD) + target_rms = 0.0f; + visual_rms += (target_rms - visual_rms) * LERP_SPEED; + SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); + SDL_RenderClear(renderer); + float volume_norm = visual_rms / MAX_VOLUME_REF; + if (volume_norm > 1.0f) + volume_norm = 1.0f; + float stretch_y = 1.0f + (volume_norm * SQUASH_STRETCH_INTENSITY); + float squash_x = 1.0f - (volume_norm * SQUASH_STRETCH_INTENSITY * 0.6f); + uint64_t ticks = SDL_GetTicks(); + float bob = sinf(ticks * IDLE_BOB_SPEED) * IDLE_BOB_AMP; + float tilt = sinf(ticks * (IDLE_BOB_SPEED * 0.7f)) * IDLE_TILT_AMP; + if (tex) { + float img_w, img_h; + SDL_GetTextureSize(tex, &img_w, &img_h); + float scale = 450.0f / (img_h > img_w ? img_h : img_w); + float base_w = img_w * scale; + float base_h = img_h * scale; + float final_w = base_w * squash_x; + float final_h = base_h * stretch_y; + SDL_FRect dst = {300.0f - (final_w / 2.0f), 550.0f - final_h + bob, + final_w, final_h}; + SDL_FPoint origin = {final_w / 2.0f, final_h}; + SDL_RenderTextureRotated(renderer, tex, NULL, &dst, (double)tilt, &origin, + SDL_FLIP_NONE); + } + SDL_RenderPresent(renderer); + SDL_Delay(8); + } + SDL_SetAtomicInt(&app_running, 0); + SDL_WaitThread(audio_thread, NULL); + if (tex) + SDL_DestroyTexture(tex); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} |
