From 3bedd89e1dddda12bbe14040d23cbf7b169ab305 Mon Sep 17 00:00:00 2001 From: jussi Date: Mon, 8 Sep 2025 22:36:40 +0300 Subject: AudioStream management functions. --- API.md | 147 ++++++++++ README.md | 2 - ReiLua_API.lua | 128 +++++++++ changelog | 3 + devnotes | 3 +- examples/audio_stream_effects/main.lua | 100 +++++++ examples/resources/clib/.gitignore | 1 + examples/resources/clib/audioProcess.c | 100 +++++++ ...unkala [Retro Game Music Pack] Title Screen.ogg | Bin 0 -> 268705 bytes examples/resources/music/LICENCE | 2 + include/audio.h | 21 ++ include/lua_core.h | 3 + src/audio.c | 306 +++++++++++++++++++++ src/core.c | 6 +- src/lua_core.c | 84 ++++++ 15 files changed, 901 insertions(+), 5 deletions(-) create mode 100644 examples/audio_stream_effects/main.lua create mode 100644 examples/resources/clib/.gitignore create mode 100644 examples/resources/clib/audioProcess.c create mode 100644 examples/resources/music/Juhani Junkala [Retro Game Music Pack] Title Screen.ogg create mode 100644 examples/resources/music/LICENCE diff --git a/API.md b/API.md index 2d46f87..08a3b40 100644 --- a/API.md +++ b/API.md @@ -9401,6 +9401,14 @@ Set pan for a sound (0.5 is center) --- +> stream = RL.GetSoundStream( Sound sound ) + +Get sound audio stream. Return as lightuserdata + +- Success return AudioStream + +--- + > RL.WaveFormat( Wave wave, int sampleRate, int sampleSize, int channels ) Convert wave data to desired format @@ -9555,6 +9563,145 @@ Get current music time played (in seconds) --- +> stream = RL.GetMusicStream( Music music ) + +Get music audio stream. Return as lightuserdata + +- Success return AudioStream + +--- + +## Audio - AudioStream management functions + +--- + +> audioStream = RL.LoadAudioStream( int sampleRate, int sampleSize, int channels ) + +Load audio stream (to stream raw audio pcm data) + +- Success return AudioStream + +--- + +> isValid = RL.IsAudioStreamValid( AudioStream stream ) + +Checks if an audio stream is valid (buffers initialized) + +- Success return bool + +--- + +> RL.UnloadAudioStream( AudioStream stream ) + +Unload audio stream and free memory + +--- + +> RL.UpdateAudioStream( AudioStream stream, Buffer data, int frameCount ) + +Update audio stream buffers with data + +--- + +> isProcessed = RL.IsAudioStreamProcessed( AudioStream stream ) + +Check if any audio stream buffers requires refill + +- Success return bool + +--- + +> RL.PlayAudioStream( AudioStream stream ) + +Play audio stream + +--- + +> RL.PauseAudioStream( AudioStream stream ) + +Pause audio stream + +--- + +> RL.ResumeAudioStream( AudioStream stream ) + +Resume audio stream + +--- + +> isPlaying = RL.IsAudioStreamPlaying( AudioStream stream ) + +Check if audio stream is playing + +- Success return bool + +--- + +> RL.StopAudioStream( AudioStream stream ) + +Stop audio stream + +--- + +> RL.SetAudioStreamVolume( AudioStream stream, float volume ) + +Set volume for audio stream (1.0 is max level) + +--- + +> RL.SetAudioStreamPitch( AudioStream stream, float pitch ) + +Set pitch for audio stream (1.0 is base level) + +--- + +> RL.SetAudioStreamPan( AudioStream stream, float pan ) + +Set pan for audio stream (0.5 is centered) + +--- + +> RL.SetAudioStreamBufferSizeDefault( int size ) + +Default size for new audio streams + +--- + +> RL.SetAudioStreamCallback( AudioStream stream, AudioCallback callback ) + +Audio thread callback to request new data. +AudioCallback should be lightuserdata function pointer + +--- + +> RL.AttachAudioStreamProcessor( AudioStream stream, AudioCallback processor ) + +Attach audio stream processor to stream, receives the samples as 'float'. +AudioCallback should be lightuserdata function pointer + +--- + +> RL.DetachAudioStreamProcessor( AudioStream stream, AudioCallback processor ) + +Detach audio stream processor from stream. +AudioCallback should be lightuserdata function pointer + +--- + +> RL.AttachAudioMixedProcessor( AudioCallback processor ) + +Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'. +AudioCallback should be lightuserdata function pointer + +--- + +> RL.DetachAudioMixedProcessor( AudioCallback processor ) + +Detach audio stream processor from the entire audio pipeline. +AudioCallback should be lightuserdata function pointer + +--- + ## Math - Utils --- diff --git a/README.md b/README.md index 1f5772a..3615ebc 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,6 @@ List of some MISSING features that are planned to be included. For specific func * Core * VR stereo config functions for VR simulator -* Audio - * AudioStream management functions ## Roadmap diff --git a/ReiLua_API.lua b/ReiLua_API.lua index e5640d6..99e1c7a 100644 --- a/ReiLua_API.lua +++ b/ReiLua_API.lua @@ -5823,6 +5823,12 @@ function RL.SetSoundPitch( sound, pitch ) end ---@return any RL.SetSoundPan function RL.SetSoundPan( sound, pan ) end +---Get sound audio stream. Return as lightuserdata +---- Success return AudioStream +---@param sound any +---@return any stream +function RL.GetSoundStream( sound ) end + ---Convert wave data to desired format ---@param wave any ---@param sampleRate integer @@ -5955,6 +5961,128 @@ function RL.GetMusicTimeLength( music ) end ---@return any timePlayed function RL.GetMusicTimePlayed( music ) end +---Get music audio stream. Return as lightuserdata +---- Success return AudioStream +---@param music any +---@return any stream +function RL.GetMusicStream( music ) end + +-- Audio - AudioStream management functions + +---Load audio stream (to stream raw audio pcm data) +---- Success return AudioStream +---@param sampleRate integer +---@param sampleSize integer +---@param channels integer +---@return any audioStream +function RL.LoadAudioStream( sampleRate, sampleSize, channels ) end + +---Checks if an audio stream is valid (buffers initialized) +---- Success return bool +---@param stream any +---@return any isValid +function RL.IsAudioStreamValid( stream ) end + +---Unload audio stream and free memory +---@param stream any +---@return any RL.UnloadAudioStream +function RL.UnloadAudioStream( stream ) end + +---Update audio stream buffers with data +---@param stream any +---@param data any +---@param frameCount integer +---@return any RL.UpdateAudioStream +function RL.UpdateAudioStream( stream, data, frameCount ) end + +---Check if any audio stream buffers requires refill +---- Success return bool +---@param stream any +---@return any isProcessed +function RL.IsAudioStreamProcessed( stream ) end + +---Play audio stream +---@param stream any +---@return any RL.PlayAudioStream +function RL.PlayAudioStream( stream ) end + +---Pause audio stream +---@param stream any +---@return any RL.PauseAudioStream +function RL.PauseAudioStream( stream ) end + +---Resume audio stream +---@param stream any +---@return any RL.ResumeAudioStream +function RL.ResumeAudioStream( stream ) end + +---Check if audio stream is playing +---- Success return bool +---@param stream any +---@return any isPlaying +function RL.IsAudioStreamPlaying( stream ) end + +---Stop audio stream +---@param stream any +---@return any RL.StopAudioStream +function RL.StopAudioStream( stream ) end + +---Set volume for audio stream (1.0 is max level) +---@param stream any +---@param volume number +---@return any RL.SetAudioStreamVolume +function RL.SetAudioStreamVolume( stream, volume ) end + +---Set pitch for audio stream (1.0 is base level) +---@param stream any +---@param pitch number +---@return any RL.SetAudioStreamPitch +function RL.SetAudioStreamPitch( stream, pitch ) end + +---Set pan for audio stream (0.5 is centered) +---@param stream any +---@param pan number +---@return any RL.SetAudioStreamPan +function RL.SetAudioStreamPan( stream, pan ) end + +---Default size for new audio streams +---@param size integer +---@return any RL.SetAudioStreamBufferSizeDefault +function RL.SetAudioStreamBufferSizeDefault( size ) end + +---Audio thread callback to request new data. +---AudioCallback should be lightuserdata function pointer +---@param stream any +---@param callback any +---@return any RL.SetAudioStreamCallback +function RL.SetAudioStreamCallback( stream, callback ) end + +---Attach audio stream processor to stream, receives the samples as 'float'. +---AudioCallback should be lightuserdata function pointer +---@param stream any +---@param processor any +---@return any RL.AttachAudioStreamProcessor +function RL.AttachAudioStreamProcessor( stream, processor ) end + +---Detach audio stream processor from stream. +---AudioCallback should be lightuserdata function pointer +---@param stream any +---@param processor any +---@return any RL.DetachAudioStreamProcessor +function RL.DetachAudioStreamProcessor( stream, processor ) end + +---Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'. +---AudioCallback should be lightuserdata function pointer +---@param processor any +---@return any RL.AttachAudioMixedProcessor +function RL.AttachAudioMixedProcessor( processor ) end + +---Detach audio stream processor from the entire audio pipeline. +---AudioCallback should be lightuserdata function pointer +---@param processor any +---@return any RL.DetachAudioMixedProcessor +function RL.DetachAudioMixedProcessor( processor ) end + -- Math - Utils ---Round float value diff --git a/changelog b/changelog index 4e7c33c..27f0d4c 100644 --- a/changelog +++ b/changelog @@ -56,6 +56,9 @@ DETAILED CHANGES: - ADDED: MeasureTextBoxed. - CHANGE: DrawTextBoxedEx to DrawTextBoxed. - CHANGE: DrawMeshInstanced takes transforms as Buffer. Much better performance. + - ADDED: AudioStream management functions. + - ADDED: GetSoundStream and GetMusicStream. + - ADDED: Audio stream effects example. ------------------------------------------------------------------------ Release: ReiLua version 0.8.0 Using Raylib 5.0 and Forked Raygui 4.0 diff --git a/devnotes b/devnotes index ffc82c5..4fa0023 100644 --- a/devnotes +++ b/devnotes @@ -11,8 +11,6 @@ Backlog { * Text input not working on gui. Could this be Raylib issue? * Haptic functions. * SDL3 GPU? - * Audio - * AudioStream. * Models * Material mapType range checks. * Mesh bone weight management? @@ -27,6 +25,7 @@ Bugs { } Notes { + * WARNING: BREAKING CHANGE: LoadFontData() has been redesigned! https://github.com/raysan5/raylib/commit/29ce5d8aa9d261eba395e24437e08c6bd744616e } Needs Testing { diff --git a/examples/audio_stream_effects/main.lua b/examples/audio_stream_effects/main.lua new file mode 100644 index 0000000..575a5ce --- /dev/null +++ b/examples/audio_stream_effects/main.lua @@ -0,0 +1,100 @@ +-- Based on raylib audio_stream_effects example + +package.path = package.path..";"..RL.GetBasePath().."../resources/lib/?.lua" +package.cpath = package.cpath..";"..RL.GetBasePath().."../resources/clib/?.so" + +Vector2 = require( "vector2" ) +Rect = require( "rectangle" ) + +-- NOTE! You have to compile the C lib containing the effects in resources/clib/audioProcess.c +-- Implementing audio processors in Lua would probably be too slow. +AudioProcess = require( "audioProcess" ) + +local music = nil +local rect = Rect:new() +local rect2 = Rect:new() +local effectLPF = nil +local effectDelay = nil +local enableEffectLPF = false +local enableEffectDelay = false + +function RL.init() + RL.SetWindowTitle( "Audio stream effects" ) + RL.SetWindowState( RL.FLAG_VSYNC_HINT ) + RL.InitAudioDevice() + + music = RL.LoadMusicStream( RL.GetBasePath().."../resources/music/Juhani Junkala [Retro Game Music Pack] Title Screen.ogg" ) + + RL.PlayMusicStream( music ) + + AudioProcess.init() + effectLPF = AudioProcess.AudioProcessEffectLPF() + effectDelay = AudioProcess.AudioProcessEffectDelay() + + local winSize = Vector2:newT( RL.GetScreenSize() ) + local barWidth = 256 + + rect:set( + winSize.x / 2 - barWidth / 2, + winSize.y / 2 - 5, + barWidth, + 10 + ) + rect2:setR( rect ) +end + +function RL.update( delta ) + RL.UpdateMusicStream( music ) + + -- Add/Remove effect: lowpass filter + if RL.IsKeyPressed( RL.KEY_F ) then + local stream = RL.GetMusicStream( music ) + enableEffectLPF = not enableEffectLPF + + if enableEffectLPF then + RL.AttachAudioStreamProcessor( stream, effectLPF ) + -- RL.AttachAudioMixedProcessor( effectLPF ) + else + RL.DetachAudioStreamProcessor( stream, effectLPF ) + -- RL.DetachAudioMixedProcessor( effectLPF ) + end + end + + -- Add/Remove effect: delay + if RL.IsKeyPressed( RL.KEY_D ) then + local stream = RL.GetMusicStream( music ) + enableEffectDelay = not enableEffectDelay + + if enableEffectDelay then + RL.AttachAudioStreamProcessor( stream, effectDelay ) + -- RL.AttachAudioMixedProcessor( effectDelay ) + else + RL.DetachAudioStreamProcessor( stream, effectDelay ) + -- RL.DetachAudioMixedProcessor( effectDelay ) + end + end +end + +function RL.draw() + local musicLen = RL.GetMusicTimeLength( music ) + local musicPos = RL.GetMusicTimePlayed( music ) + + RL.ClearBackground( RL.RAYWHITE ) + + RL.DrawText( + "PRESS F TO TOGGLE LPF EFFECT "..( enableEffectLPF and "ON" or "OFF" ), + Vector2:temp( rect.x - 80, rect.y - 80 ), 20, RL.GRAY + ) + RL.DrawText( + "PRESS D TO TOGGLE DELAY EFFECT "..( enableEffectDelay and "ON" or "OFF" ), + Vector2:temp( rect.x - 90, rect.y - 40 ), 20, RL.GRAY + ) + + RL.DrawRectangleLines( rect + Rect:temp( 0, -1, 1, 1 ), RL.BLACK ) + rect2.width = musicPos / musicLen * rect.width + RL.DrawRectangle( rect2, RL.RED ) +end + +function RL.exit() + AudioProcess.free() +end diff --git a/examples/resources/clib/.gitignore b/examples/resources/clib/.gitignore new file mode 100644 index 0000000..f1fe8d1 --- /dev/null +++ b/examples/resources/clib/.gitignore @@ -0,0 +1 @@ +*.so \ No newline at end of file diff --git a/examples/resources/clib/audioProcess.c b/examples/resources/clib/audioProcess.c new file mode 100644 index 0000000..ad18117 --- /dev/null +++ b/examples/resources/clib/audioProcess.c @@ -0,0 +1,100 @@ +/* Based on raylib audio_stream_effects example */ + +#include +#include +#include +#include +#include +#include + +/* gcc audioProcess.c -shared -o audioProcess.so -fPIC -llua */ + +// Audio effect: lowpass filter +void AudioProcessEffectLPF(void *buffer, unsigned int frames) +{ + 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 + + // Converts the buffer data before using it + float *bufferData = (float *)buffer; + for (unsigned int i = 0; i < frames*2; i += 2) + { + const float l = bufferData[i]; + const float r = bufferData[i + 1]; + + low[0] += k * (l - low[0]); + low[1] += k * (r - low[1]); + bufferData[i] = low[0]; + bufferData[i + 1] = low[1]; + } +} + +static float *delayBuffer = NULL; +static unsigned int delayBufferSize = 0; +static unsigned int delayReadIndex = 2; +static unsigned int delayWriteIndex = 0; + +// Audio effect: delay +static void AudioProcessEffectDelay(void *buffer, unsigned int frames) +{ + for (unsigned int i = 0; i < frames*2; i += 2) + { + float leftDelay = delayBuffer[delayReadIndex++]; // ERROR: Reading buffer -> WHY??? Maybe thread related??? + float rightDelay = delayBuffer[delayReadIndex++]; + + if (delayReadIndex == delayBufferSize) delayReadIndex = 0; + + ((float *)buffer)[i] = 0.5f*((float *)buffer)[i] + 0.5f*leftDelay; + ((float *)buffer)[i + 1] = 0.5f*((float *)buffer)[i + 1] + 0.5f*rightDelay; + + delayBuffer[delayWriteIndex++] = ((float *)buffer)[i]; + delayBuffer[delayWriteIndex++] = ((float *)buffer)[i + 1]; + if (delayWriteIndex == delayBufferSize) delayWriteIndex = 0; + } +} + +/* API. */ + +static int apInit( lua_State* L ) { + // Allocate buffer for the delay effect + delayBufferSize = 48000*2; // 1 second delay (device sampleRate*channels) + delayBuffer = (float *)calloc(delayBufferSize, sizeof(float)); + + return 0; +} + +static int getAudioProcessEffectLPF( lua_State* L ) { + lua_pushlightuserdata( L, AudioProcessEffectLPF ); + + return 1; +} + +static int getAudioProcessEffectDelay( lua_State* L ) { + lua_pushlightuserdata( L, AudioProcessEffectDelay ); + + return 1; +} + +static int apFree( lua_State* L ) { + free(delayBuffer); // Free delay buffer + + return 0; +} + +static const luaL_Reg audioProcess[] = { + { "init", apInit }, + { "AudioProcessEffectLPF", getAudioProcessEffectLPF }, + { "AudioProcessEffectDelay", getAudioProcessEffectDelay }, + { "free", apFree }, + { NULL, NULL } // sentinel. +}; + +int luaopen_audioProcess(lua_State *L) { +#if LUA_VERSION_NUM <= 501 + luaL_openlib( L, "audioProcess", audioProcess, 0 ); +#else + luaL_newlib( L, audioProcess ); +#endif + return 1; +} diff --git a/examples/resources/music/Juhani Junkala [Retro Game Music Pack] Title Screen.ogg b/examples/resources/music/Juhani Junkala [Retro Game Music Pack] Title Screen.ogg new file mode 100644 index 0000000..755ddfb Binary files /dev/null and b/examples/resources/music/Juhani Junkala [Retro Game Music Pack] Title Screen.ogg differ diff --git a/examples/resources/music/LICENCE b/examples/resources/music/LICENCE new file mode 100644 index 0000000..7b94830 --- /dev/null +++ b/examples/resources/music/LICENCE @@ -0,0 +1,2 @@ +Resource Author Licence Source Edits +Juhani Junkala [Retro Game Music Pack] Title Screen Juhani Junkala CC0 https://opengameart.org/content/5-chiptunes-action diff --git a/include/audio.h b/include/audio.h index 6589db7..939bdf9 100644 --- a/include/audio.h +++ b/include/audio.h @@ -52,3 +52,24 @@ int laudioSetMusicLooping( lua_State* L ); int laudioGetMusicLooping( lua_State* L ); int laudioGetMusicTimeLength( lua_State* L ); int laudioGetMusicTimePlayed( lua_State* L ); +int laudioGetMusicStream( lua_State* L ); +/* Audio - AudioStream management functions. */ +int laudioLoadAudioStream( lua_State* L ); +int laudioIsAudioStreamValid( lua_State* L ); +int laudioUnloadAudioStream( lua_State* L ); +int laudioUpdateAudioStream( lua_State* L ); +int laudioIsAudioStreamProcessed( lua_State* L ); +int laudioPlayAudioStream( lua_State* L ); +int laudioPauseAudioStream( lua_State* L ); +int laudioResumeAudioStream( lua_State* L ); +int laudioIsAudioStreamPlaying( lua_State* L ); +int laudioStopAudioStream( lua_State* L ); +int laudioSetAudioStreamVolume( lua_State* L ); +int laudioSetAudioStreamPitch( lua_State* L ); +int laudioSetAudioStreamPan( lua_State* L ); +int laudioSetAudioStreamBufferSizeDefault( lua_State* L ); +int laudioSetAudioStreamCallback( lua_State* L ); +int laudioAttachAudioStreamProcessor( lua_State* L ); +int laudioDetachAudioStreamProcessor( lua_State* L ); +int laudioAttachAudioMixedProcessor( lua_State* L ); +int laudioDetachAudioMixedProcessor( lua_State* L ); diff --git a/include/lua_core.h b/include/lua_core.h index 405f054..8204b49 100644 --- a/include/lua_core.h +++ b/include/lua_core.h @@ -87,6 +87,7 @@ REILUAPI GlyphInfo* uluaGetGlyphInfo( lua_State* L, int index ); REILUAPI Wave* uluaGetWave( lua_State* L, int index ); REILUAPI Sound* uluaGetSound( lua_State* L, int index ); REILUAPI Music* uluaGetMusic( lua_State* L, int index ); +REILUAPI AudioStream* uluaGetAudioStream( lua_State* L, int index ); REILUAPI Light* uluaGetLight( lua_State* L, int index ); REILUAPI Material* uluaGetMaterial( lua_State* L, int index ); REILUAPI Model* uluaGetModel( lua_State* L, int index ); @@ -123,6 +124,7 @@ REILUAPI void uluaPushWave( lua_State* L, Wave wave ); REILUAPI void uluaPushSound( lua_State* L, Sound sound ); REILUAPI void uluaPushSoundAlias( lua_State* L, Sound alias ); REILUAPI void uluaPushMusic( lua_State* L, Music music ); +REILUAPI void uluaPushAudioStream( lua_State* L, AudioStream stream ); REILUAPI void uluaPushLight( lua_State* L, Light light ); REILUAPI void uluaPushMaterial( lua_State* L, Material material ); REILUAPI void uluaPushMesh( lua_State* L, Mesh mesh ); @@ -143,6 +145,7 @@ void uluaUnloadWave( Wave* wave ); void uluaUnloadSound( Sound* sound ); void uluaUnloadSoundAlias( Sound* sound ); void uluaUnloadMusic( Music* music ); +void uluaUnloadAudioStream( AudioStream* stream ); void uluaUnloadMaterial( Material* material, bool freeAll ); void uluaUnloadMesh( Mesh* mesh ); void uluaUnloadModel( Model* model ); diff --git a/src/audio.c b/src/audio.c index 47e71ef..d07f806 100644 --- a/src/audio.c +++ b/src/audio.c @@ -386,6 +386,21 @@ int laudioSetSoundPan( lua_State* L ) { return 0; } +/* +> stream = RL.GetSoundStream( Sound sound ) + +Get sound audio stream. Return as lightuserdata + +- Success return AudioStream +*/ +int laudioGetSoundStream( lua_State* L ) { + Sound* sound = uluaGetSound( L, 1 ); + + lua_pushlightuserdata( L, &sound->stream ); + + return 1; +} + /* > RL.WaveFormat( Wave wave, int sampleRate, int sampleSize, int channels ) @@ -716,3 +731,294 @@ int laudioGetMusicTimePlayed( lua_State* L ) { return 1; } + +static float arr[10] = {}; + +/* +> stream = RL.GetMusicStream( Music music ) + +Get music audio stream. Return as lightuserdata + +- Success return AudioStream +*/ +int laudioGetMusicStream( lua_State* L ) { + Music* music = uluaGetMusic( L, 1 ); + + lua_pushlightuserdata( L, &music->stream ); + + return 1; +} + +/* +## Audio - AudioStream management functions +*/ + +/* +> audioStream = RL.LoadAudioStream( int sampleRate, int sampleSize, int channels ) + +Load audio stream (to stream raw audio pcm data) + +- Success return AudioStream +*/ +int laudioLoadAudioStream( lua_State* L ) { + int sampleRate = luaL_checkinteger( L, 1 ); + int sampleSize = luaL_checkinteger( L, 2 ); + int channels = luaL_checkinteger( L, 3 ); + + uluaPushAudioStream( L, LoadAudioStream( sampleRate, sampleSize, channels ) ); + + return 1; +} + +/* +> isValid = RL.IsAudioStreamValid( AudioStream stream ) + +Checks if an audio stream is valid (buffers initialized) + +- Success return bool +*/ +int laudioIsAudioStreamValid( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + lua_pushboolean( L, IsAudioStreamValid( *stream ) ); + + return 1; +} + +/* +> RL.UnloadAudioStream( AudioStream stream ) + +Unload audio stream and free memory +*/ +int laudioUnloadAudioStream( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + uluaUnloadAudioStream( stream ); + + return 0; +} + +/* +> RL.UpdateAudioStream( AudioStream stream, Buffer data, int frameCount ) + +Update audio stream buffers with data +*/ +int laudioUpdateAudioStream( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + Buffer* buffer = uluaGetBuffer( L, 2 ); + int frameCount = luaL_checkinteger( L, 3 ); + + UpdateAudioStream( *stream, buffer->data, frameCount ); + + return 0; +} + +/* +> isProcessed = RL.IsAudioStreamProcessed( AudioStream stream ) + +Check if any audio stream buffers requires refill + +- Success return bool +*/ +int laudioIsAudioStreamProcessed( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + lua_pushboolean( L, IsAudioStreamProcessed( *stream ) ); + + return 1; +} + +/* +> RL.PlayAudioStream( AudioStream stream ) + +Play audio stream +*/ +int laudioPlayAudioStream( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + PlayAudioStream( *stream ); + + return 0; +} + +/* +> RL.PauseAudioStream( AudioStream stream ) + +Pause audio stream +*/ +int laudioPauseAudioStream( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + PauseAudioStream( *stream ); + + return 0; +} + +/* +> RL.ResumeAudioStream( AudioStream stream ) + +Resume audio stream +*/ +int laudioResumeAudioStream( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + ResumeAudioStream( *stream ); + + return 0; +} + +/* +> isPlaying = RL.IsAudioStreamPlaying( AudioStream stream ) + +Check if audio stream is playing + +- Success return bool +*/ +int laudioIsAudioStreamPlaying( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + lua_pushboolean( L, IsAudioStreamPlaying( *stream ) ); + + return 1; +} + +/* +> RL.StopAudioStream( AudioStream stream ) + +Stop audio stream +*/ +int laudioStopAudioStream( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + + StopAudioStream( *stream ); + + return 0; +} + +/* +> RL.SetAudioStreamVolume( AudioStream stream, float volume ) + +Set volume for audio stream (1.0 is max level) +*/ +int laudioSetAudioStreamVolume( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + float volume = luaL_checknumber( L, 2 ); + + SetAudioStreamVolume( *stream, volume ); + + return 0; +} + +/* +> RL.SetAudioStreamPitch( AudioStream stream, float pitch ) + +Set pitch for audio stream (1.0 is base level) +*/ +int laudioSetAudioStreamPitch( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + float pitch = luaL_checknumber( L, 2 ); + + SetAudioStreamPitch( *stream, pitch ); + + return 0; +} + +/* +> RL.SetAudioStreamPan( AudioStream stream, float pan ) + +Set pan for audio stream (0.5 is centered) +*/ +int laudioSetAudioStreamPan( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + float pan = luaL_checknumber( L, 2 ); + + SetAudioStreamPan( *stream, pan ); + + return 0; +} + +/* +> RL.SetAudioStreamBufferSizeDefault( int size ) + +Default size for new audio streams +*/ +int laudioSetAudioStreamBufferSizeDefault( lua_State* L ) { + int size = luaL_checkinteger( L, 1 ); + + SetAudioStreamBufferSizeDefault( size ); + + return 0; +} + +/* +> RL.SetAudioStreamCallback( AudioStream stream, AudioCallback callback ) + +Audio thread callback to request new data. +AudioCallback should be lightuserdata function pointer +*/ +int laudioSetAudioStreamCallback( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + AudioCallback callback = lua_touserdata( L, 2 ); + + SetAudioStreamCallback( *stream, callback ); + + return 0; +} + +/* +> RL.AttachAudioStreamProcessor( AudioStream stream, AudioCallback processor ) + +Attach audio stream processor to stream, receives the samples as 'float'. +AudioCallback should be lightuserdata function pointer +*/ +int laudioAttachAudioStreamProcessor( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + AudioCallback processor = lua_touserdata( L, 2 ); + + AttachAudioStreamProcessor( *stream, processor ); + + return 0; +} + +/* +> RL.DetachAudioStreamProcessor( AudioStream stream, AudioCallback processor ) + +Detach audio stream processor from stream. +AudioCallback should be lightuserdata function pointer +*/ +int laudioDetachAudioStreamProcessor( lua_State* L ) { + AudioStream* stream = uluaGetAudioStream( L, 1 ); + AudioCallback processor = lua_touserdata( L, 2 ); + + DetachAudioStreamProcessor( *stream, processor ); + + return 0; +} + +/* +> RL.AttachAudioMixedProcessor( AudioCallback processor ) + +Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'. +AudioCallback should be lightuserdata function pointer +*/ +int laudioAttachAudioMixedProcessor( lua_State* L ) { + AudioCallback processor = lua_touserdata( L, 1 ); + + AttachAudioMixedProcessor( processor ); + + return 0; +} + +/* +> RL.DetachAudioMixedProcessor( AudioCallback processor ) + +Detach audio stream processor from the entire audio pipeline. +AudioCallback should be lightuserdata function pointer +*/ +int laudioDetachAudioMixedProcessor( lua_State* L ) { + AudioCallback processor = lua_touserdata( L, 1 ); + + DetachAudioMixedProcessor( processor ); + + return 0; +} diff --git a/src/core.c b/src/core.c index 4cbd551..cd9c9c0 100644 --- a/src/core.c +++ b/src/core.c @@ -3821,7 +3821,11 @@ int lcoreSetBufferData( lua_State* L ) { int len = uluaGetTableLen( L, 3 ); - if ( position < 0 || buffer->size / getBufferElementSize( buffer ) <= ( position + len - 1 ) ) { + // printf( "buffer->size %d len %d position %d element size %d\n", buffer->size, len, position, getBufferElementSize( buffer ) ); + // printf( "Kissa %d %d\n", buffer->size / getBufferElementSize( buffer ), position + len - 1 ); + + // if ( position < 0 || buffer->size / getBufferElementSize( buffer ) <= ( position + len - 1 ) ) { + if ( position < 0 || ( ( buffer->size / getBufferElementSize( buffer ) ) <= ( position + len - 1 ) ) ) { TraceLog( state->logLevelInvalid, "SetBufferData. position %d out of bounds", position ); return 0; } diff --git a/src/lua_core.c b/src/lua_core.c index c4afe80..1a46a5b 100644 --- a/src/lua_core.c +++ b/src/lua_core.c @@ -254,6 +254,25 @@ static void defineMusic() { lua_setfield( L, -2, "__gc" ); } + /* AudioStream. */ +static int gcAudioStream( lua_State* L ) { + if ( state->gcUnload ) { + AudioStream* stream = luaL_checkudata( L, 1, "AudioStream" ); + uluaUnloadAudioStream( stream ); + } + return 0; +} + +static void defineAudioStream() { + lua_State* L = state->luaState; + + luaL_newmetatable( L, "AudioStream" ); + lua_pushvalue( L, -1 ); + lua_setfield( L, -2, "__index" ); + lua_pushcfunction( L, gcAudioStream ); + lua_setfield( L, -2, "__gc" ); +} + /* Light. */ static void defineLight() { lua_State* L = state->luaState; @@ -1341,6 +1360,7 @@ bool luaInit( int argn, const char** argc ) { defineSound(); defineSoundAlias(); defineMusic(); + defineAudioStream(); defineLight(); defineMaterial(); defineMesh(); @@ -2319,6 +2339,27 @@ void luaRegister() { assingGlobalFunction( "GetMusicLooping", laudioGetMusicLooping ); assingGlobalFunction( "GetMusicTimeLength", laudioGetMusicTimeLength ); assingGlobalFunction( "GetMusicTimePlayed", laudioGetMusicTimePlayed ); + assingGlobalFunction( "GetMusicStream", laudioGetMusicStream ); + /* AudioStream management functions. */ + assingGlobalFunction( "LoadAudioStream", laudioLoadAudioStream ); + assingGlobalFunction( "IsAudioStreamValid", laudioIsAudioStreamValid ); + assingGlobalFunction( "UnloadAudioStream", laudioUnloadAudioStream ); + assingGlobalFunction( "UpdateAudioStream", laudioUpdateAudioStream ); + assingGlobalFunction( "IsAudioStreamProcessed", laudioIsAudioStreamProcessed ); + assingGlobalFunction( "PlayAudioStream", laudioPlayAudioStream ); + assingGlobalFunction( "PauseAudioStream", laudioPauseAudioStream ); + assingGlobalFunction( "ResumeAudioStream", laudioResumeAudioStream ); + assingGlobalFunction( "IsAudioStreamPlaying", laudioIsAudioStreamPlaying ); + assingGlobalFunction( "StopAudioStream", laudioStopAudioStream ); + assingGlobalFunction( "SetAudioStreamVolume", laudioSetAudioStreamVolume ); + assingGlobalFunction( "SetAudioStreamPitch", laudioSetAudioStreamPitch ); + assingGlobalFunction( "SetAudioStreamPan", laudioSetAudioStreamPan ); + assingGlobalFunction( "SetAudioStreamBufferSizeDefault", laudioSetAudioStreamBufferSizeDefault ); + assingGlobalFunction( "SetAudioStreamCallback", laudioSetAudioStreamCallback ); + assingGlobalFunction( "AttachAudioStreamProcessor", laudioAttachAudioStreamProcessor ); + assingGlobalFunction( "DetachAudioStreamProcessor", laudioDetachAudioStreamProcessor ); + assingGlobalFunction( "AttachAudioMixedProcessor", laudioAttachAudioMixedProcessor ); + assingGlobalFunction( "DetachAudioMixedProcessor", laudioDetachAudioMixedProcessor ); /* Math. */ /* Utils. */ @@ -3735,6 +3776,36 @@ Music* uluaGetMusic( lua_State* L, int index ) { } } +AudioStream* uluaGetAudioStream( lua_State* L, int index ) { + switch ( lua_type( L, index ) ) { + case LUA_TLIGHTUSERDATA: + return (AudioStream*)lua_touserdata( L, index ); + case LUA_TTABLE: + int t = index, i = 0; + lua_pushnil( L ); + while ( lua_next( L, t ) != 0 ) { + if ( TextIsEqual( "audioStream", lua_tostring( L, -2 ) ) ) { + AudioStream* stream = NULL; + if ( lua_islightuserdata( L, lua_gettop( L ) ) ) { + stream = lua_touserdata( L, lua_gettop( L ) ); + } + else { + stream = luaL_checkudata( L, lua_gettop( L ), "AudioStream" ); + } + lua_pop( L, 2 ); /* Pops also the string. */ + return stream; + } + else { + lua_pop( L, 1 ); + } + i++; + } + /* Don't brake here, we want to get error from default if not found. */ + default: + return luaL_checkudata( L, index, "AudioStream" ); + } +} + Light* uluaGetLight( lua_State* L, int index ) { switch ( lua_type( L, index ) ) { case LUA_TLIGHTUSERDATA: @@ -4238,6 +4309,13 @@ void uluaPushMusic( lua_State* L, Music music ) { luaL_setmetatable( L, "Music" ); } +void uluaPushAudioStream( lua_State* L, AudioStream stream ) { + AudioStream* streamP = lua_newuserdata( L, sizeof( AudioStream ) ); + *streamP = stream; + luaCallLoad( "AudioStream", streamP ); + luaL_setmetatable( L, "AudioStream" ); +} + void uluaPushLight( lua_State* L, Light light ) { Light* lightP = lua_newuserdata( L, sizeof( Light ) ); *lightP = light; @@ -4361,6 +4439,12 @@ void uluaUnloadMusic( Music* music ) { memset( music, 0, sizeof( Music ) ); } +void uluaUnloadAudioStream( AudioStream* stream ) { + luaCallUnload( "AudioStream", stream ); + UnloadAudioStream( *stream ); + memset( stream, 0, sizeof( AudioStream ) ); +} + void uluaUnloadMaterial( Material* material, bool freeAll ) { luaCallUnload( "Material", material ); if ( freeAll ) { -- cgit v1.2.3