Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'source/frontend')
-rw-r--r--source/frontend/CMakeLists.txt2
-rw-r--r--source/frontend/StarVoice.cpp75
-rw-r--r--source/frontend/StarVoice.hpp5
-rw-r--r--source/frontend/StarVoiceLuaBindings.cpp29
-rw-r--r--source/frontend/StarVoiceLuaBindings.hpp16
5 files changed, 101 insertions, 26 deletions
diff --git a/source/frontend/CMakeLists.txt b/source/frontend/CMakeLists.txt
index d8e5390..4c6b9c8 100644
--- a/source/frontend/CMakeLists.txt
+++ b/source/frontend/CMakeLists.txt
@@ -57,6 +57,7 @@ SET (star_frontend_HEADERS
StarTeleportDialog.hpp
StarWireInterface.hpp
StarVoice.hpp
+ StarVoiceLuaBindings.hpp
)
SET (star_frontend_SOURCES
@@ -106,6 +107,7 @@ SET (star_frontend_SOURCES
StarTeleportDialog.cpp
StarWireInterface.cpp
StarVoice.cpp
+ StarVoiceLuaBindings.cpp
)
ADD_LIBRARY (star_frontend OBJECT ${star_frontend_SOURCES} ${star_frontend_HEADERS})
diff --git a/source/frontend/StarVoice.cpp b/source/frontend/StarVoice.cpp
index c6ec1d0..9d84033 100644
--- a/source/frontend/StarVoice.cpp
+++ b/source/frontend/StarVoice.cpp
@@ -28,13 +28,13 @@ EnumMap<VoiceChannelMode> const VoiceChannelModeNames{
{VoiceChannelMode::Stereo, "Stereo"}
};
-float getAudioChunkLoudness(int16_t* data, size_t samples) {
+inline float getAudioChunkLoudness(int16_t* data, size_t samples, float volume) {
if (!samples)
return 0.f;
double rms = 0.;
for (size_t i = 0; i != samples; ++i) {
- float sample = (float)data[i] / 32767.f;
+ float sample = ((float)data[i] / 32767.f) * volume;
rms += (double)(sample * sample);
}
@@ -46,12 +46,12 @@ float getAudioChunkLoudness(int16_t* data, size_t samples) {
return -127.f;
}
-float getAudioLoudness(int16_t* data, size_t samples) {
+float getAudioLoudness(int16_t* data, size_t samples, float volume = 1.0f) {
constexpr size_t CHUNK_SIZE = 50;
float highest = -127.f;
for (size_t i = 0; i < samples; i += CHUNK_SIZE) {
- float level = getAudioChunkLoudness(data + i, std::min<size_t>(i + CHUNK_SIZE, samples) - i);
+ float level = getAudioChunkLoudness(data + i, std::min<size_t>(i + CHUNK_SIZE, samples) - i, volume);
if (level > highest)
highest = level;
}
@@ -192,6 +192,10 @@ Voice::SpeakerPtr Voice::setLocalSpeaker(SpeakerId speakerId) {
return m_speakers.insert(m_speakerId, m_clientSpeaker).first->second;
}
+Voice::SpeakerPtr Voice::localSpeaker() {
+ return m_clientSpeaker;
+}
+
Voice::SpeakerPtr Voice::speaker(SpeakerId speakerId) {
if (m_speakerId == speakerId)
return m_clientSpeaker;
@@ -203,28 +207,47 @@ Voice::SpeakerPtr Voice::speaker(SpeakerId speakerId) {
}
}
+List<Voice::SpeakerPtr> Voice::speakers(bool onlyPlaying) {
+ List<SpeakerPtr> result;
+
+ auto sorter = [](SpeakerPtr const& a, SpeakerPtr const& b) -> bool {
+ if (a->lastPlayTime != b->lastPlayTime)
+ return a->lastPlayTime < b->lastPlayTime;
+ else
+ return a->speakerId < b->speakerId;
+ };
+
+ for (auto& p : m_speakers) {
+ if (!onlyPlaying || p.second->playing)
+ result.insertSorted(p.second, sorter);
+ }
+
+ return result;
+}
+
void Voice::readAudioData(uint8_t* stream, int len) {
auto now = Time::monotonicMilliseconds();
- if (!m_encoder || m_inputMode == VoiceInputMode::PushToTalk && now > m_lastInputTime)
- return;
-
- // Stop encoding if 2048 bytes have been encoded and not taken by the game thread yet
- if (m_encodedChunksLength > 2048)
- return;
+ bool active = m_encoder && m_encodedChunksLength < 2048
+ && (m_inputMode == VoiceInputMode::VoiceActivity || now < m_lastInputTime);
size_t sampleCount = len / 2;
- float decibels = getAudioLoudness((int16_t*)stream, sampleCount);
- m_clientSpeaker->decibelLevel = decibels;
- bool active = true;
+ if (active) {
+ float volume = m_inputVolume;
+ float decibels = getAudioLoudness((int16_t*)stream, sampleCount);
- if (m_inputMode == VoiceInputMode::VoiceActivity) {
- if (decibels > m_threshold)
- m_lastThresholdTime = now;
- active = now - m_lastThresholdTime < 50;
+ if (m_inputMode == VoiceInputMode::VoiceActivity) {
+ if (decibels > m_threshold)
+ m_lastThresholdTime = now;
+ active = now - m_lastThresholdTime < 50;
+ }
}
- bool added = false;
+ if (active && !m_clientSpeaker->playing)
+ m_clientSpeaker->lastPlayTime = now;
+
+ if (!(m_clientSpeaker->playing = active))
+ return;
MutexLocker captureLock(m_captureMutex);
if (active) {
@@ -232,7 +255,7 @@ void Voice::readAudioData(uint8_t* stream, int len) {
auto data = (opus_int16*)malloc(len);
memcpy(data, stream, len);
m_capturedChunks.emplace(data, sampleCount); // takes ownership
- added = true;
+ m_threadCond.signal();
}
else { // Clear out any residual data so they don't manifest at the start of the next encode, whenever that is
while (!m_capturedChunks.empty())
@@ -240,9 +263,6 @@ void Voice::readAudioData(uint8_t* stream, int len) {
m_capturedChunksFrames = 0;
}
-
- if (added)
- m_threadCond.signal();
}
void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) {
@@ -493,9 +513,12 @@ bool Voice::playSpeaker(SpeakerPtr const& speaker, int channels) {
if (speaker->playing || speaker->audioStream->samples.size() < minSamples)
return false;
- speaker->playing = true;
- MutexLocker lock(m_activeSpeakersMutex);
- m_activeSpeakers.insert(speaker);
+ if (!speaker->playing) {
+ speaker->lastPlayTime = Time::monotonicMilliseconds();
+ speaker->playing = true;
+ MutexLocker lock(m_activeSpeakersMutex);
+ m_activeSpeakers.insert(speaker);
+ }
return true;
}
@@ -529,6 +552,8 @@ void Voice::thread() {
samples[i] *= m_inputVolume;
}
+ m_clientSpeaker->decibelLevel = getAudioLoudness(samples.data(), samples.size());
+
if (int encodedSize = opus_encode(m_encoder.get(), samples.data(), VOICE_FRAME_SIZE, (unsigned char*)encoded.ptr(), encoded.size())) {
if (encodedSize == 1)
continue;
diff --git a/source/frontend/StarVoice.hpp b/source/frontend/StarVoice.hpp
index 38964b0..d6bc467 100644
--- a/source/frontend/StarVoice.hpp
+++ b/source/frontend/StarVoice.hpp
@@ -81,9 +81,10 @@ public:
Mutex mutex;
int64_t lastReceiveTime = 0;
+ int64_t lastPlayTime = 0;
atomic<bool> muted = false;
- atomic<bool> playing = false;
+ atomic<bool> playing = 0;
atomic<float> decibelLevel = 0.0f;
atomic<Array<float, 2>> channelVolumes = Array<float, 2>::filled(1.0f);
@@ -118,7 +119,9 @@ public:
// Sets the local speaker ID and returns the local speaker. Must be called upon loading into a world.
SpeakerPtr setLocalSpeaker(SpeakerId speakerId);
+ SpeakerPtr localSpeaker();
SpeakerPtr speaker(SpeakerId speakerId);
+ List<Voice::SpeakerPtr> speakers(bool onlyPlaying);
// 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
new file mode 100644
index 0000000..e3271fd
--- /dev/null
+++ b/source/frontend/StarVoiceLuaBindings.cpp
@@ -0,0 +1,29 @@
+#include "StarVoiceLuaBindings.hpp"
+#include "StarVoice.hpp"
+
+namespace Star {
+
+LuaCallbacks LuaBindings::makeVoiceCallbacks(Voice* voice) {
+ LuaCallbacks callbacks;
+
+ callbacks.registerCallback("speakers", [voice](Maybe<bool> onlyPlaying) -> List<Json> {
+ List<Json> 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 },
+ {"loudness", (float)speaker->decibelLevel },
+ });
+ }
+
+ return list;
+ });
+
+ return callbacks;
+}
+
+}
diff --git a/source/frontend/StarVoiceLuaBindings.hpp b/source/frontend/StarVoiceLuaBindings.hpp
new file mode 100644
index 0000000..8c83e54
--- /dev/null
+++ b/source/frontend/StarVoiceLuaBindings.hpp
@@ -0,0 +1,16 @@
+#ifndef STAR_VOICE_LUA_BINDINGS_HPP
+#define STAR_VOICE_LUA_BINDINGS_HPP
+
+#include "StarLua.hpp"
+
+namespace Star {
+
+STAR_CLASS(Voice);
+
+namespace LuaBindings {
+ LuaCallbacks makeVoiceCallbacks(Voice* voice);
+}
+
+}
+
+#endif