diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index 90883d94..cec5fc07 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -116,6 +116,17 @@ function on_hud_open() configure_SSAO() hud.default_hand_controller = update_hand + + local stream = PCMStream(44100, 1, 8) + stream:share("test-stream") + local bytes = Bytearray(44100 * 16) + for i=1,#bytes do + local x = math.sin(i * 0.08) * 127 + 128 + bytes[i] = x + end + stream:feed(bytes) + + audio.play_stream_2d("test-stream", 0.05, 1.0) end function on_hud_render() diff --git a/src/audio/audio.hpp b/src/audio/audio.hpp index 5893c334..a3553b38 100644 --- a/src/audio/audio.hpp +++ b/src/audio/audio.hpp @@ -121,6 +121,10 @@ namespace audio { /// (always equals bufferSize if seekable and looped) virtual size_t readFully(char* buffer, size_t bufferSize, bool loop); + /// @brief Read available data to buffer + /// @param buffer destination buffer + /// @param bufferSize destination buffer size + /// @return count of received bytes or PCMStream::ERROR virtual size_t read(char* buffer, size_t bufferSize) = 0; /// @brief Close stream diff --git a/src/logic/scripting/lua/libs/libaudio.cpp b/src/logic/scripting/lua/libs/libaudio.cpp index db868c57..4d4ad6a6 100644 --- a/src/logic/scripting/lua/libs/libaudio.cpp +++ b/src/logic/scripting/lua/libs/libaudio.cpp @@ -67,6 +67,26 @@ inline audio::speakerid_t play_stream( if (channel == -1) { return 0; } + if (!scripting::engine->isHeadless()) { + auto assets = scripting::engine->getAssets(); + + auto stream = assets->getShared(filename); + if (stream) { + return audio::play( + audio::open_stream(std::move(stream), true), + glm::vec3( + static_cast(x), + static_cast(y), + static_cast(z) + ), + relative, + volume, + pitch, + loop, + channel + ); + } + } io::path file; if (std::strchr(filename, ':')) { file = std::string(filename); diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 05adb354..c4507bb1 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -12,6 +12,7 @@ #include "usertypes/lua_type_voxelfragment.hpp" #include "usertypes/lua_type_canvas.hpp" #include "usertypes/lua_type_random.hpp" +#include "usertypes/lua_type_pcmstream.hpp" #include "engine/Engine.hpp" static debug::Logger logger("lua-state"); @@ -130,6 +131,7 @@ void lua::init_state(State* L, StateType stateType) { newusertype(L); newusertype(L); newusertype(L); + newusertype(L); } void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { diff --git a/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp b/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp new file mode 100644 index 00000000..04c585cf --- /dev/null +++ b/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp @@ -0,0 +1,94 @@ +#include "../lua_util.hpp" +#include "lua_type_pcmstream.hpp" +#include "assets/Assets.hpp" +#include "audio/MemoryPCMStream.hpp" +#include "engine/Engine.hpp" + +using namespace lua; +using namespace audio; +using namespace scripting; + +LuaPCMStream::LuaPCMStream(std::shared_ptr&& stream) + : stream(std::move(stream)) { +} + +LuaPCMStream::~LuaPCMStream() = default; + +const std::shared_ptr& LuaPCMStream::getStream() const { + return stream; +} + +#include + +static int l_feed(lua::State* L) { + std::cout << "feed" << std::endl; + auto stream = touserdata(L, 1); + if (stream == nullptr) { + return 0; + } + auto bytes = bytearray_as_string(L, 2); + stream->getStream()->feed( + {reinterpret_cast(bytes.data()), bytes.size()} + ); + return 0; +} + +static int l_share(lua::State* L) { + auto stream = touserdata(L, 1); + if (stream == nullptr) { + return 0; + } + auto alias = require_lstring(L, 2); + if (engine->isHeadless()) { + return 0; + } + auto assets = engine->getAssets(); + assets->store(stream->getStream(), std::string(alias)); + return 0; +} + +static std::unordered_map methods { + {"feed", lua::wrap}, + {"share", lua::wrap}, +}; + +static int l_meta_meta_call(lua::State* L) { + auto sampleRate = touinteger(L, 2); + auto channels = touinteger(L, 3); + auto bitsPerSample = touinteger(L, 4); + auto stream = + std::make_shared(sampleRate, channels, bitsPerSample); + return newuserdata(L, std::move(stream)); +} + +static int l_meta_tostring(lua::State* L) { + return pushstring(L, "PCMStream"); +} + +static int l_meta_index(lua::State* L) { + auto stream = touserdata(L, 1); + if (stream == nullptr) { + return 0; + } + if (isstring(L, 2)) { + auto found = methods.find(tostring(L, 2)); + if (found != methods.end()) { + return pushcfunction(L, found->second); + } + } + return 0; +} + +int LuaPCMStream::createMetatable(lua::State* L) { + createtable(L, 0, 3); + pushcfunction(L, lua::wrap); + setfield(L, "__tostring"); + pushcfunction(L, lua::wrap); + setfield(L, "__index"); + + createtable(L, 0, 1); + pushcfunction(L, lua::wrap); + setfield(L, "__call"); + setmetatable(L); + return 1; +} diff --git a/src/logic/scripting/lua/usertypes/lua_type_pcmstream.hpp b/src/logic/scripting/lua/usertypes/lua_type_pcmstream.hpp new file mode 100644 index 00000000..47979693 --- /dev/null +++ b/src/logic/scripting/lua/usertypes/lua_type_pcmstream.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "../lua_commons.hpp" + +namespace audio { + class MemoryPCMStream; +} + +namespace lua { + class LuaPCMStream : public Userdata { + public: + explicit LuaPCMStream(std::shared_ptr&& stream); + virtual ~LuaPCMStream() override; + + const std::shared_ptr& getStream() const; + + const std::string& getTypeName() const override { + return TYPENAME; + } + static int createMetatable(lua::State*); + inline static std::string TYPENAME = "PCMStream"; + private: + std::shared_ptr stream; + }; + static_assert(!std::is_abstract()); +}