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

summaryrefslogtreecommitdiff
path: root/source/frontend/StarVoice.cpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-07-13 19:12:55 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-07-13 19:12:55 +1000
commit28f4204b09b04280340ffbbeaccf86b96f377296 (patch)
treee2a166cc3ddd57460814631b7a8258ed0ee975ea /source/frontend/StarVoice.cpp
parentc3bf7a3c87e61c56d48dd932f295c99d64d14a38 (diff)
more Voice work
Diffstat (limited to 'source/frontend/StarVoice.cpp')
-rw-r--r--source/frontend/StarVoice.cpp146
1 files changed, 146 insertions, 0 deletions
diff --git a/source/frontend/StarVoice.cpp b/source/frontend/StarVoice.cpp
new file mode 100644
index 0000000..436461b
--- /dev/null
+++ b/source/frontend/StarVoice.cpp
@@ -0,0 +1,146 @@
+#include "StarVoice.hpp"
+#include "StarFormat.hpp"
+#include "StarApplicationController.hpp"
+#include "opus/include/opus.h"
+
+#include "SDL.h"
+
+constexpr int VOICE_SAMPLE_RATE = 48000;
+constexpr int VOICE_FRAME_SIZE = 960;
+
+constexpr int VOICE_MAX_FRAME_SIZE = 6 * VOICE_FRAME_SIZE;
+constexpr int VOICE_MAX_PACKET_SIZE = 3 * 1276;
+
+constexpr uint16_t VOICE_VERSION = 1;
+
+namespace Star {
+
+EnumMap<VoiceInputMode> const VoiceInputModeNames{
+ {VoiceInputMode::VoiceActivity, "VoiceActivity"},
+ {VoiceInputMode::PushToTalk, "PushToTalk"}
+};
+
+EnumMap<VoiceChannelMode> const VoiceChannelModeNames{
+ {VoiceChannelMode::Mono, "Mono"},
+ {VoiceChannelMode::Stereo, "Stereo"}
+};
+
+Voice::Speaker::Speaker(SpeakerId id)
+ : decoderMono (createDecoder(1), opus_decoder_destroy)
+ , decoderStereo(createDecoder(2), opus_decoder_destroy) {
+ speakerId = id;
+}
+
+Voice* Voice::s_singleton;
+
+Voice* Voice::singletonPtr() {
+ return s_singleton;
+}
+
+Voice& Voice::singleton() {
+ if (!s_singleton)
+ throw VoiceException("Voice::singleton() called with no Voice instance available");
+ else
+ return *s_singleton;
+}
+
+Voice::Voice(ApplicationControllerPtr appController) : m_encoder(nullptr, opus_encoder_destroy) {
+ if (s_singleton)
+ throw VoiceException("Singleton Voice has been constructed twice");
+
+ m_clientSpeaker = make_shared<Speaker>(m_speakerId);
+ m_inputMode = VoiceInputMode::PushToTalk;
+ m_channelMode = VoiceChannelMode::Mono;
+ m_applicationController = appController;
+
+ resetEncoder();
+ s_singleton = this;
+}
+
+Voice::~Voice() {
+ s_singleton = nullptr;
+}
+
+void Voice::load(Json const& config) {
+ // do stuff
+}
+Json Voice::save() const {
+ return JsonObject{};
+}
+
+Voice::SpeakerPtr Voice::setLocalSpeaker(SpeakerId speakerId) {
+ if (m_speakers.contains(m_speakerId))
+ m_speakers.remove(m_speakerId);
+
+ m_clientSpeaker->speakerId = m_speakerId = speakerId;
+ return m_speakers.insert(m_speakerId, m_clientSpeaker).first->second;
+}
+
+Voice::SpeakerPtr Voice::speaker(SpeakerId speakerId) {
+ if (m_speakerId == speakerId)
+ return m_clientSpeaker;
+ else {
+ if (SpeakerPtr const* ptr = m_speakers.ptr(speakerId))
+ return *ptr;
+ else
+ return m_speakers.emplace(speakerId, make_shared<Speaker>(speakerId)).first->second;
+ }
+}
+
+void Voice::mix(int16_t* buffer, size_t frames, unsigned channels) {
+
+}
+
+void Voice::update(PositionalAttenuationFunction positionalAttenuationFunction) {
+ if (positionalAttenuationFunction) {
+ for (auto& entry : m_speakers) {
+ if (SpeakerPtr& speaker = entry.second) {
+ speaker->channelVolumes = {
+ positionalAttenuationFunction(0, speaker->position, 1.0f),
+ positionalAttenuationFunction(1, speaker->position, 1.0f)
+ };
+ }
+ }
+ }
+}
+
+OpusDecoder* Voice::createDecoder(int channels) {
+ int error;
+ OpusDecoder* decoder = opus_decoder_create(VOICE_SAMPLE_RATE, channels, &error);
+ if (error != OPUS_OK)
+ throw VoiceException::format("Could not create decoder: {}", opus_strerror(error));
+ else
+ return decoder;
+}
+
+OpusEncoder* Voice::createEncoder(int channels) {
+ int error;
+ OpusEncoder* encoder = opus_encoder_create(VOICE_SAMPLE_RATE, channels, OPUS_APPLICATION_AUDIO, &error);
+ if (error != OPUS_OK)
+ throw VoiceException::format("Could not create encoder: {}", opus_strerror(error));
+ else
+ return encoder;
+}
+
+void Voice::resetEncoder() {
+ int channels = encoderChannels();
+ m_encoder.reset(createEncoder(channels));
+ opus_encoder_ctl(m_encoder.get(), OPUS_SET_BITRATE(channels == 2 ? 50000 : 24000));
+}
+
+void Voice::openDevice() {
+ closeDevice();
+
+ m_deviceOpen = true;
+}
+
+void Voice::closeDevice() {
+ if (!m_deviceOpen)
+ return;
+
+ m_applicationController->closeAudioInputDevice();
+
+ m_deviceOpen = false;
+}
+
+} \ No newline at end of file