diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index cec5fc07..ba828491 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -55,6 +55,8 @@ local function update_hand() skeleton.set_model("hand", bone, item.model_name(itemid)) end +local stream + function on_hud_open() input.add_callback("player.pick", function () if hud.is_paused() or hud.is_inventory_open() then @@ -117,16 +119,16 @@ function on_hud_open() hud.default_hand_controller = update_hand - local stream = PCMStream(44100, 1, 8) + stream = PCMStream(44100, 1, 16) stream:share("test-stream") - local bytes = Bytearray(44100 * 16) + local bytes = Bytearray(44100 / 8) for i=1,#bytes do - local x = math.sin(i * 0.08) * 127 + 128 + local x = math.sin(i * 0.08) * 1 + 0 bytes[i] = x end stream:feed(bytes) - audio.play_stream_2d("test-stream", 0.05, 1.0) + audio.play_stream_2d("test-stream", 2.0, 1.0, "ui") end function on_hud_render() @@ -135,4 +137,7 @@ function on_hud_render() else update_hand() end + + local bytes = audio.fetch_input() + stream:feed(bytes) end diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index 92dd209a..23dfeaa8 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -5,11 +5,40 @@ #include "debug/Logger.hpp" #include "alutil.hpp" +#include "../MemoryPCMStream.hpp" static debug::Logger logger("al-audio"); using namespace audio; +const char* alc_error_to_string(ALCenum error) { + switch (error) { + case ALC_NO_ERROR: + return "no error"; + case ALC_INVALID_DEVICE: + return "invalid device handle"; + case ALC_INVALID_CONTEXT: + return "invalid context handle"; + case ALC_INVALID_ENUM: + return "invalid enum parameter passed to an ALC call"; + case ALC_INVALID_VALUE: + return "invalid value parameter passed to an ALC call"; + case ALC_OUT_OF_MEMORY: + return "out of memory"; + default: + return "unknown ALC error"; + } +} + +static void check_alc_errors(ALCdevice* device, const char* context) { + ALCenum error = alcGetError(device); + if (error == ALC_NO_ERROR) { + return; + } + logger.error() << context << ": " << alc_error_to_string(error) << "(" + << error << ")"; +} + ALSound::ALSound( ALAudio* al, uint buffer, const std::shared_ptr& pcm, bool keepPCM ) @@ -45,14 +74,17 @@ ALInputDevice::ALInputDevice( ALInputDevice::~ALInputDevice() { alcCaptureCloseDevice(device); + check_alc_errors(device, "alcCaptureCloseDevice"); } void ALInputDevice::startCapture() { - AL_CHECK(alcCaptureStart(device)); + alcCaptureStart(device); + check_alc_errors(device, "alcCaptureStart"); } void ALInputDevice::stopCapture() { - AL_CHECK(alcCaptureStop(device)); + alcCaptureStop(device); + check_alc_errors(device, "alcCaptureStop"); } uint ALInputDevice::getChannels() const { @@ -60,13 +92,15 @@ uint ALInputDevice::getChannels() const { } size_t ALInputDevice::read(char* buffer, size_t bufferSize) { - ALCint samplesCount; - AL_CHECK(alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, 1, &samplesCount)); + ALCint samplesCount = 0; + alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, sizeof(samplesCount), &samplesCount); + check_alc_errors(device, "alcGetIntegerv(ALC_CAPTURE_SAMPLES)"); size_t samplesRead = std::min( samplesCount, bufferSize / channels / (bitsPerSample >> 3) ); - AL_CHECK(alcCaptureSamples(device, buffer, samplesRead)); - return samplesRead; + alcCaptureSamples(device, buffer, samplesRead); + check_alc_errors(device, "alcCaptureSamples"); + return samplesRead * channels * (bitsPerSample >> 3); } ALStream::ALStream( @@ -195,7 +229,7 @@ void ALStream::update(double delta) { // alspeaker->stopped is assigned to false at ALSpeaker::play(...) if (p_speaker->isStopped() && !alspeaker->stopped) { //TODO: -V560 false-positive? - if (preloaded) { + if (preloaded || dynamic_cast(source.get())) { p_speaker->play(); } else { p_speaker->stop(); @@ -417,8 +451,10 @@ ALAudio::~ALAudio() { AL_CHECK(alDeleteBuffers(1, &buffer)); } - AL_CHECK(alcMakeContextCurrent(context)); + alcMakeContextCurrent(nullptr); + check_alc_errors(device, "alcMakeContextCurrent"); alcDestroyContext(context); + check_alc_errors(device, "alcDestroyContext"); if (!alcCloseDevice(device)) { logger.error() << "device not closed!"; } @@ -447,15 +483,16 @@ std::unique_ptr ALAudio::openInputDevice( uint sampleRate, uint channels, uint bitsPerSample ) { uint bps = bitsPerSample >> 3; - AL_CHECK( - ALCdevice* device = alcCaptureOpenDevice( - nullptr, - sampleRate, - AL::to_al_format(channels, bps), - sampleRate * channels * bps - ) + ALCdevice* device = alcCaptureOpenDevice( + nullptr, + sampleRate, + AL::to_al_format(channels, bitsPerSample), + sampleRate * channels * bps / 8 + ); + check_alc_errors(device, "alcCaptureOpenDevice"); + return std::make_unique( + this, device, channels, bitsPerSample ); - return std::make_unique(this, device, channels, bps); } std::unique_ptr ALAudio::create() { diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index f98c436e..130e6fac 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -151,6 +151,8 @@ public: } }; +static std::unique_ptr input_device = nullptr; + void audio::initialize(bool enabled, AudioSettings& settings) { enabled = enabled && settings.enabled.get(); if (enabled) { @@ -180,6 +182,13 @@ void audio::initialize(bool enabled, AudioSettings& settings) { audio::get_channel(channel.name)->setVolume(value * value); }, true)); } + + input_device = backend->openInputDevice(44100, 1, 16); + input_device->startCapture(); +} + +InputDevice* audio::get_input_device() { + return input_device.get(); } std::unique_ptr audio::load_PCM(const io::path& file, bool headerOnly) { @@ -458,6 +467,7 @@ void audio::reset_channel(int index) { } void audio::close() { + input_device->stopCapture(); speakers.clear(); delete backend; backend = nullptr; diff --git a/src/audio/audio.hpp b/src/audio/audio.hpp index 56de29ad..e1c19d25 100644 --- a/src/audio/audio.hpp +++ b/src/audio/audio.hpp @@ -24,6 +24,8 @@ namespace audio { /// @brief streams and important sounds constexpr inline int PRIORITY_HIGH = 10; + constexpr inline size_t MAX_INPUT_SAMPLES = 22050; + class Speaker; /// @brief Audio speaker states @@ -423,6 +425,15 @@ namespace audio { std::shared_ptr stream, bool keepSource ); + /// @brief Open audio input device + /// @param sampleRate sample rate + /// @param channels channels count (1 - mono, 2 - stereo) + /// @param bitsPerSample number of bits per sample (8 or 16) + /// @return new InputDevice instance or nullptr + std::unique_ptr open_input_device( + uint sampleRate, uint channels, uint bitsPerSample + ); + /// @brief Configure 3D listener /// @param position listener position /// @param velocity listener velocity (used for Doppler effect) @@ -536,6 +547,8 @@ namespace audio { /// @brief Stop all playing audio in channel, reset channel state void reset_channel(int channel); + InputDevice* get_input_device(); + /// @brief Finalize audio system void close(); }; diff --git a/src/logic/scripting/lua/libs/libaudio.cpp b/src/logic/scripting/lua/libs/libaudio.cpp index 4d4ad6a6..7f68bd6d 100644 --- a/src/logic/scripting/lua/libs/libaudio.cpp +++ b/src/logic/scripting/lua/libs/libaudio.cpp @@ -379,16 +379,35 @@ static int l_audio_get_velocity(lua::State* L) { return 0; } -// @brief audio.count_speakers() -> integer +/// @brief audio.count_speakers() -> integer static int l_audio_count_speakers(lua::State* L) { return lua::pushinteger(L, audio::count_speakers()); } -// @brief audio.count_streams() -> integer +/// @brief audio.count_streams() -> integer static int l_audio_count_streams(lua::State* L) { return lua::pushinteger(L, audio::count_streams()); } +/// @brief audio.fetch_input(size) -> Bytearray +static int l_audio_fetch_input(lua::State* L) { + auto device = audio::get_input_device(); + if (device == nullptr) { + return 0; + } + size_t size = lua::touinteger(L, 1); + const size_t MAX_BUFFER_SIZE = audio::MAX_INPUT_SAMPLES * 4; + if (size == 0) { + size = MAX_BUFFER_SIZE; + } + size = std::min(size, MAX_BUFFER_SIZE); + ubyte buffer[MAX_BUFFER_SIZE]; + size = device->read(reinterpret_cast(buffer), size); + + std::vector bytes(buffer, buffer + size); + return lua::create_bytearray(L, std::move(bytes)); +} + const luaL_Reg audiolib[] = { {"play_sound", lua::wrap}, {"play_sound_2d", lua::wrap}, @@ -414,5 +433,6 @@ const luaL_Reg audiolib[] = { {"get_velocity", lua::wrap}, {"count_speakers", lua::wrap}, {"count_streams", lua::wrap}, + {"fetch_input", lua::wrap}, {nullptr, nullptr} }; diff --git a/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp b/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp index 04c585cf..3367813e 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_pcmstream.cpp @@ -18,10 +18,7 @@ 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;