summaryrefslogtreecommitdiff
path: root/plug.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug.c')
-rw-r--r--plug.c390
1 files changed, 0 insertions, 390 deletions
diff --git a/plug.c b/plug.c
deleted file mode 100644
index f67382d..0000000
--- a/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();
-}