diff options
| author | yuzu-eva <cafebabe@disroot.org> | 2025-04-23 11:21:04 +0200 |
|---|---|---|
| committer | yuzu-eva <cafebabe@disroot.org> | 2025-04-23 11:21:04 +0200 |
| commit | a029d434b3f67c37bb4663be8e9b4d6623a32955 (patch) | |
| tree | 44872c7797ff3b2b27a53ead3f9ca4ce80b5b30c /src/plug.c | |
| parent | 083dc7097143385e55ca14372194da7e05fb8c19 (diff) | |
restructure project
Diffstat (limited to 'src/plug.c')
| -rw-r--r-- | src/plug.c | 390 |
1 files changed, 0 insertions, 390 deletions
diff --git a/src/plug.c b/src/plug.c deleted file mode 100644 index f67382d..0000000 --- a/src/plug.c +++ /dev/null @@ -1,390 +0,0 @@ -#include <stdio.h> -#include <math.h> -#include <assert.h> -#include <string.h> -#include <stdlib.h> -#include <complex.h> -#include <raylib.h> -#include <rlgl.h> - -#include "plug.h" - -#define SAMPLE_SIZE (1<<13) -#define FONT_SIZE 69 -#define SMOOTHNESS 8 -#define SMEARNESS 6 - -typedef struct { - Music music; - float audio_volume; - bool error; - Font font; - Shader circle; - Shader smear; - char render_option; -} Plug; - -Plug *plug = NULL; - -float in_raw[SAMPLE_SIZE]; -float in_win[SAMPLE_SIZE]; -float complex out_raw[SAMPLE_SIZE]; -float out_log[SAMPLE_SIZE]; -float out_smooth[SAMPLE_SIZE]; -float out_smear[SAMPLE_SIZE]; - -// char render_option = 'b'; - -void fft(float in[], size_t stride, float complex out[], size_t n) -{ - assert(n > 0); - - if (n == 1) { - out[0] = in[0]; - return; - } - - fft(in, stride*2, out, n/2); // even - fft(in + stride, stride*2, out + n/2, n/2); // odd - - // v = o*x - // out = e + o*x e + o*x e e | e - o*x e - o*x o o - for (size_t k = 0; k < n/2; ++k) { - float t = (float)k/n; - float complex v = cexp(-2*I*PI*t)*out[k + n/2]; - float complex e = out[k]; - out[k] = e + v; - out[k + n/2] = e - v; - } -} - -float amp(float complex z) -{ - float a = cabsf(z); - return 2*log10f(a); -} - -void callback(void *bufferData, unsigned int frames) -{ - float (*fs)[2] = bufferData; - - for (size_t i = 0; i < frames; ++i) { - memmove(in_raw, in_raw + 1, (SAMPLE_SIZE - 1)*sizeof(in_raw[0])); - in_raw[SAMPLE_SIZE-1] = fs[i][0]; - } -} - -void PausePlayMusic(void) -{ - if (IsMusicStreamPlaying(plug->music)) { - PauseMusicStream(plug->music); - printf("Music paused\n"); - } else { - ResumeMusicStream(plug->music); - printf("Music resumed\n"); - } -} - -void RaiseVolume(void) -{ - plug->audio_volume += 0.02; - if (plug->audio_volume > 1.0) { - plug->audio_volume = 1.0; - } - printf("Audio Level: %f\n", plug->audio_volume); - SetMusicVolume(plug->music, plug->audio_volume); -} - -void LowerVolume(void) -{ - plug->audio_volume -= 0.02; - if (plug->audio_volume < 0.0) { - plug->audio_volume = 0.0; - } - printf("Audio Level: %f\n", plug->audio_volume); - SetMusicVolume(plug->music, plug->audio_volume); -} - -void PrepareMusicStream(void) -{ - if (IsMusicValid(plug->music)) { - StopMusicStream(plug->music); - UnloadMusicStream(plug->music); - } -} - -void StartMusicStream(void) -{ - if (IsMusicValid(plug->music)) { - plug->error = false; - printf("music.frameCount = %u\n", plug->music.frameCount); - printf("music.stream.sampleRate = %u\n", plug->music.stream.sampleRate); - printf("music.stream.sampleSize = %u\n", plug->music.stream.sampleSize); - printf("music.stream.channels = %u\n", plug->music.stream.channels); - SetMusicVolume(plug->music, plug->audio_volume); - AttachAudioStreamProcessor(plug->music.stream, callback); - PlayMusicStream(plug->music); - } else { - plug->error = true; - } -} - -size_t fft_analyze(float dt) -{ - // Apply Hann Window on input - https://en.wikipedia.org/wiki/Hann_function - for (size_t i = 0; i < SAMPLE_SIZE; ++i) { - float t = (float)i/(SAMPLE_SIZE-1); - float hann = 0.5 - 0.5*cosf(2*PI*t); - in_win[i] = hann * in_raw[i]; - } - - // FFT - fft(in_win, 1, out_raw, SAMPLE_SIZE); - - // Convert into logarithmic scale - float step = 1.06; - float lowf = 1.0f; - size_t m = 0; - float max_amp = 1.0f; - for (float f = lowf; (size_t) f < SAMPLE_SIZE/2; f = ceilf(f*step)) { - float f1 = ceilf(f*step); - float a = 0.0f; - for (size_t q = (size_t) f; q < SAMPLE_SIZE/2 && q < (size_t) f1; ++q) { - float b = amp(out_raw[q]); - if (b > a) a = b; - } - if (max_amp < a) max_amp = a; - out_log[m++] = a; - } - - // Normalize frequencies 0..1 range - for (size_t i = 0; i < m; ++i) { - out_log[i] /= max_amp; - } - - // Interpolate frequencies - for (size_t i = 0; i < m; ++i) { - out_smooth[i] += (out_log[i] - out_smooth[i])*dt*SMOOTHNESS; - out_smear[i] += (out_smooth[i] - out_smear[i])*dt*SMEARNESS; - } - return m; -} - -void fft_render_bars(int w, int h, size_t m) -{ - // Display the frequencies - float cell_width = (float)w/m; - float saturation = 0.75f; - float value = 0.9f; - - // Draw the bars - for (size_t i = 0; i < m; ++i) { - float t = out_smooth[i]; - float hue = (float)i/m; - Color color = ColorFromHSV(hue*360, saturation, value); - float thickness = (cell_width/1.8)*sqrtf(t); - Vector2 start_pos = { - .x = i*cell_width + cell_width/2, - .y = h, - }; - Vector2 end_pos = { - .x = start_pos.x, - .y = start_pos.y - h*2/3*t, - }; - DrawLineEx(start_pos, end_pos, thickness, color); - } - - - // Construct a texture to be used in the shader - // since fragTexCoord doesn't work on shapes - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - // Draw smear frames - BeginShaderMode(plug->smear); - for (size_t i = 0; i < m; ++i) { - float start = out_smear[i]; - float end = out_smooth[i]; - float hue = (float)i/m; - Color color = ColorFromHSV(hue*360, saturation, value); - float radius = cell_width*3*sqrt(end); - Vector2 origin = {0}; - Vector2 start_pos = { - .x = i*cell_width + cell_width/2, - .y = h - h*2/3*start, - }; - Vector2 end_pos = { - .x = i*cell_width + cell_width/2, - .y = h - h*2/3*end, - }; - if (end_pos.y >= start_pos.y) { - Rectangle dest = { - .x = start_pos.x - radius/2, - .y = start_pos.y, - .width = radius, - .height = end_pos.y - start_pos.y, - }; - Rectangle source = {0, 0, 1, 0.5}; - DrawTexturePro(texture, source, dest, origin, 0, color); - } else { - Rectangle dest = { - .x = end_pos.x - radius/2, - .y = end_pos.y, - .width = radius, - .height = start_pos.y - end_pos.y, - }; - Rectangle source = {0, 0.5, 1, 0.5}; - DrawTexturePro(texture, source, dest, origin, 0, color); - } - } - EndShaderMode(); - - // Draw the circles - BeginShaderMode(plug->circle); - for (size_t i = 0; i < m; ++i) { - float t = out_smooth[i]; - float hue = (float)i/m; - Color color = ColorFromHSV(hue*360, saturation, value); - float radius = cell_width*4*sqrtf(t); - Vector2 position = { - .x = i*cell_width + cell_width/2 - radius, - .y = h - h*2/3*t - radius, - }; - DrawTextureEx(texture, position, 0, 2*radius, color); - } - EndShaderMode(); -} - -void fft_render_circle(int w, int h, size_t m) -{ - // Display the frequencies - float cell_width = (float)720/m; - float saturation = 0.75f; - float value = 0.9f; - - // Construct a texture to be used in the shader - // since fragTexCoord doesn't work on shapes - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - // Draw the circles - BeginShaderMode(plug->circle); - for (size_t i = 0; i < m; ++i) { - float t = out_smooth[i]; - float hue = (float)i/m; - Color color = ColorFromHSV(hue*360, saturation, value); - float radius = cell_width*4*sqrtf(t); - Vector2 position = { - .x = w/2 + (h/2.1 * cos(i * cell_width)) * sqrtf(t) - radius, - .y = h/2 + (h/2.1 * sin(i * cell_width)) * sqrtf(t) - radius, - }; - DrawTextureEx(texture, position, 0, 2*radius, color); - } - EndShaderMode(); -} - -Plug *plug_pre_reload(void) -{ - if (IsMusicValid(plug->music)) { - DetachAudioStreamProcessor(plug->music.stream, callback); - } - return plug; -} - -void plug_post_reload(Plug *prev) -{ - plug = prev; - if (IsMusicValid(plug->music)) { - AttachAudioStreamProcessor(plug->music.stream, callback); - } - UnloadShader(plug->circle); - UnloadShader(plug->smear); - plug->circle = LoadShader(NULL, "./shaders/circles.fs"); - plug->smear = LoadShader(NULL, "./shaders/smear.fs"); -} - -void plug_init(void) -{ - plug = malloc(sizeof(*plug)); - assert(plug != NULL && "Not enough RAM!"); - memset(plug, 0, sizeof(*plug)); - - plug->font = LoadFontEx("./fonts/AlegreyaSans-Regular.ttf", FONT_SIZE, NULL, 0); - plug->circle = LoadShader(NULL, "./shaders/circles.fs"); - plug->smear = LoadShader(NULL, "./shaders/smear.fs"); - plug->audio_volume = 0.5f; - plug->render_option = 'b'; -} - -void plug_update(void) -{ - if (IsMusicValid(plug->music)) { - UpdateMusicStream(plug->music); - } - - if (IsKeyPressed(KEY_SPACE) && IsMusicValid(plug->music)) { - PausePlayMusic(); - } - - if (IsKeyPressed(KEY_UP)) { - RaiseVolume(); - } - - if (IsKeyPressed(KEY_DOWN)) { - LowerVolume(); - } - - if (IsKeyPressed(KEY_N)) { - plug->render_option = 'b'; - } - - if (IsKeyPressed(KEY_I)) { - plug->render_option = 'c'; - } - - if (IsFileDropped()){ - FilePathList droppedFiles = LoadDroppedFiles(); - const char *file_path = droppedFiles.paths[0]; - - PrepareMusicStream(); - plug->music = LoadMusicStream(file_path); - StartMusicStream(); - - UnloadDroppedFiles(droppedFiles); - } - - float w = GetRenderWidth(); - float h = GetRenderHeight(); - float dt = GetFrameTime(); - - BeginDrawing(); - ClearBackground(CLITERAL(Color) { - 0x1, 0x1, 0x1, 0xFF - }); - - if (IsMusicValid(plug->music)) { - size_t m = fft_analyze(dt); - switch(plug->render_option) - { - case 'b': fft_render_bars(w, h, m); break; - case 'c': fft_render_circle(w, h, m); break; - default: break; - } - - } else { - const char *label; - Color color; - if (plug->error) { - label = "Could not load file"; - color = RED; - } else { - label = "Drag & Drop Music Here"; - color = WHITE; - } - Vector2 size = MeasureTextEx(plug->font, label, plug->font.baseSize, 0); - Vector2 position = { - .x = w/2 - size.x/2, - .y = h/2 - size.y/2, - }; - DrawTextEx(plug->font, label, position, plug->font.baseSize, 0, color); - } - EndDrawing(); -} |
