diff --git a/examples/audio/audio_effect_processor.c b/examples/audio/audio_effect_processor.c new file mode 100644 index 000000000..6bf2c2db8 --- /dev/null +++ b/examples/audio/audio_effect_processor.c @@ -0,0 +1,179 @@ +/******************************************************************************************* +* +* raylib [audio] example - Audio effects (streaming) +* +* This example has been created using raylib 1.3 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include + +#define AUDIO_THREAD_MUSIC_UPDATE true + +// a simple lowpass filter applied to the music stream +static void processFilterEffect(float* buffer, unsigned int nframes) +{ + 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 < nframes*2; i+=2) + { + float l = buffer[i], r = buffer[i + 1]; + low[0] += k * (l - low[0]); + low[1] += k * (r - low[1]); + buffer[i] = low[0]; + buffer[i + 1] = low[1]; + } +} + + +float* delayBuffer = NULL; +unsigned int delayBufferSize = 0; +unsigned int delayReadIndex = 2; +unsigned int delayWriteIndex = 0; +static void processDelayEffect(float* buffer, unsigned int nframes) +{ + for (unsigned int i = 0; i < nframes*2; i+=2) + { + float leftDelay = delayBuffer[delayReadIndex++]; + float rightDelay = delayBuffer[delayReadIndex++]; + if (delayReadIndex == delayBufferSize) delayReadIndex = 0; + + buffer[i] = 0.5f * buffer[i] + 0.5f * leftDelay; + buffer[i+1] = 0.5f * buffer[i+1] + 0.5f * rightDelay; + + delayBuffer[delayWriteIndex++] = buffer[i]; + delayBuffer[delayWriteIndex++] = buffer[i+1]; + if (delayWriteIndex == delayBufferSize) delayWriteIndex = 0; + } +} + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - audio effects"); + + AudioDeviceInfo audioDevice = InitAudioDevice(); // Initialize audio device + if (!audioDevice.channels) return -1; + + // allocate buffer for the delay effect + delayBufferSize = audioDevice.channels * audioDevice.sampleRate; // 1s delay + delayBuffer = (float*)RL_CALLOC(delayBufferSize, sizeof(float)); + + Music music = LoadMusicStream("resources/country.mp3"); + music.background = AUDIO_THREAD_MUSIC_UPDATE; + + PlayMusicStream(music); + + float timePlayed = 0.0f; + bool pause = false; + bool hasFilter = false; + bool hasDelay = false; + + 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 + //---------------------------------------------------------------------------------- + if (!AUDIO_THREAD_MUSIC_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 effects + if (IsKeyPressed(KEY_F)) + { + hasFilter = !hasFilter; + if (hasFilter) + { + AddAudioStreamProcessor(music.stream, &processFilterEffect); + } + else + { + RemoveAudioStreamProcessor(music.stream, &processFilterEffect); + } + } + + if (IsKeyPressed(KEY_D)) + { + hasDelay = !hasDelay; + if (hasDelay) + { + AddAudioStreamProcessor(music.stream, &processDelayEffect); + } + else + { + RemoveAudioStreamProcessor(music.stream, &processDelayEffect); + } + } + + // Get timePlayed scaled to bar dimensions (400 pixels) + timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music)*400; + + if (timePlayed > 400) StopMusicStream(music); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, LIGHTGRAY); + + DrawRectangle(200, 200, 400, 12, LIGHTGRAY); + DrawRectangle(200, 200, (int)timePlayed, 12, MAROON); + DrawRectangleLines(200, 200, 400, 12, GRAY); + DrawRectangleLines(200, 200, 400, 12, GRAY); + + DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, LIGHTGRAY); + DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, LIGHTGRAY); + DrawText("PRESS F TO ADD/REMOVE FILTER EFFECT", 180, 310, 20, LIGHTGRAY); + DrawText("PRESS D TO ADD/REMOVE DELAY EFFECT", 180, 340, 20, LIGHTGRAY); + + 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; +} \ No newline at end of file diff --git a/examples/audio/audio_effect_processor.png b/examples/audio/audio_effect_processor.png new file mode 100644 index 000000000..785055419 Binary files /dev/null and b/examples/audio/audio_effect_processor.png differ diff --git a/examples/audio/audio_music_stream.c b/examples/audio/audio_music_stream.c index 3ea6fcb74..551116179 100644 --- a/examples/audio/audio_music_stream.c +++ b/examples/audio/audio_music_stream.c @@ -14,23 +14,6 @@ #define AUDIO_THREAD_MUSIC_UPDATE true -// a simple lowpass filter applied to the music stream -static void audioEffectDemo(float* buffer, unsigned int nframes) -{ - 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 < nframes*2; i+=2) - { - float l = buffer[i], r = buffer[i + 1]; - low[0] += k * (l - low[0]); - low[1] += k * (r - low[1]); - buffer[i] = low[0]; - buffer[i + 1] = low[1]; - } -} - int main(void) { // Initialization @@ -49,7 +32,6 @@ int main(void) float timePlayed = 0.0f; bool pause = false; - bool hasfx = false; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -80,20 +62,6 @@ int main(void) else ResumeMusicStream(music); } - // Add/Remove effect - if (IsKeyPressed(KEY_F)) - { - hasfx = !hasfx; - if (hasfx) - { - AddAudioStreamProcessor(music.stream, &audioEffectDemo); - } - else - { - RemoveAudioStreamProcessor(music.stream, &audioEffectDemo); - } - } - // Get timePlayed scaled to bar dimensions (400 pixels) timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music)*400; @@ -111,11 +79,9 @@ int main(void) DrawRectangle(200, 200, 400, 12, LIGHTGRAY); DrawRectangle(200, 200, (int)timePlayed, 12, MAROON); DrawRectangleLines(200, 200, 400, 12, GRAY); - DrawRectangleLines(200, 200, 400, 12, GRAY); DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, LIGHTGRAY); DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, LIGHTGRAY); - DrawText("PRESS F TO ADD/REMOVE EFFECT", 208, 310, 20, LIGHTGRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raudio.c b/src/raudio.c index f40b6bdfa..b8b9b891b 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -418,8 +418,13 @@ void UntrackAudioBuffer(AudioBuffer *buffer); // Module Functions Definition - Audio Device initialization and Closing //---------------------------------------------------------------------------------- // Initialize audio device -void InitAudioDevice(void) +AudioDeviceInfo InitAudioDevice(void) { + AudioDeviceInfo deviceInfo; + deviceInfo.sampleRate = 0; + deviceInfo.channels = 0; + deviceInfo.bufferSize = 0; + // Init audio context ma_context_config ctxConfig = ma_context_config_init(); ctxConfig.logCallback = OnLog; @@ -428,7 +433,7 @@ void InitAudioDevice(void) if (result != MA_SUCCESS) { TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize context"); - return; + return deviceInfo; } // Init audio device @@ -449,7 +454,7 @@ void InitAudioDevice(void) { TRACELOG(LOG_WARNING, "AUDIO: Failed to initialize playback device"); ma_context_uninit(&AUDIO.System.context); - return; + return deviceInfo; } // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running @@ -460,7 +465,7 @@ void InitAudioDevice(void) TRACELOG(LOG_WARNING, "AUDIO: Failed to start playback device"); ma_device_uninit(&AUDIO.System.device); ma_context_uninit(&AUDIO.System.context); - return; + return deviceInfo; } // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may @@ -470,7 +475,7 @@ void InitAudioDevice(void) TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); ma_device_uninit(&AUDIO.System.device); ma_context_uninit(&AUDIO.System.context); - return; + return deviceInfo; } // Init dummy audio buffers pool for multichannel sound playing @@ -489,6 +494,11 @@ void InitAudioDevice(void) TRACELOG(LOG_INFO, " > Periods size: %d", AUDIO.System.device.playback.internalPeriodSizeInFrames*AUDIO.System.device.playback.internalPeriods); AUDIO.System.isReady = true; + + deviceInfo.sampleRate = AUDIO.System.device.playback.internalSampleRate; + deviceInfo.channels = AUDIO.System.device.playback.channels; + deviceInfo.bufferSize = AUDIO.System.device.playback.internalPeriodSizeInFrames; + return deviceInfo; } // Close the audio device for all contexts @@ -2195,6 +2205,7 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, if (audioBuffer->audioCallback) { audioBuffer->audioCallback(framesOut, frameCount, audioBuffer->audioCallbackData); + audioBuffer->framesProcessed += frameCount; return frameCount; } diff --git a/src/raudio.h b/src/raudio.h index bc74895dc..c7270814f 100644 --- a/src/raudio.h +++ b/src/raudio.h @@ -114,6 +114,13 @@ typedef struct Music { void *ctxData; // Audio context data, depends on type } Music; +typedef struct AudioDeviceInfo +{ + unsigned int sampleRate; + unsigned int channels; + unsigned int bufferSize; +} AudioDeviceInfo; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -128,7 +135,7 @@ extern "C" { // Prevents name mangling of functions #endif // Audio device management functions -void InitAudioDevice(void); // Initialize audio device and context +AudioDeviceInfo InitAudioDevice(void); // Initialize audio device and context void CloseAudioDevice(void); // Close the audio device and context bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully void SetMasterVolume(float volume); // Set master volume (listener) @@ -193,6 +200,7 @@ void SetAudioStreamBufferSizeDefault(int size); // Default size void SetAudioStreamCallback(AudioStream stream, void callback(void*, unsigned int, void*), void* callbackData); // Audio thread callback to request new data void AddAudioStreamProcessor(AudioStream stream, void (*process)(float*, unsigned int)); void RemoveAudioStreamProcessor(AudioStream stream, void (*process)(float*, unsigned int)); + #ifdef __cplusplus } #endif diff --git a/src/raylib.h b/src/raylib.h index 0a9d81660..8d1984eba 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -453,6 +453,13 @@ typedef struct Music { void *ctxData; // Audio context data, depends on type } Music; +typedef struct AudioDeviceInfo +{ + unsigned int sampleRate; + unsigned int channels; + unsigned int bufferSize; +} AudioDeviceInfo; + // VrDeviceInfo, Head-Mounted-Display device parameters typedef struct VrDeviceInfo { int hResolution; // Horizontal resolution in pixels @@ -1469,7 +1476,7 @@ RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 //------------------------------------------------------------------------------------ // Audio device management functions -RLAPI void InitAudioDevice(void); // Initialize audio device and context +RLAPI AudioDeviceInfo InitAudioDevice(void); // Initialize audio device and context RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI void SetMasterVolume(float volume); // Set master volume (listener)