From 79a6188e9c25912a344d665513bc4e72c2b7f8f4 Mon Sep 17 00:00:00 2001 From: iann Date: Thu, 13 Nov 2025 06:46:18 +0900 Subject: [PATCH 1/3] copy audio_raw_stream.c to new file as-is --- examples/Makefile | 1 + examples/audio/audio_raw_stream_callback.c | 214 +++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 examples/audio/audio_raw_stream_callback.c diff --git a/examples/Makefile b/examples/Makefile index 729459de40d3..c73b15d89ffa 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -707,6 +707,7 @@ AUDIO = \ audio/audio_module_playing \ audio/audio_music_stream \ audio/audio_raw_stream \ + audio/audio_raw_stream_callback \ audio/audio_sound_loading \ audio/audio_sound_multi \ audio/audio_sound_positioning \ diff --git a/examples/audio/audio_raw_stream_callback.c b/examples/audio/audio_raw_stream_callback.c new file mode 100644 index 000000000000..501a0c324ba5 --- /dev/null +++ b/examples/audio/audio_raw_stream_callback.c @@ -0,0 +1,214 @@ +/******************************************************************************************* +* +* raylib [audio] example - raw stream +* +* 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 // Required for: malloc(), free() +#include // Required for: sinf() +#include // Required for: memcpy() + +#define MAX_SAMPLES 512 +#define MAX_SAMPLES_PER_UPDATE 4096 + +// Cycles per second (hz) +float frequency = 440.0f; + +// Audio frequency, for smoothing +float audioFrequency = 440.0f; + +// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency +float oldFrequency = 1.0f; + +// Index for audio rendering +float sineIdx = 0.0f; + +// Audio input processing callback +void AudioInputCallback(void *buffer, unsigned int frames) +{ + audioFrequency = frequency + (audioFrequency - frequency)*0.95f; + + float incr = audioFrequency/44100.0f; + short *d = (short *)buffer; + + for (unsigned int i = 0; i < frames; i++) + { + d[i] = (short)(32000.0f*sinf(2*PI*sineIdx)); + sineIdx += incr; + if (sineIdx > 1.0f) sineIdx -= 1.0f; + } +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream"); + + InitAudioDevice(); // Initialize audio device + + SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE); + + // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) + AudioStream stream = LoadAudioStream(44100, 16, 1); + + SetAudioStreamCallback(stream, AudioInputCallback); + + // Buffer for the single cycle waveform we are synthesizing + short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); + + // Frame buffer, describing the waveform when repeated over the course of a frame + short *writeBuf = (short *)malloc(sizeof(short)*MAX_SAMPLES_PER_UPDATE); + + PlayAudioStream(stream); // Start processing stream buffer (no data loaded currently) + + // Position read in to determine next frequency + Vector2 mousePosition = { -100.0f, -100.0f }; + + /* + // Cycles per second (hz) + float frequency = 440.0f; + + // Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency + float oldFrequency = 1.0f; + + // Cursor to read and copy the samples of the sine wave buffer + int readCursor = 0; + */ + + // Computed size in samples of the sine wave + int waveLength = 1; + + Vector2 position = { 0, 0 }; + + SetTargetFPS(30); // Set our game to run at 30 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) + { + float fp = (float)(mousePosition.y); + frequency = 40.0f + (float)(fp); + + float pan = (float)(mousePosition.x)/(float)screenWidth; + SetAudioStreamPan(stream, pan); + } + + // Rewrite the sine wave + // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. + if (frequency != oldFrequency) + { + // Compute wavelength. Limit size in both directions + //int oldWavelength = waveLength; + waveLength = (int)(22050/frequency); + if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; + if (waveLength < 1) waveLength = 1; + + // Write sine wave + for (int i = 0; i < waveLength*2; i++) + { + data[i] = (short)(sinf(((2*PI*(float)i/waveLength)))*32000); + } + // Make sure the rest of the line is flat + for (int j = waveLength*2; j < MAX_SAMPLES; j++) + { + data[j] = (short)0; + } + + // Scale read cursor's position to minimize transition artifacts + //readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength)); + oldFrequency = frequency; + } + + /* + // Refill audio stream if required + if (IsAudioStreamProcessed(stream)) + { + // Synthesize a buffer that is exactly the requested size + int writeCursor = 0; + + while (writeCursor < MAX_SAMPLES_PER_UPDATE) + { + // Start by trying to write the whole chunk at once + int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor; + + // Limit to the maximum readable size + int readLength = waveLength-readCursor; + + if (writeLength > readLength) writeLength = readLength; + + // Write the slice + memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short)); + + // Update cursors and loop audio + readCursor = (readCursor + writeLength) % waveLength; + + writeCursor += writeLength; + } + + // Copy finished frame to audio stream + UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); + } + */ + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText(TextFormat("sine frequency: %i",(int)frequency), GetScreenWidth() - 220, 10, 20, RED); + DrawText("click mouse button to change frequency or pan", 10, 10, 20, DARKGRAY); + + // Draw the current buffer state proportionate to the screen + for (int i = 0; i < screenWidth; i++) + { + position.x = (float)i; + position.y = 250 + 50*data[i*MAX_SAMPLES/screenWidth]/32000.0f; + + DrawPixelV(position, RED); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + free(data); // Unload sine wave data + free(writeBuf); // Unload write buffer + + 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; +} \ No newline at end of file From b3c25186d9cf395bab53f8b4b5f4162d88897242 Mon Sep 17 00:00:00 2001 From: iann Date: Thu, 13 Nov 2025 07:31:47 +0900 Subject: [PATCH 2/3] THIS COMMIT IS FOR REVIEW: audio update functions clarity review in examples --- examples/audio/audio_raw_stream.c | 42 +----- examples/audio/audio_raw_stream_callback.c | 139 ++++++++++++------- examples/audio/audio_raw_stream_callback.png | Bin 0 -> 16434 bytes examples/audio/audio_sound_loading.c | 33 ++++- examples/audio/audio_sound_loading.png | Bin 15438 -> 15987 bytes 5 files changed, 125 insertions(+), 89 deletions(-) create mode 100644 examples/audio/audio_raw_stream_callback.png diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index 4deae2090982..0195efba85eb 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 1.6, last time updated with raylib 4.2 +* Example originally created with raylib 1.6, last time updated with raylib 6.0 * * Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) * @@ -24,34 +24,6 @@ #define MAX_SAMPLES 512 #define MAX_SAMPLES_PER_UPDATE 4096 -// Cycles per second (hz) -float frequency = 440.0f; - -// Audio frequency, for smoothing -float audioFrequency = 440.0f; - -// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency -float oldFrequency = 1.0f; - -// Index for audio rendering -float sineIdx = 0.0f; - -// Audio input processing callback -void AudioInputCallback(void *buffer, unsigned int frames) -{ - audioFrequency = frequency + (audioFrequency - frequency)*0.95f; - - float incr = audioFrequency/44100.0f; - short *d = (short *)buffer; - - for (unsigned int i = 0; i < frames; i++) - { - d[i] = (short)(32000.0f*sinf(2*PI*sineIdx)); - sineIdx += incr; - if (sineIdx > 1.0f) sineIdx -= 1.0f; - } -} - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -71,8 +43,6 @@ int main(void) // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) AudioStream stream = LoadAudioStream(44100, 16, 1); - SetAudioStreamCallback(stream, AudioInputCallback); - // Buffer for the single cycle waveform we are synthesizing short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); @@ -84,7 +54,6 @@ int main(void) // Position read in to determine next frequency Vector2 mousePosition = { -100.0f, -100.0f }; - /* // Cycles per second (hz) float frequency = 440.0f; @@ -93,7 +62,6 @@ int main(void) // Cursor to read and copy the samples of the sine wave buffer int readCursor = 0; - */ // Computed size in samples of the sine wave int waveLength = 1; @@ -124,8 +92,8 @@ int main(void) if (frequency != oldFrequency) { // Compute wavelength. Limit size in both directions - //int oldWavelength = waveLength; - waveLength = (int)(22050/frequency); + int oldWavelength = waveLength; + waveLength = (int)(stream.sampleRate/frequency); if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; if (waveLength < 1) waveLength = 1; @@ -141,11 +109,10 @@ int main(void) } // Scale read cursor's position to minimize transition artifacts - //readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength)); + readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength)); oldFrequency = frequency; } - /* // Refill audio stream if required if (IsAudioStreamProcessed(stream)) { @@ -174,7 +141,6 @@ int main(void) // Copy finished frame to audio stream UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); } - */ //---------------------------------------------------------------------------------- // Draw diff --git a/examples/audio/audio_raw_stream_callback.c b/examples/audio/audio_raw_stream_callback.c index 501a0c324ba5..746b29716f93 100644 --- a/examples/audio/audio_raw_stream_callback.c +++ b/examples/audio/audio_raw_stream_callback.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★★★☆] 3/4 * -* Example originally created with raylib 1.6, last time updated with raylib 4.2 +* Example originally created with raylib 1.6, last time updated with raylib 6.0 * * Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) * @@ -19,7 +19,12 @@ #include // Required for: malloc(), free() #include // Required for: sinf() -#include // Required for: memcpy() + +enum Flags { FLAG_CHANNEL_MONO = 1u<<0, FLAG_SAMPLESIZE_SHORT = 1u<<1 }; +static unsigned int gflags = FLAG_CHANNEL_MONO | FLAG_SAMPLESIZE_SHORT; // mono + 16-bit to match initial stream specs +#define CHANNEL_MONO() ((gflags & FLAG_CHANNEL_MONO) != 0) +#define SAMPLESIZE_SHORT() ((gflags & FLAG_SAMPLESIZE_SHORT) != 0) +#define TOGGLE(K, F) do { if (IsKeyPressed(K)) { gflags ^= (F); } } while (0) #define MAX_SAMPLES 512 #define MAX_SAMPLES_PER_UPDATE 4096 @@ -37,7 +42,7 @@ float oldFrequency = 1.0f; float sineIdx = 0.0f; // Audio input processing callback -void AudioInputCallback(void *buffer, unsigned int frames) +void AudioInputCallbackMonoShort(void *buffer, unsigned int frames) { audioFrequency = frequency + (audioFrequency - frequency)*0.95f; @@ -52,6 +57,54 @@ void AudioInputCallback(void *buffer, unsigned int frames) } } +void AudioInputCallbackStereoShort(void *buffer, unsigned int frames) +{ + audioFrequency = frequency + (audioFrequency - frequency)*0.95f; + + float incr = audioFrequency/44100.0f; + short *d = (short *)buffer; + + for (unsigned int i = 0; i < frames; i++) + { + short s = (short)(32000.0f*sinf(2*PI*sineIdx)); + d[2*i + 0] = s; // L + d[2*i + 1] = s; // R + sineIdx += incr; + if (sineIdx > 1.0f) sineIdx -= 1.0f; + } +} + +void AudioInputCallbackMonoFloat(void *buffer, unsigned int frames) +{ + audioFrequency = frequency + (audioFrequency - frequency)*0.95f; + + float incr = audioFrequency/44100.0f; + float *d = (float *)buffer; + + for (unsigned int i = 0; i < frames; i++) + { + d[i] = sinf(2*PI*sineIdx); + sineIdx += incr; + if (sineIdx > 1.0f) sineIdx -= 1.0f; + } +} + +void AudioInputCallbackStereoFloat(void *buffer, unsigned int frames) +{ + audioFrequency = frequency + (audioFrequency - frequency)*0.95f; + float incr = audioFrequency/44100.0f; + float *d = (float *)buffer; + for (unsigned int i = 0; i < frames; i++) + { + float s = sinf(2*PI*sineIdx); + d[2*i + 0] = s; + d[2*i + 1] = s; + + sineIdx += incr; + if (sineIdx > 1.0f) sineIdx -= 1.0f; + } +} + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -62,7 +115,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream"); + InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream with callbacks"); InitAudioDevice(); // Initialize audio device @@ -71,7 +124,9 @@ int main(void) // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) AudioStream stream = LoadAudioStream(44100, 16, 1); - SetAudioStreamCallback(stream, AudioInputCallback); + SetAudioStreamCallback(stream, AudioInputCallbackMonoShort); + unsigned int previousSampleSize = stream.sampleSize; + unsigned int previousChannels = stream.channels; // Buffer for the single cycle waveform we are synthesizing short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); @@ -84,17 +139,6 @@ int main(void) // Position read in to determine next frequency Vector2 mousePosition = { -100.0f, -100.0f }; - /* - // Cycles per second (hz) - float frequency = 440.0f; - - // Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency - float oldFrequency = 1.0f; - - // Cursor to read and copy the samples of the sine wave buffer - int readCursor = 0; - */ - // Computed size in samples of the sine wave int waveLength = 1; @@ -106,6 +150,32 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { + TOGGLE(KEY_M, FLAG_CHANNEL_MONO); + TOGGLE(KEY_F, FLAG_SAMPLESIZE_SHORT); + unsigned int nextSampleSize = (SAMPLESIZE_SHORT())? 16u : 32u; + unsigned int nextChannels = (CHANNEL_MONO())? 1u : 2u; + if (nextSampleSize != previousSampleSize || nextChannels != previousChannels) + { + StopAudioStream(stream); + UnloadAudioStream(stream); + stream = LoadAudioStream(44100, nextSampleSize, nextChannels); + // CORRECT ALIGNMENT + if (nextChannels == 1 && nextSampleSize == 16) SetAudioStreamCallback(stream, AudioInputCallbackMonoShort); + if (nextChannels == 2 && nextSampleSize == 16) SetAudioStreamCallback(stream, AudioInputCallbackStereoShort); + if (nextChannels == 1 && nextSampleSize == 32) SetAudioStreamCallback(stream, AudioInputCallbackMonoFloat); + if (nextChannels == 2 && nextSampleSize == 32) SetAudioStreamCallback(stream, AudioInputCallbackStereoFloat); + + // INCORRECT ALIGNMENT TESTS: comment and uncomment or add your own to observe common misconfigurations + // if (nextChannels == 1 && nextSampleSize == 16) SetAudioStreamCallback(stream, AudioInputCallbackStereoShort); + // if (nextChannels == 1 && nextSampleSize == 32) SetAudioStreamCallback(stream, AudioInputCallbackMonoShort); + // if (nextChannels == 2 && nextSampleSize == 32) SetAudioStreamCallback(stream, AudioInputCallbackMonoShort); + // if (nextChannels == 2 && nextSampleSize == 16) SetAudioStreamCallback(stream, AudioInputCallbackMonoFloat); + // if (nextChannels == 2 && nextSampleSize == 16) SetAudioStreamCallback(stream, AudioInputCallbackStereoFloat); + + PlayAudioStream(stream); + previousSampleSize = nextSampleSize; + previousChannels = nextChannels; + } // Update //---------------------------------------------------------------------------------- mousePosition = GetMousePosition(); @@ -124,7 +194,6 @@ int main(void) if (frequency != oldFrequency) { // Compute wavelength. Limit size in both directions - //int oldWavelength = waveLength; waveLength = (int)(22050/frequency); if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; if (waveLength < 1) waveLength = 1; @@ -140,41 +209,9 @@ int main(void) data[j] = (short)0; } - // Scale read cursor's position to minimize transition artifacts - //readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength)); oldFrequency = frequency; } - /* - // Refill audio stream if required - if (IsAudioStreamProcessed(stream)) - { - // Synthesize a buffer that is exactly the requested size - int writeCursor = 0; - - while (writeCursor < MAX_SAMPLES_PER_UPDATE) - { - // Start by trying to write the whole chunk at once - int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor; - - // Limit to the maximum readable size - int readLength = waveLength-readCursor; - - if (writeLength > readLength) writeLength = readLength; - - // Write the slice - memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short)); - - // Update cursors and loop audio - readCursor = (readCursor + writeLength) % waveLength; - - writeCursor += writeLength; - } - - // Copy finished frame to audio stream - UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); - } - */ //---------------------------------------------------------------------------------- // Draw @@ -185,6 +222,10 @@ int main(void) DrawText(TextFormat("sine frequency: %i",(int)frequency), GetScreenWidth() - 220, 10, 20, RED); DrawText("click mouse button to change frequency or pan", 10, 10, 20, DARKGRAY); + DrawText("press M to SWAP channels [ M ]:", 250, 366, 20, BLUE); + DrawText((CHANNEL_MONO())? "MONO" : "STEREO", 600, 366, 20, (CHANNEL_MONO())? GREEN : RED); + DrawText("press F to SWAP Sample Size [ F ]:", 250, 400, 20, BLUE); + DrawText((SAMPLESIZE_SHORT())? "16" : "32", 620, 400, 20, (SAMPLESIZE_SHORT())? GREEN : RED); // Draw the current buffer state proportionate to the screen for (int i = 0; i < screenWidth; i++) diff --git a/examples/audio/audio_raw_stream_callback.png b/examples/audio/audio_raw_stream_callback.png new file mode 100644 index 0000000000000000000000000000000000000000..08ab77ac95d4c4c1c8ef94597196085a3ede0140 GIT binary patch literal 16434 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 NxvlhcK1N~e`ft#iRKNfL literal 0 HcmV?d00001 diff --git a/examples/audio/audio_sound_loading.c b/examples/audio/audio_sound_loading.c index 97e26a450dd7..997c93b19a93 100644 --- a/examples/audio/audio_sound_loading.c +++ b/examples/audio/audio_sound_loading.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 1.1, last time updated with raylib 3.5 +* Example originally created with raylib 1.1, last time updated with raylib 6.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software @@ -14,6 +14,8 @@ ********************************************************************************************/ #include "raylib.h" +#include // Required for: malloc(), free() +#include // Required for: memcpy() //------------------------------------------------------------------------------------ // Program main entry point @@ -32,6 +34,17 @@ int main(void) Sound fxWav = LoadSound("resources/sound.wav"); // Load WAV audio file Sound fxOgg = LoadSound("resources/target.ogg"); // Load OGG audio file + bool soundReversed = false; + + float *soundData = malloc(sizeof(float)*fxWav.frameCount*fxWav.stream.channels); + float *scratchSoundData = malloc(sizeof(float)*fxWav.frameCount*fxWav.stream.channels); + + Wave wave = LoadWave("resources/sound.wav"); + // Sounds always have 32bit sampleSize: + WaveFormat(&wave, fxWav.stream.sampleRate, 32, fxWav.stream.channels); + memcpy(soundData, wave.data, sizeof(float)*fxWav.frameCount*fxWav.stream.channels); + UnloadWave(wave); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -40,6 +53,18 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_R)) + { + soundReversed = !soundReversed; + for (unsigned int i = 0; i < fxWav.frameCount; i++) + { + unsigned int src = (soundReversed)? fxWav.frameCount - 1 - i : i; + // Sounds always have STEREO channels: + scratchSoundData[i * fxWav.stream.channels + 0] = soundData[src * fxWav.stream.channels + 0]; + scratchSoundData[i * fxWav.stream.channels + 1] = soundData[src * fxWav.stream.channels + 1]; + } + UpdateSound(fxWav, scratchSoundData, fxWav.frameCount); + } if (IsKeyPressed(KEY_SPACE)) PlaySound(fxWav); // Play WAV sound if (IsKeyPressed(KEY_ENTER)) PlaySound(fxOgg); // Play OGG sound //---------------------------------------------------------------------------------- @@ -51,7 +76,9 @@ int main(void) ClearBackground(RAYWHITE); DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, LIGHTGRAY); - DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, LIGHTGRAY); + DrawText(TextFormat("Press R to REVERSE the WAV sound : "), 120, 220, 20, LIGHTGRAY); + DrawText((soundReversed)? "BACKWARDS" : "FORWARDS", 525, 220, 20, (soundReversed)? MAROON : DARKGREEN); + DrawText("Press ENTER to PLAY the OGG sound!", 200, 260, 20, LIGHTGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -59,6 +86,8 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + free(soundData); + free(scratchSoundData); UnloadSound(fxWav); // Unload sound data UnloadSound(fxOgg); // Unload sound data diff --git a/examples/audio/audio_sound_loading.png b/examples/audio/audio_sound_loading.png index 24071ce3ccc7e93f29e8eb9d3655c9d90ec585bd..64b463f6c0f820c7837fe05c2966378d833542f1 100644 GIT binary patch delta 1734 zcmc&!Ygm$J81_I)9#QiEra+LkGSdKEIF!HyPvp>1mw9NeEz)LXZh8sM6#aBXrhapV zhKXqAwLC3d(?)i1m02be)110Ya(tDjsek*w_j=#w=lwkIec#XBo%)7ffHUwV zH*L;y0xPz{K@S2pqv8O(Q3%XVS!8yV>;RR!qoaqd3h`W9caM{hQ@(O3m!00RWJ8HE zdLZj?Nj%?fL>8Mxq-EvU)Xs(bly`>4;$$(>FWYw2j`_2vN@JfIbNs@V4$h4w5g$gY z)c7jPubkbNJB+Mlw~dGWly~oO%T5qsmu(HC1dQ~^5h7SgvIoQPFb>&pC(YVXQs~cS zq~!)TnqNDP?e{Yzz1dSQo0+Ro!$nBES1d@r?Hjpb)_xio5UoqT*n0roLUT^PBq(SV zkoY~rdtQe;=3eJ(@tt(|@t}m{unHfog)bdw1oWB+o2u%IYf6?f^Eoq9bhN$;iO&F` zDKmW<1RGDFK{-Qw61&${_P{%e;HOj5MVjI5iV#lChTRLRX6y9j`r_fL`MNiogLaBz% zgiW(1G>7ml(M(yap`Dkq++t7SfOYBjlD_-CEv#FWeKEFi%b}Ao0WPx1a zi!zX6J+uYv)ZegQJ5K1)2y^{7#_3KctKVPjk3YL$?V}+_6q(m83%dAekMZW$Ql&&~ zcaRs%Exy6ro&oSko{GcY?VhHqDNcfAqT9*lG9_a^PUtlV1jB{hr-YYYfG1!Z!3KwQ z8Pa$-;`*gh1=2LF;Ydxgzj#0fo6*=~-yvREMB(l!0sHE?O4k?~Zd~c|ZTxkcaf`@_ zU05%)wWIT|4;|g05=H9f{$WAgP$g#pBz)nue{Vu1CkN=tFl=$n>Is2kQzVZs2-On~ zU;ksCtZRNtj&Lx~)D@5qL%{XM`Ws^3oddu^G!mR5z~HzwH}pQ$x`7O?xC{Q{?LrRF zpJ`rnnPEObg|89Tk$Y2RcAp!M1m&I%vG13W3l{= z`U-v8;8%0m^VW{F9e4ejMyV4W{;yI6ttBInAnfD9GPO!9g2XIFm$xP^wL&w=ji#Us z?tG-1?9HlA!AW~l((AlsrfEppqlITb=PC-BNy9!v&o9QTARl=vi{VPZQ8s&)wsSeP)=pcGYJqRz8HFZS%tMYpX2kYv>2HD?^+GryY?+-Z0P&X%bUaZf8g+Qdlu) zxHJ{3`o~43i-FceCtAx!2v}uX$M@nXsDn!o$%a zCB!4HFoG}35g3XN<pf(rr^i{u@K>Og89nU@+{5y(ryb1t E7yTa`;Q#;t literal 15438 zcmeHOYfuwc6y87zD*^^VC18LEJ_-c_L{Tt2BO%H|kt+3pX>~MEs?-J_1O*fp8DoIr zi&!+0s6_>327I7YG}vkbYG_&z@xfFWBAV(DM+K&IAGYIYHp34)`s4oDAG_JP=bm%E z?>l?WP2v1Fo{g206$C*x{Ag|h1ev=*5akMNf!>+0vg#TH%^t_+h9_=HUO6=Iye}JB zz;ZHEFi9>Vm~vDz-^C_ZLvu8Gp=9Pa3N2T$t9BucLRezt!jfzVi?Y9L{2}_n>Cw3w zda;JfAu}WFEY0yrj%3OiQXA?C8)`^xz)bj!#6Hx7r-TWKzyvf9zzbM90xtkB0A9dJ z8?Ym$YrqS@3;&ikAQwO`fLwU9)(40HhyaKHhwf}v{OH-y z0cq@3@1uupV!QDo1#Bu@fVaE%{7Y)@g!Ps;H7QD!$)-j{LL1@YAlZ=j&UDA`B+xJ>u6Mp4vLzxtj z)5&pBN>7#s`Oow2zaC3{DAkraA2i(iI`Z+3nU!65is)&c@a=g|>;!ke+;D1i!%$@E>;F<6yr_Zev7xmx{=}+_ zQCM;yYl$u28^M-y!yU|;q^tyS-saQQjAM(nG$)Eni-&@l<4r}>*7RoA-d8!TS>dU( zrfV0L=~)N-7%k6Huc~Q|ekFeIwYgc>nAUA zt#m)sq~AsVtSct1-XqXU({ZQ?39Qr~+i>oIe^IgyYKwsz{>+f814YAQ`&{ ztFH=Snk_!4N%S`{s1aZ@rD4relM|!7Ml9?>&PE4H=%h)$6vk&pX#SDG44pXHkUfK~ zFA+Za-K39f@ysI`YylIn-~ccDzg`Gi4KMpCtBjJzGf&3^Bj+VP={&mE1b;wkVG_=2 z4{Rw3ygE@v29KNmy|WK%OWu`H%NgetJ6Yms8NM|obJK?Goo%&oF}2db{bF5t2%K+! zUMIa*R(4M(s?BouMJzpsrGH=``GH_d&IsAEN!ngi+o)`ljPVgiGYUQ08dTnY>$(qV zvLcfU!`7bDEWI!6_Iu3|4-p*g9duJ@M|A=l@5;kr+J!SREyv1`5Z}l3t4Ey1Ug_0Q zw@?pgMQmBwvjXVwQ$&YLjw)B`x}*iF7~GlM?OGF{s9k?kxD}@qw@_TZ7TzSIkGF*L zQHmeSozM8zgI!UePfRU~UpvWdgVJ%|1s%seqFQ`?S9U}f>dX?E52^=V!}2@xsyUI~ z7b_W|TA$q-RcX73R~?eMCtHcw*ZsOUv)dIPsjFtF6V49^dKO-| zNUu6ch)_HsLOmvu2oDLWyMzeEEEKQWEa#KMxB>O?NU=7U1t5A4xbJxy-P~vXs=n#xVoo9T}FD0l~GhlEZakK4a9?ZWqya z7HSvsC8u=9>Ou=`)+O|t*QxS5)8%JFmUVG`H(DoVhQA1GW+(!fik3nR%`JootJ;J^ zbDs6Jof_t;#2A`TK{j^jwKG!$xG8jL#vh|7T+3v2pc0!#Djy0)v4%}@Vgh|>h=9}Q zCgBw9rYQV|%mhKLxxdW9nDs=6R}dk7WGckisf-VJA|h9V3apdyLnD~zZe_B9&`lo4 zt7>CA?HzB1`5CKYNCJTc$(-aFT0i>|`SRREzTkdtH?}%)4-HZZNgAO)c$rUNfwb89 z2=o&>Idhbx$U%eCH30`hCH}o)Y^MMW?_=}}!0=HNlK>1hln;Ma4uAoG0h4r4CgEpl zL74=~BmjojB`N>|00RI60K@CX1^@#90{{a6!+#w>@XY|e8StJmPzIn3KpEbjGC)v7 Y Date: Thu, 13 Nov 2025 11:40:31 -0600 Subject: [PATCH 3/3] revert audio sound loading changes --- examples/audio/audio_sound_loading.c | 33 ++----------------------- examples/audio/audio_sound_loading.png | Bin 15987 -> 4680 bytes 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/examples/audio/audio_sound_loading.c b/examples/audio/audio_sound_loading.c index 997c93b19a93..97e26a450dd7 100644 --- a/examples/audio/audio_sound_loading.c +++ b/examples/audio/audio_sound_loading.c @@ -4,7 +4,7 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 1.1, last time updated with raylib 6.0 +* Example originally created with raylib 1.1, last time updated with raylib 3.5 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software @@ -14,8 +14,6 @@ ********************************************************************************************/ #include "raylib.h" -#include // Required for: malloc(), free() -#include // Required for: memcpy() //------------------------------------------------------------------------------------ // Program main entry point @@ -34,17 +32,6 @@ int main(void) Sound fxWav = LoadSound("resources/sound.wav"); // Load WAV audio file Sound fxOgg = LoadSound("resources/target.ogg"); // Load OGG audio file - bool soundReversed = false; - - float *soundData = malloc(sizeof(float)*fxWav.frameCount*fxWav.stream.channels); - float *scratchSoundData = malloc(sizeof(float)*fxWav.frameCount*fxWav.stream.channels); - - Wave wave = LoadWave("resources/sound.wav"); - // Sounds always have 32bit sampleSize: - WaveFormat(&wave, fxWav.stream.sampleRate, 32, fxWav.stream.channels); - memcpy(soundData, wave.data, sizeof(float)*fxWav.frameCount*fxWav.stream.channels); - UnloadWave(wave); - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -53,18 +40,6 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (IsKeyPressed(KEY_R)) - { - soundReversed = !soundReversed; - for (unsigned int i = 0; i < fxWav.frameCount; i++) - { - unsigned int src = (soundReversed)? fxWav.frameCount - 1 - i : i; - // Sounds always have STEREO channels: - scratchSoundData[i * fxWav.stream.channels + 0] = soundData[src * fxWav.stream.channels + 0]; - scratchSoundData[i * fxWav.stream.channels + 1] = soundData[src * fxWav.stream.channels + 1]; - } - UpdateSound(fxWav, scratchSoundData, fxWav.frameCount); - } if (IsKeyPressed(KEY_SPACE)) PlaySound(fxWav); // Play WAV sound if (IsKeyPressed(KEY_ENTER)) PlaySound(fxOgg); // Play OGG sound //---------------------------------------------------------------------------------- @@ -76,9 +51,7 @@ int main(void) ClearBackground(RAYWHITE); DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, LIGHTGRAY); - DrawText(TextFormat("Press R to REVERSE the WAV sound : "), 120, 220, 20, LIGHTGRAY); - DrawText((soundReversed)? "BACKWARDS" : "FORWARDS", 525, 220, 20, (soundReversed)? MAROON : DARKGREEN); - DrawText("Press ENTER to PLAY the OGG sound!", 200, 260, 20, LIGHTGRAY); + DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, LIGHTGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -86,8 +59,6 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - free(soundData); - free(scratchSoundData); UnloadSound(fxWav); // Unload sound data UnloadSound(fxOgg); // Unload sound data diff --git a/examples/audio/audio_sound_loading.png b/examples/audio/audio_sound_loading.png index 64b463f6c0f820c7837fe05c2966378d833542f1..94e0a842e1247f363c622b203472225c99514c4f 100644 GIT binary patch literal 4680 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