diff --git a/examples/audio/audio_stream_effects.c b/examples/audio/audio_stream_effects.c new file mode 100644 index 00000000..ddaeb7fb --- /dev/null +++ b/examples/audio/audio_stream_effects.c @@ -0,0 +1,179 @@ +/******************************************************************************************* +* +* raylib [audio] example - Music stream processing effects +* +* Example originally created with raylib 4.2, last time updated with raylib 4.2 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2022 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include // Required for: NULL + +// Required delay effect variables +static float *delayBuffer = NULL; +static unsigned int delayBufferSize = 0; +static unsigned int delayReadIndex = 2; +static unsigned int delayWriteIndex = 0; + +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static void AudioProcessEffectLPF(void *buffer, unsigned int frames); // Audio effect: lowpass filter +static void AudioProcessEffectDelay(void *buffer, unsigned int frames); // Audio effect: delay + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream effects"); + + InitAudioDevice(); // Initialize audio device + + Music music = LoadMusicStream("resources/country.mp3"); + + // Allocate buffer for the delay effect + delayBufferSize = 48000*2; // 1 second delay (device sampleRate*channels) + delayBuffer = (float *)RL_CALLOC(delayBufferSize, sizeof(float)); + + PlayMusicStream(music); + + float timePlayed = 0.0f; // Time played normalized [0.0f..1.0f] + bool pause = false; // Music playing paused + + bool enableEffectLPF = false; // Enable effect low-pass-filter + bool enableEffectDelay = false; // Enable effect delay (1 second) + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateMusicStream(music); // Update music buffer with new stream data + + // Restart music playing (stop and play) + if (IsKeyPressed(KEY_SPACE)) + { + StopMusicStream(music); + PlayMusicStream(music); + } + + // Pause/Resume music playing + if (IsKeyPressed(KEY_P)) + { + pause = !pause; + + if (pause) PauseMusicStream(music); + else ResumeMusicStream(music); + } + + // Add/Remove effect: lowpass filter + if (IsKeyPressed(KEY_F)) + { + enableEffectLPF = !enableEffectLPF; + if (enableEffectLPF) AttachAudioStreamProcessor(music.stream, AudioProcessEffectLPF); + else DetachAudioStreamProcessor(music.stream, AudioProcessEffectLPF); + } + + // Add/Remove effect: delay + if (IsKeyPressed(KEY_D)) + { + enableEffectDelay = !enableEffectDelay; + if (enableEffectDelay) AttachAudioStreamProcessor(music.stream, AudioProcessEffectDelay); + else DetachAudioStreamProcessor(music.stream, AudioProcessEffectDelay); + } + + // Get normalized time played for current music stream + timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music); + + if (timePlayed > 1.0f) timePlayed = 1.0f; // Make sure time played is no longer than music + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("MUSIC SHOULD BE PLAYING!", 245, 150, 20, LIGHTGRAY); + + DrawRectangle(200, 180, 400, 12, LIGHTGRAY); + DrawRectangle(200, 180, (int)(timePlayed*400.0f), 12, MAROON); + DrawRectangleLines(200, 180, 400, 12, GRAY); + + DrawText("PRESS SPACE TO RESTART MUSIC", 215, 230, 20, LIGHTGRAY); + DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 260, 20, LIGHTGRAY); + + DrawText(TextFormat("PRESS F TO TOGGLE LPF EFFECT: %s", enableEffectLPF? "ON" : "OFF"), 200, 320, 20, GRAY); + DrawText(TextFormat("PRESS D TO TOGGLE DELAY EFFECT: %s", enableEffectDelay? "ON" : "OFF"), 180, 350, 20, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadMusicStream(music); // Unload music stream buffers from RAM + + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + + RL_FREE(delayBuffer); // Free delay buffer + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ +// Audio effect: lowpass filter +static void AudioProcessEffectLPF(void *buffer, unsigned int frames) +{ + static float low[2] = { 0.0f, 0.0f }; + static const float cutoff = 70.0f / 44100.0f; // 70 Hz lowpass filter + const float k = cutoff / (cutoff + 0.1591549431f); // RC filter formula + + for (unsigned int i = 0; i < frames*2; i += 2) + { + float l = ((float *)buffer)[i], r = ((float *)buffer)[i + 1]; + low[0] += k * (l - low[0]); + low[1] += k * (r - low[1]); + ((float *)buffer)[i] = low[0]; + ((float *)buffer)[i + 1] = low[1]; + } +} + +// Audio effect: delay +static void AudioProcessEffectDelay(void *buffer, unsigned int frames) +{ + for (unsigned int i = 0; i < frames*2; i += 2) + { + float leftDelay = delayBuffer[delayReadIndex++]; // ERROR: Reading buffer -> WHY??? Maybe thread related??? + float rightDelay = delayBuffer[delayReadIndex++]; + + if (delayReadIndex == delayBufferSize) delayReadIndex = 0; + + ((float *)buffer)[i] = 0.5f*((float *)buffer)[i] + 0.5f*leftDelay; + ((float *)buffer)[i + 1] = 0.5f*((float *)buffer)[i + 1] + 0.5f*rightDelay; + + delayBuffer[delayWriteIndex++] = ((float *)buffer)[i]; + delayBuffer[delayWriteIndex++] = ((float *)buffer)[i + 1]; + if (delayWriteIndex == delayBufferSize) delayWriteIndex = 0; + } +} \ No newline at end of file