From d682b164aa87435183a5ad3196b25b5ff8a5ad18 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Wed, 19 Jul 2023 21:12:14 +1000 Subject: [PATCH] more Lua voice callbacks --- source/client/StarClientApplication.cpp | 2 ++ source/frontend/StarVoice.cpp | 39 +++++++++++++++++++++--- source/frontend/StarVoice.hpp | 6 +++- source/frontend/StarVoiceLuaBindings.cpp | 28 +++++++++-------- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index ba2bbd1..d8d79fd 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -464,6 +464,8 @@ void ClientApplication::changeState(MainAppState newState) { } m_cinematicOverlay->stop(); + m_voice->clearSpeakers(); + if (auto p2pNetworkingService = appController()->p2pNetworkingService()) { p2pNetworkingService->setJoinUnavailable(); p2pNetworkingService->setAcceptingP2PConnections(false); diff --git a/source/frontend/StarVoice.cpp b/source/frontend/StarVoice.cpp index a4641ab..b7f8a6c 100644 --- a/source/frontend/StarVoice.cpp +++ b/source/frontend/StarVoice.cpp @@ -1,5 +1,6 @@ #include "StarVoice.hpp" #include "StarFormat.hpp" +#include "StarJsonExtra.hpp" #include "StarApplicationController.hpp" #include "StarTime.hpp" #include "StarRoot.hpp" @@ -103,6 +104,19 @@ Voice::Speaker::Speaker(SpeakerId id) audioStream = make_shared(); } +Json Voice::Speaker::toJson() const { + return JsonObject{ + {"speakerId", speakerId}, + {"entityId", entityId }, + {"name", name }, + {"playing", (bool)playing}, + {"muted", (bool)muted }, + {"decibels", (float)decibelLevel}, + {"smoothDecibels", (float)smoothDb }, + {"position", jsonFromVec2F(position)} + }; +} + Voice* Voice::s_singleton; Voice* Voice::singletonPtr() { @@ -258,7 +272,11 @@ Voice::SpeakerPtr Voice::speaker(SpeakerId speakerId) { } } -List Voice::speakers(bool onlyPlaying) { +HashMap& Voice::speakers() { + return m_speakers; +} + +List Voice::sortedSpeakers(bool onlyPlaying) { List result; auto sorter = [](SpeakerPtr const& a, SpeakerPtr const& b) -> bool { @@ -276,6 +294,16 @@ List Voice::speakers(bool onlyPlaying) { return result; } +void Voice::clearSpeakers() { + auto it = m_speakers.begin(); + while (it != m_speakers.end()) { + if (it->second == m_clientSpeaker) + it = ++it; + else + it = m_speakers.erase(it); + } +} + void Voice::readAudioData(uint8_t* stream, int len) { auto now = Time::monotonicMilliseconds(); bool active = m_encoder && m_encodedChunksLength < 2048 @@ -335,7 +363,7 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) { SpeakerPtr const& speaker = *it; VoiceAudioStream* audio = speaker->audioStream.get(); MutexLocker audioLock(audio->mutex); - if (!audio->samples.empty()) { + if (speaker->playing && !audio->samples.empty()) { if (!speaker->muted) { mix = true; for (size_t i = 0; i != samples; ++i) @@ -343,9 +371,10 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) { speaker->decibelLevel = getAudioLoudness(speakerBuffer.data(), samples); - auto levels = speaker->channelVolumes.load(); + float volume = speaker->volume; + Array2F levels = speaker->channelVolumes; for (size_t i = 0; i != samples; ++i) - sharedBuffer[i] += (int32_t)(speakerBuffer[i]) * levels[i % 2]; + sharedBuffer[i] += (int32_t)(speakerBuffer[i]) * levels[i % 2] * volume; //Blends the weaker channel into the stronger one, /* unused, is a bit too strong on stereo music. float maxLevel = max(levels[0], levels[1]); @@ -606,6 +635,8 @@ void Voice::closeDevice() { return; m_applicationController->closeAudioInputDevice(); + if (!m_loopback) + m_clientSpeaker->playing = false; m_deviceOpen = false; } diff --git a/source/frontend/StarVoice.hpp b/source/frontend/StarVoice.hpp index 99228e8..4500aa6 100644 --- a/source/frontend/StarVoice.hpp +++ b/source/frontend/StarVoice.hpp @@ -88,11 +88,13 @@ public: atomic muted = false; atomic playing = 0; atomic decibelLevel = -96.0f; + atomic volume = 1.0f; atomic> channelVolumes = Array::filled(1); unsigned int minimumPlaySamples = 4096; Speaker(SpeakerId speakerId); + Json toJson() const; }; typedef std::shared_ptr SpeakerPtr; @@ -123,7 +125,9 @@ public: SpeakerPtr setLocalSpeaker(SpeakerId speakerId); SpeakerPtr localSpeaker(); SpeakerPtr speaker(SpeakerId speakerId); - List speakers(bool onlyPlaying); + HashMap& speakers(); + List sortedSpeakers(bool onlyPlaying); + void clearSpeakers(); // Called when receiving input audio data from SDL, on its own thread. void readAudioData(uint8_t* stream, int len); diff --git a/source/frontend/StarVoiceLuaBindings.cpp b/source/frontend/StarVoiceLuaBindings.cpp index a91937e..1b62aef 100644 --- a/source/frontend/StarVoiceLuaBindings.cpp +++ b/source/frontend/StarVoiceLuaBindings.cpp @@ -1,28 +1,32 @@ +#include "StarLuaConverters.hpp" #include "StarVoiceLuaBindings.hpp" #include "StarVoice.hpp" + namespace Star { +typedef Voice::SpeakerId SpeakerId; LuaCallbacks LuaBindings::makeVoiceCallbacks(Voice* voice) { LuaCallbacks callbacks; - callbacks.registerCallback("getSettings", [voice]() -> Json { return voice->saveJson(); }); + callbacks.registerCallbackWithSignature("devices", bind(&Voice::availableDevices, voice)); + callbacks.registerCallback( "getSettings", [voice]() -> Json { return voice->saveJson(); }); callbacks.registerCallback("mergeSettings", [voice](Json const& settings) { voice->loadJson(settings); }); + // i have an alignment addiction i'm so sorry + callbacks.registerCallback("setSpeakerMuted", [voice](SpeakerId speakerId, bool muted) { voice->speaker(speakerId)->muted = muted; }); + callbacks.registerCallback( "speakerMuted", [voice](SpeakerId speakerId) { return (bool)voice->speaker(speakerId)->muted; }); + // it just looks so neat to me!! + callbacks.registerCallback("setSpeakerVolume", [voice](SpeakerId speakerId, float volume) { voice->speaker(speakerId)->volume = volume; }); + callbacks.registerCallback( "speakerVolume", [voice](SpeakerId speakerId) { return (float)voice->speaker(speakerId)->volume; }); + callbacks.registerCallback("speakerPosition", [voice](SpeakerId speakerId) { return voice->speaker(speakerId)->position; }); + + callbacks.registerCallback("speaker", [voice](SpeakerId speakerId) { return voice->speaker(speakerId)->toJson(); }); callbacks.registerCallback("speakers", [voice](Maybe onlyPlaying) -> List { List list; - for (auto& speaker : voice->speakers(onlyPlaying.value(true))) { - list.append(JsonObject{ - {"speakerId", speaker->speakerId }, - {"entityId", speaker->entityId }, - {"name", speaker->name }, - {"playing", (bool)speaker->playing }, - {"muted", (bool)speaker->muted }, - {"decibels", (float)speaker->decibelLevel }, - {"smoothDecibels", (float)speaker->smoothDb }, - }); - } + for (auto& speaker : voice->sortedSpeakers(onlyPlaying.value(true))) + list.append(speaker->toJson()); return list; });