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 zcmeHPdpy+n`k%ovwKGhuQ!}G!INGhYRTCpEi(xdA5K*l&T~=}#R@N=bh>?tHhoOxQ zMKrmj9m}545fPFU%D8lKT`D_>E~cGS?D>4Zx~R<9*PgSl-|vt4XUxs#c|Once%{aX z`8*GYoYpfivk9{i2n5D?u7Rn^wUYU7^mJ6}G3Iuym- zKqjaN^yEB*ppZFYr-hhkF%~!N12R0elBMB65yo>XSt#B#4`f~_mHtW9^dEv>DEv5D zj87EXn9Eax`fn_5n8W+*oLn7BCv_;2s{^D$iyZxc3XdjL*!X{{0&<>vxi*oA*<2bj z;Cz93a1%qAxv0$|yo}#&>y=j$l@~Y4+F>%)NYAW)5ghu&HSPC?3NeR(L8iqbvO>7a z9Sg#(7iNZzzZ9Y zeLRVIjt3OH`MG>-e~c_0M(R;iZ0oL^PS|+Fmvnwa|K_%%&q?MH2h&#)maun>e0no7 z$XlI7ywR#@=^nhQ=&_ye>L1Q#w9fU(?DG_jyow3rIvS_BeD3Lj>|9wp+x1!E;Ek&O z70j}@&1|na<72JmI8l3K=?rp9Dc zU%=w>iXTh6gRT7=jts76o@}=n3J@em`54pI_#|Y9Jtw6j@j}n(kPg2G;LiKzV&u4^ zUB@ZLcZ7uw*J-+Htn~G9>nw0SW4lEW1cMCti(EStI=TEK{aso$k_@c}U;05#dqA9) z{QxZjrxo(JPl1sSU^8V4)5RN&S6jPLx*0BgZw!wxpt+syWLOWA7XT$2k12v=+CwBh zzFuBb0fC&@N(s7-#hjjxCpE&BYj_l%m`7U{=H$D){He>b1$0Lw!C==r){(>^ir8}k zx@cDUk?V$a9D?=D)ac$+dazuggb{OM|C+(Z+Sb{6zKsZUicS$@^!l5PPX1;c2I<=v zkQt|2fY*W+KC5p8FYGe$-WT7z1PgP#jC5-3e9NpQuVcauxH9^_QwCnt8+BVnd&|1h*#+A(T-QzqKg9bH&ot1Qpts zu;`YDZ)Cl_snABkLCY%#-zFOL4XkdcqiR;_taUPbD3tmOdl#rcfj7W@tJb^EIR1LnQ9Rb!+cd8@ z7iq3$n?8nfh>2OIwj_O}PW+9jzR&bRVasxNQd$`hjgx6^0F0DP)1}$hfu`=Kg<4K> zF8x)dt5clH?@c~Vmxb_B65e7iW~mx$x0bpe*E3{A9V?uaphpJbn^pjecn2Y`)CQrm z=mrPN{GM-N?T{btzHhyxRZwL(YBf~;@qy}SX74Pcxb}&x5PlQuA1kLOs8%%6Sr*#l zy8?CIh*VRmaSL7V_V{;J{=)7_k=2Ss!qIApt7e}bZo>_*UFfoYUP{nA+n~#~!Wk&@ z8x+L#If^O-L_N#3=;R$nU@q$D9cn)>G$?K}idQ>c=THPbC&-Q-K>XN{Tgz!@m0MlJ zW_neS;$I|f>EYLo{}Ru>K*aiLBXoaqsXtMr&1)U3b1I`zZuj|7Mgnu&#T1G(EjY&D zI+s1RlG)!<@se|g%VzW56$~4U)EqPX4r5k?lf<_r_MbD+JU%7%+URneVC5TIaeqsvsA{M9VY2g4a z+^u80Rxe+9iaw73DXt9^XLV}*ip6cqKVTlp7&U)?Mh$_uG$@4E8N^nc{^96y9Wf7}yGur4T?FqH!*?MGVWK+RN=B7blBYl$| zm-=6U49Wc$(T87vhVYK2jy2ykB+NtTd!u#&nIi#htWJKd`4?*R`j=wEldV{sfhBi) zTGr+hn3NLIFo(?BFISbBo78Am4G(EeT(sRjRr7cYf(#&w`CGH48rQCZWMH}0(G0v1 zWOt(XXFq>Rk5+kvqi`$3r;`tEozIng0EpK}0ntGj;y9iC+P=Q~spJqzWa|wtQwT61 zEMJ0&&LW>OEgfJ7+#-q^Vqq`~vcKAG4d3#A`nJjN-hDr4r*SosT>h5&aFmLYw6p*GgP&HntIaPJ)?UehI0V0XUG z?l`=SVi?L{DbJYIR|^0n&{-DWT5q)(Q&7xmh_<@8x*hqg}9aK1N}@o&gJfeO`mdT>dpE) zBMc#+r4R1W-E|b8J~M<$H|fAcT+`our(Qxo)9b=SxBh${2$nuEUp`-JBOnEDerG8J zKVV!8dMgTj-!KYl!TkE3OWwW&&Y_#>3J&d>nk=Rw1nU0$pjo$eYxBx*ckH6Z1ju{k zlX2^e;$=`)Gy7fAy5qvjc8D4lTYki=HCMd^b^N{yQ?Mkt9f^1FQ)CXSE@ajfX}6^L zOjT+J5O*(SKa4mLI$CTPwddH_Gk_GBn^&f!VO^`s8Li7OCo}s1}9zf`IxdRCQ?uKA!!&W*pjT)j<^93?(2_$Qrm1_1#kGyeooHb^(w;c~96&1h{ zAgL5%GdHJNf^J{?6GiC&p4jP-*W-dZrh~-9tkp!z@H>Rv%{s+>85uq-5G4McE@zJt z&5gYRvh~8xkzTp^?9tfDwf;s4QV~2R5dJn+-h1{(LWj&U)2*?upJOy`9&TO#sKEhd zB3s|f#qW? z03J@MzZX~Olzx-cD2rL#(^58qi+@q&(g&ph;XZEgWLCo=2V})w2T~bqHFHr-j!p9Fh8dwH^*{cdjlcjK@eap>1A3$J~ z!U@z-rsY>E$(87)DD0U8cZIe318yk*@O3MlL)d*3Izgm zhoT7$#elA?8{wd905xXY#pvIHv*|dD-2u=L_MCY1`KT?(LQr~L3bF~sW-o#8U;5SU zSr}CUQKKy;#%M7n`vYqHJD3KL6g}_+)c7z|)uJc9)_mXHNGOGK@d`594Ey{AFjP(Y z?*cnh-GM-9tHzVj`%Wj@iD!-pHwYFjgm@y7w|oGgDXBYyxXUP^Q=7Wve?*I&delMD z1V}!;KcoRu-b+El$)|=}DLHZ#lyl8k+%6Mz1mP6mCs$A;gNeY5_vcF95E3+pA_5}| zx8-B}77}lsz4KAdDyC!ftEefCS)s;V2bFVqCqrQ!*1|f(n5*S&>xWCZO8E=UQ`tDg zLC1*;#qsQW*=B%ns5w~>!3R1Dn?pld`YUs?qWw&ug1nP|Zi_9bKUUqXb68mjlYoJ- zqLpWENQDrwK{@ve9isUDi3{-OVY2nH6_${;Sr|#1n}EoE_B}xEpk^5UQVQmeM&kVQ zi2$Vrfr$L;5^66kMDmGT`yDJ7B*?7lrgDRbW-9&DDwv2 zeB<5^Cv;}=_B<8)%z-xXqlFjrI2Pj$BccVx4S0uTQs4yOp)>KDkFR2wxbja>jG6uX z6jC7;*C+#r4Kf$8$qNjpns~J)9Nqmd7!87S=gDf5P69`i+bvC?vMn==0@$M9eF>p0DLEz)CuGFqQ_(H7S2)-HW?&Ct>pVOiHe-Z}OxeLwJ=*QeJ zSrjWD!JZIdUy}g>ij{fYuyyW%c;!?<#XgF6PDgT*o42W7>$lRKBGqd0Z23t0n~HWA zm3z1JYSk)Vo8dVJtIl0uk+VVgEH04Xx1Hjeolqaj8)a4G?khGW$fO3A>MkyUkTx03 z($%is$61@g&}n$A?;h;Dy}IEdEwi5?h%@{T=Wg!gFE_@8!{tY)1EVqY66^R57RX7R zTI1UF&z=t5`dxtkP99o8UX;vH5wO8@@SQ&oKPZR$ypv+zvBa~<)>=PeKjLC_~(ml$Vq?c6!~92`UN{l^+WT&s*EpzdKqxB3pCC; zZP7Y&=2Zp*O&JXTi$AGQ1_L}?uFMR|%m7`h|J6gm%FIAe`D0gn$}$NmN9Es1D$69U zvP{A#&kU0lwDQcL3-{FaQ-0h`$|i+Uum6Gzk7H0K#_7dYb}kxBdSC DTKu{y literal 0 HcmV?d00001