From 15af86b06d7412aed70f0eebedf8be753b8e662a Mon Sep 17 00:00:00 2001 From: dan-hoang <56205882+dan-hoang@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:26:20 -0700 Subject: [PATCH] Add audio_raw_stream_callback.c --- examples/audio/audio_raw_stream_callback.c | 155 +++++++++++++++++++ examples/audio/audio_raw_stream_callback.png | Bin 0 -> 16444 bytes 2 files changed, 155 insertions(+) create mode 100644 examples/audio/audio_raw_stream_callback.c create mode 100644 examples/audio/audio_raw_stream_callback.png diff --git a/examples/audio/audio_raw_stream_callback.c b/examples/audio/audio_raw_stream_callback.c new file mode 100644 index 000000000..a9bb464c7 --- /dev/null +++ b/examples/audio/audio_raw_stream_callback.c @@ -0,0 +1,155 @@ +/******************************************************************************************* + * + * raylib [audio] example - raw stream callback + * + * Example complexity rating: [★★★☆] 3/4 + * + * Example originally created with raylib 1.6, last time updated with raylib 4.2 + * + * Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) + * + * 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) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) + * + ********************************************************************************************/ + +#include "raylib.h" +#include + +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 + +// This example sends a sine wave to the audio device +int sineFrequency = 440; +int newSineFrequency = 440; +int sineIndex = 0; +double sineStartTime = 0.0; + +void AudioStreamCallback(void *framesOut, unsigned int frameCount) +{ + for (int i = 0; i < frameCount; i++) + { + int wavelength = SAMPLE_RATE/sineFrequency; + ((float *)framesOut)[i] = sin(2*PI*sineIndex/wavelength); + + sineIndex++; + + if (sineIndex >= wavelength) + { + sineFrequency = newSineFrequency; + sineIndex = 0; + sineStartTime = GetTime(); + } + } +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream callback"); + SetTargetFPS(30); + + InitAudioDevice(); + + // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE + SetAudioStreamBufferSizeDefault(BUFFER_SIZE); + + // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono) + AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1); + float pan = 0.0f; + SetAudioStreamPan(stream, pan); + PlayAudioStream(stream); + + // Configure it so AudioStreamCallback is called whenever stream is out of samples + SetAudioStreamCallback(stream, AudioStreamCallback); + + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + if (IsKeyDown(KEY_UP)) + { + newSineFrequency += 10; + + if (newSineFrequency > 12500) + newSineFrequency = 12500; + } + + if (IsKeyDown(KEY_DOWN)) + { + newSineFrequency -= 10; + + if (newSineFrequency < 20) + newSineFrequency = 20; + } + + if (IsKeyDown(KEY_LEFT)) + { + pan -= 0.01f; + SetAudioStreamPan(stream, pan); + + if (pan < -1.0f) + pan = -1.0f; + } + + if (IsKeyDown(KEY_RIGHT)) + { + pan += 0.01f; + SetAudioStreamPan(stream, pan); + + if (pan > 1.0f) + pan = 1.0f; + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(RAYWHITE); + + DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED); + DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED); + DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY); + DrawText("Left/right to pan", 10, 30, 20, DARKGRAY); + + int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; + int windowSize = 0.1f*SAMPLE_RATE; + int wavelength = SAMPLE_RATE/sineFrequency; + + // Draw a sine wave with the same frequency as the one being sent to the audio stream + for (int i = 0; i < screenWidth; i++) { + int t0 = windowStart + i*windowSize/screenWidth; + int t1 = windowStart + (i + 1)*windowSize/screenWidth; + Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) }; + Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) }; + DrawLineV(startPos, endPos, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/audio/audio_raw_stream_callback.png b/examples/audio/audio_raw_stream_callback.png new file mode 100644 index 0000000000000000000000000000000000000000..2f01d883506f5982f46e4519ab0e433d569aea6c GIT binary patch literal 16444 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