From ee4f4a29c2462d8d0b2542f55900f202a61217c7 Mon Sep 17 00:00:00 2001 From: dan-hoang <56205882+dan-hoang@users.noreply.github.com> Date: Thu, 12 Mar 2026 03:33:47 -0700 Subject: [PATCH] [examples] Update audio_raw_stream.c (#5637) * Update audio_raw_stream.c * Update audio_raw_stream.c to more closely follow raylib coding conventions * Update audio_raw_stream.c to more closely follow raylib coding conventions I accidentally put the file in the wrong folder. * Delete examples/audio_raw_stream.c * Remove spaces before asterisks in comments in audio_raw_stream.c * Put SetTargetFPS(30) before while (!WindowShouldClose()) in audio_raw_stream.c --- examples/audio/audio_raw_stream.c | 196 +++++++++------------------- examples/audio/audio_raw_stream.png | Bin 16736 -> 16468 bytes 2 files changed, 64 insertions(+), 132 deletions(-) diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index b327e92af..536f8c311 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -16,41 +16,10 @@ ********************************************************************************************/ #include "raylib.h" +#include -#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; - } -} +#define BUFFER_SIZE 4096 +#define SAMPLE_RATE 44100 //------------------------------------------------------------------------------------ // Program main entry point @@ -64,43 +33,24 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream"); - InitAudioDevice(); // Initialize audio device + InitAudioDevice(); - SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE); + // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE + SetAudioStreamBufferSizeDefault(BUFFER_SIZE); + float buffer[BUFFER_SIZE] = {}; - // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) - AudioStream stream = LoadAudioStream(44100, 16, 1); + // 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); - SetAudioStreamCallback(stream, AudioInputCallback); + int sineFrequency = 440; + int newSineFrequency = 440; + int sineIndex = 0; + double sineStartTime = 0.0; - // 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 + SetTargetFPS(30); //-------------------------------------------------------------------------------------- // Main game loop @@ -108,92 +58,77 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - mousePosition = GetMousePosition(); - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) + if (IsKeyDown(KEY_UP)) { - float fp = (float)(mousePosition.y); - frequency = 40.0f + (float)(fp); + newSineFrequency += 10; + if (newSineFrequency > 12500) newSineFrequency = 12500; + } - float pan = (float)(mousePosition.x)/(float)screenWidth; + if (IsKeyDown(KEY_DOWN)) + { + newSineFrequency -= 10; + if (newSineFrequency < 20) newSineFrequency = 20; + } + + if (IsKeyDown(KEY_LEFT)) + { + pan -= 0.01f; + if (pan < -1.0f) pan = -1.0f; SetAudioStreamPan(stream, pan); } - // Rewrite the sine wave - // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. - if (frequency != oldFrequency) + if (IsKeyDown(KEY_RIGHT)) { - // 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; + pan += 0.01f; + if (pan > 1.0f) pan = 1.0f; + SetAudioStreamPan(stream, pan); } - /* - // 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) + for (int i = 0; i < BUFFER_SIZE; i++) { - // Start by trying to write the whole chunk at once - int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor; + int wavelength = SAMPLE_RATE/sineFrequency; + buffer[i] = sin(2*PI*sineIndex/wavelength); + sineIndex++; - // 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; + if (sineIndex >= wavelength) + { + sineFrequency = newSineFrequency; + sineIndex = 0; + sineStartTime = GetTime(); + } } - // Copy finished frame to audio stream - UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); + UpdateAudioStream(stream, buffer, BUFFER_SIZE); } - */ + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); + ClearBackground(RAYWHITE); - 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); - 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); + int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE; + int windowSize = 0.1f*SAMPLE_RATE; + int wavelength = SAMPLE_RATE/sineFrequency; - // 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); - } + // 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(); //---------------------------------------------------------------------------------- @@ -201,9 +136,6 @@ int main(void) // 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) diff --git a/examples/audio/audio_raw_stream.png b/examples/audio/audio_raw_stream.png index 344f4a71077431c9f50c72e2815552acef26f7ed..b061abd2df6350a81782d55c1f960c08fee0e1de 100644 GIT binary patch delta 4666 zcmaFR#CWBFae^|FgY-t#sf^)_Vo7^NWMz57c9pyg+7s0g>b`b?%2m6n-Tk*b(^l-L zU(xMDhPkP&Y(|gtL zj;->~%ZmwD3nfQzT*HtX)atSwjAT5U41 z`nKEaB^L!GcpD$eGac#sa>%W#aP3}ok)Rj9Iz#<)cm!s>RJcBW%O~-*-*qmW-rtfY zP_T6N#3=S{J?9#Fu4zr)Xe3?Fxa%#Wq6w4diQ`N9LMCO&?|i3xEq2N;dE1(fxrS02 z)rs#^uhkW$vt`JhzrtsqRBX2Kqj#3YGyukOiKwQ_B}+nOi0(pYxKAKM#;@;4lNo^jU*8qzIJ4fmFN239YT zx@(iw`-Fd+;Maq8hy2&hantzc(fj_OP~Ku=g~h2KCo4+I_+B{I&~vV)=S+-2K($^$ ztJ&5xjf7{xw{4qi2tR#EPvSzuYT!$~!aZm0#5S zTQ08)7Ei6uG<9~(>|0v?OMWh+6Bc(gf%{jq@rOlgzXX;*705;sv)Aluk zqo+k+!FRd27eh=foDxj#xd%VW;OGH6s*OR{Ibq2icc-@oGIJRv&rSy=4|c{`?-?a~ zPfz}6B*}GAprQ4^3!W1hQIjWHNjffw<`CG+p0Z(%n6q|yK$hO#uff_fZvKo?y18Hn z!H5^_3>&Kj3+_5QuB!8TEA3mXY|Y}9?%-BDS&>O{@&<7h+Y57=Twk3%c7;nZ>nsD$ zH0h1*pU=Ua1q!gX11ESHv(6SQXjfP#$qzDQwX!kTkl=rl6(yw;8q$mvj?CNNZ&&Q_ z#)oOw&n4#^1ZMqIxcc|Svx%?kgVLKcc-bSolvDV2{yR8z(n;QUCrjpOd@J7GyJ(bY z!UA(?MLWaV?+z2P?bb=QUHoDp#pG-Ww&e1AkR=ff&#D~?&iV*gJN0U-oVZ);_-Y=% z*Y~msuj;c_s++P3zV^>LuD|UO1a>X4K?*`5ZC2T1sp_3Oficj8e&uYz zy|}XYpwVZ?*L%5J4mCzS1NG|AmZ5Luhsc8+?U_G3a~Qg)DuJoyPJdrZ%=;}-=M z#B+eksb!NNGRp8WoX&{2tj6N@eyxOWVf2KaV}DPqWpR64XY%eOZ{tOy`YmUco3OZD7WjMlLW1V!m$Kho${pOC z7RltC4rTKWw37OM%!hXfq_oEI~*DQjEy9{Zco%qGGFQva+v>u1fD zvlbSYF1!$0b5ZRfsA4d6uvTbZC~mduUSUGf?TkD9cY^Xv-nl=nQ*ch$az}`WIqMDWwi>*Uu*&JMZx_w*T1Qj640)&UXk%oc*qB zZh9ff|8@e;*^5T=%ToU(=-Yz>ATv^}_gn+dCL#H~4pKb9(--sl{c2&@`eFE z1wC*ul?FdT`GRcr6pfxWJ$d&Q`QDQ5{XH>y6)5QL@IMof(EaRw`p-o(^-|{qjWq`( zY#*dQ`1}`C0h~0Re2`Jb`U4+_<=%jty{|6@u9-M7+wF1PlWBd63+<)8pHOsIyDNFi zUc(*E4N*VqO7c&C@OZiSXWfq}5uh?}>-ouwlD76KXKCBt2r!V{C+rhn$i`Dr*I_Xs%cU9{uMRBTxNPGT;jq_5}XjYjgO3SK!| zq<)x~1f>3RI12W+e>$kJnH+DgWZS%) z*+^*lQ#XIU#wGhN?ohXv=leR@+3mfjWN)=s_QH#wl3#31ar^xD#M$rARQoN#qZb_V z_ufrbl(e#Ki)U|<3*Nn0{29-=ImS}o7pqDwRcz4b?bVwpbMGZM{(d_4Kebu>w(a5F zyaUg7U)&*oV+SZ1=1TWXZe*00++d(&nNsS&mL!#%Dv4oBk`O zf4QBmc{TN?^V&ZWy}vK+Snk?sZ^dkM>o%-ip*(qmIJ>onkn}>$n~fHhDJJrl)LB-D ze*fa-y!d5Pm7>G?r#6edHQMr5$)5eLyxm0#WXf?-Cg+7`
  • *Y+DA(P?P<|7403i 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