2023-07-12 12:16:12 +00:00
|
|
|
#ifndef STAR_VOICE_HPP
|
|
|
|
#define STAR_VOICE_HPP
|
2023-07-13 05:01:07 +00:00
|
|
|
#include "StarJson.hpp"
|
|
|
|
#include "StarBiMap.hpp"
|
|
|
|
#include "StarException.hpp"
|
2023-07-13 09:12:55 +00:00
|
|
|
#include "StarGameTypes.hpp"
|
|
|
|
#include "StarMaybe.hpp"
|
|
|
|
#include "StarApplicationController.hpp"
|
2023-07-13 05:01:07 +00:00
|
|
|
|
|
|
|
struct OpusDecoder;
|
|
|
|
typedef std::unique_ptr<OpusDecoder, void(*)(OpusDecoder*)> OpusDecoderPtr;
|
|
|
|
struct OpusEncoder;
|
|
|
|
typedef std::unique_ptr<OpusEncoder, void(*)(OpusEncoder*)> OpusEncoderPtr;
|
2023-07-12 12:16:12 +00:00
|
|
|
|
|
|
|
namespace Star {
|
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
STAR_EXCEPTION(VoiceException, StarException);
|
|
|
|
|
2023-07-13 09:12:55 +00:00
|
|
|
enum class VoiceInputMode : uint8_t { VoiceActivity, PushToTalk };
|
|
|
|
extern EnumMap<VoiceInputMode> const VoiceInputModeNames;
|
2023-07-13 05:01:07 +00:00
|
|
|
|
|
|
|
enum class VoiceChannelMode: uint8_t { Mono = 1, Stereo = 2 };
|
|
|
|
extern EnumMap<VoiceChannelMode> const VoiceChannelModeNames;
|
2023-07-12 12:16:12 +00:00
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
STAR_CLASS(Voice);
|
2023-07-13 09:12:55 +00:00
|
|
|
STAR_CLASS(ApplicationController);
|
2023-07-12 12:16:12 +00:00
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
class Voice {
|
|
|
|
public:
|
|
|
|
// Individual speakers are represented by their connection ID.
|
|
|
|
typedef ConnectionId SpeakerId;
|
2023-07-12 12:16:12 +00:00
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
struct Speaker {
|
|
|
|
SpeakerId speakerId = 0;
|
|
|
|
EntityId entityId = 0;
|
2023-07-13 09:12:55 +00:00
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
Vec2F position = Vec2F();
|
|
|
|
String name = "Unnamed";
|
2023-07-12 12:16:12 +00:00
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
OpusDecoderPtr decoderMono;
|
|
|
|
OpusDecoderPtr decoderStereo;
|
2023-07-12 12:16:12 +00:00
|
|
|
|
2023-07-13 05:01:07 +00:00
|
|
|
atomic<bool> active = false;
|
|
|
|
atomic<float> currentLoudness = 0.0f;
|
|
|
|
atomic<Array<float, 2>> channelVolumes = Array<float, 2>::filled(1.0f);
|
|
|
|
|
|
|
|
Speaker(SpeakerId speakerId);
|
2023-07-12 12:16:12 +00:00
|
|
|
};
|
2023-07-13 05:01:07 +00:00
|
|
|
|
|
|
|
typedef std::shared_ptr<Speaker> SpeakerPtr;
|
|
|
|
|
|
|
|
// Get pointer to the singleton Voice instance, if it exists. Otherwise,
|
|
|
|
// returns nullptr.
|
|
|
|
static Voice* singletonPtr();
|
|
|
|
|
|
|
|
// Gets reference to Voice singleton, throws VoiceException if root
|
|
|
|
// is not initialized.
|
|
|
|
static Voice& singleton();
|
|
|
|
|
2023-07-13 09:12:55 +00:00
|
|
|
Voice(ApplicationControllerPtr appController);
|
2023-07-13 05:01:07 +00:00
|
|
|
~Voice();
|
|
|
|
|
|
|
|
Voice(Voice const&) = delete;
|
|
|
|
Voice& operator=(Voice const&) = delete;
|
|
|
|
|
|
|
|
void load(Json const& config);
|
|
|
|
Json save() const;
|
|
|
|
|
|
|
|
// Sets the local speaker ID and returns the local speaker. Must be called upon loading into a world.
|
|
|
|
SpeakerPtr setLocalSpeaker(SpeakerId speakerId);
|
|
|
|
SpeakerPtr speaker(SpeakerId speakerId);
|
|
|
|
|
|
|
|
// Called to mix voice audio with the game.
|
|
|
|
void mix(int16_t* buffer, size_t frames, unsigned channels);
|
|
|
|
|
|
|
|
typedef function<float(unsigned, Vec2F, float)> PositionalAttenuationFunction;
|
|
|
|
void update(PositionalAttenuationFunction positionalAttenuationFunction = {});
|
|
|
|
|
|
|
|
inline int encoderChannels() const {
|
|
|
|
return m_channelMode == VoiceChannelMode::Mono ? 1 : 2;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
static Voice* s_singleton;
|
|
|
|
|
|
|
|
static OpusDecoder* createDecoder(int channels);
|
|
|
|
static OpusEncoder* createEncoder(int channels);
|
|
|
|
void resetEncoder();
|
2023-07-13 09:12:55 +00:00
|
|
|
void openDevice();
|
|
|
|
void closeDevice();
|
2023-07-13 05:01:07 +00:00
|
|
|
|
|
|
|
SpeakerId m_speakerId = 0;
|
|
|
|
SpeakerPtr m_clientSpeaker;
|
|
|
|
HashMap<SpeakerId, SpeakerPtr> m_speakers;
|
|
|
|
|
|
|
|
HashSet<SpeakerPtr> m_activeSpeakers;
|
|
|
|
|
|
|
|
OpusEncoderPtr m_encoder;
|
|
|
|
|
2023-07-13 09:12:55 +00:00
|
|
|
bool m_deviceOpen = false;
|
|
|
|
Maybe<String> m_deviceName;
|
|
|
|
VoiceInputMode m_inputMode;
|
2023-07-13 05:01:07 +00:00
|
|
|
VoiceChannelMode m_channelMode;
|
2023-07-13 09:12:55 +00:00
|
|
|
|
|
|
|
ApplicationControllerPtr m_applicationController;
|
2023-07-13 05:01:07 +00:00
|
|
|
};
|
2023-07-12 12:16:12 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|