Initial work
This commit is contained in:
parent
cf09616b1b
commit
40223a5090
@ -221,7 +221,7 @@ void Mixer::stopAll(float rampTime) {
|
|||||||
p.first->stop(vel);
|
p.first->stop(vel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mixer::read(int16_t* outBuffer, size_t frameCount) {
|
void Mixer::read(int16_t* outBuffer, size_t frameCount, ExtraMixFunction extraMixFunction) {
|
||||||
// Make this method as least locky as possible by copying all the needed
|
// Make this method as least locky as possible by copying all the needed
|
||||||
// member data before the expensive audio / effect stuff.
|
// member data before the expensive audio / effect stuff.
|
||||||
unsigned sampleRate;
|
unsigned sampleRate;
|
||||||
@ -326,7 +326,7 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount) {
|
|||||||
m_mixBuffer[s * channels + c] = 0;
|
m_mixBuffer[s * channels + c] = 0;
|
||||||
} else {
|
} else {
|
||||||
for (size_t c = 0; c < channels; ++c)
|
for (size_t c = 0; c < channels; ++c)
|
||||||
m_mixBuffer[s * channels + c] = m_mixBuffer[s * channels + c] * volume;
|
m_mixBuffer[s * channels + c] *= volume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,7 +338,8 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount) {
|
|||||||
float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd);
|
float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd);
|
||||||
for (size_t c = 0; c < channels; ++c) {
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value;
|
float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value;
|
||||||
outBuffer[s * channels + c] = clamp(sample + outBuffer[s * channels + c], -32767.0f, 32767.0f);
|
int16_t& outSample = outBuffer[s * channels + c];
|
||||||
|
outSample = clamp(sample + outSample, -32767.0f, 32767.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,6 +348,9 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extraMixFunction)
|
||||||
|
extraMixFunction(outBuffer, bufferSize, channels);
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexLocker locker(m_effectsMutex);
|
MutexLocker locker(m_effectsMutex);
|
||||||
// Apply all active effects
|
// Apply all active effects
|
||||||
|
@ -95,6 +95,7 @@ private:
|
|||||||
// Thread safe mixer class with basic effects support.
|
// Thread safe mixer class with basic effects support.
|
||||||
class Mixer {
|
class Mixer {
|
||||||
public:
|
public:
|
||||||
|
typedef function<void(int16_t* buffer, size_t frames, unsigned channels)> ExtraMixFunction;
|
||||||
typedef function<void(int16_t* buffer, size_t frames, unsigned channels)> EffectFunction;
|
typedef function<void(int16_t* buffer, size_t frames, unsigned channels)> EffectFunction;
|
||||||
typedef function<float(unsigned, Vec2F, float)> PositionalAttenuationFunction;
|
typedef function<float(unsigned, Vec2F, float)> PositionalAttenuationFunction;
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ public:
|
|||||||
|
|
||||||
// Reads pending audio data. This is thread safe with the other Mixer
|
// Reads pending audio data. This is thread safe with the other Mixer
|
||||||
// methods, but only one call to read may be active at a time.
|
// methods, but only one call to read may be active at a time.
|
||||||
void read(int16_t* samples, size_t frameCount);
|
void read(int16_t* samples, size_t frameCount, ExtraMixFunction extraMixFunction = {});
|
||||||
|
|
||||||
// Call within the main loop of the program using Mixer, calculates
|
// Call within the main loop of the program using Mixer, calculates
|
||||||
// positional attenuation of audio and does cleanup.
|
// positional attenuation of audio and does cleanup.
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
#include "StarWorldTemplate.hpp"
|
#include "StarWorldTemplate.hpp"
|
||||||
#include "StarWorldClient.hpp"
|
#include "StarWorldClient.hpp"
|
||||||
#include "StarRootLoader.hpp"
|
#include "StarRootLoader.hpp"
|
||||||
|
#include "StarInput.hpp"
|
||||||
|
#include "StarVoice.hpp"
|
||||||
|
|
||||||
|
|
||||||
#include "StarInterfaceLuaBindings.hpp"
|
#include "StarInterfaceLuaBindings.hpp"
|
||||||
#include "StarInputLuaBindings.hpp"
|
#include "StarInputLuaBindings.hpp"
|
||||||
@ -171,6 +174,7 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController)
|
|||||||
|
|
||||||
m_guiContext = make_shared<GuiContext>(m_mainMixer->mixer(), appController);
|
m_guiContext = make_shared<GuiContext>(m_mainMixer->mixer(), appController);
|
||||||
m_input = make_shared<Input>();
|
m_input = make_shared<Input>();
|
||||||
|
m_voice = make_shared<Voice>();
|
||||||
|
|
||||||
auto configuration = m_root->configuration();
|
auto configuration = m_root->configuration();
|
||||||
bool vsync = configuration->get("vsync").toBool();
|
bool vsync = configuration->get("vsync").toBool();
|
||||||
@ -417,8 +421,12 @@ void ClientApplication::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClientApplication::getAudioData(int16_t* sampleData, size_t frameCount) {
|
void ClientApplication::getAudioData(int16_t* sampleData, size_t frameCount) {
|
||||||
if (m_mainMixer)
|
if (m_mainMixer) {
|
||||||
m_mainMixer->read(sampleData, frameCount);
|
m_mainMixer->read(sampleData, frameCount, [&](int16_t* buffer, size_t frames, unsigned channels) {
|
||||||
|
if (m_voice)
|
||||||
|
m_voice->mix(buffer, frames, channels);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientApplication::changeState(MainAppState newState) {
|
void ClientApplication::changeState(MainAppState newState) {
|
||||||
|
@ -11,11 +11,13 @@
|
|||||||
#include "StarErrorScreen.hpp"
|
#include "StarErrorScreen.hpp"
|
||||||
#include "StarCinematic.hpp"
|
#include "StarCinematic.hpp"
|
||||||
#include "StarKeyBindings.hpp"
|
#include "StarKeyBindings.hpp"
|
||||||
#include "StarInput.hpp"
|
|
||||||
#include "StarMainApplication.hpp"
|
#include "StarMainApplication.hpp"
|
||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
|
STAR_CLASS(Input);
|
||||||
|
STAR_CLASS(Voice);
|
||||||
|
|
||||||
class ClientApplication : public Application {
|
class ClientApplication : public Application {
|
||||||
protected:
|
protected:
|
||||||
virtual void startup(StringList const& cmdLineArgs) override;
|
virtual void startup(StringList const& cmdLineArgs) override;
|
||||||
@ -76,6 +78,7 @@ private:
|
|||||||
MainMixerPtr m_mainMixer;
|
MainMixerPtr m_mainMixer;
|
||||||
GuiContextPtr m_guiContext;
|
GuiContextPtr m_guiContext;
|
||||||
InputPtr m_input;
|
InputPtr m_input;
|
||||||
|
VoicePtr m_voice;
|
||||||
|
|
||||||
// Valid after renderInit is called the first time
|
// Valid after renderInit is called the first time
|
||||||
CinematicPtr m_cinematicOverlay;
|
CinematicPtr m_cinematicOverlay;
|
||||||
|
@ -205,7 +205,7 @@ size_t DataStream::readVlqU(uint64_t& i) {
|
|||||||
size_t bytesRead = Star::readVlqU(i, makeFunctionInputIterator([this]() { return this->read<uint8_t>(); }));
|
size_t bytesRead = Star::readVlqU(i, makeFunctionInputIterator([this]() { return this->read<uint8_t>(); }));
|
||||||
|
|
||||||
if (bytesRead == NPos)
|
if (bytesRead == NPos)
|
||||||
throw DataStreamException("Error reading VLQ encoded intenger!");
|
throw DataStreamException("Error reading VLQ encoded integer!");
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
@ -214,7 +214,7 @@ size_t DataStream::readVlqI(int64_t& i) {
|
|||||||
size_t bytesRead = Star::readVlqI(i, makeFunctionInputIterator([this]() { return this->read<uint8_t>(); }));
|
size_t bytesRead = Star::readVlqI(i, makeFunctionInputIterator([this]() { return this->read<uint8_t>(); }));
|
||||||
|
|
||||||
if (bytesRead == NPos)
|
if (bytesRead == NPos)
|
||||||
throw DataStreamException("Error reading VLQ encoded intenger!");
|
throw DataStreamException("Error reading VLQ encoded integer!");
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
@ -164,4 +164,11 @@ void DataStreamExternalBuffer::reset(char const* externalData, size_t len) {
|
|||||||
m_buffer.reset(externalData, len);
|
m_buffer.reset(externalData, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataStreamExternalBuffer::readData(char* data, size_t len) {
|
||||||
|
m_buffer.readFull(data, len);
|
||||||
|
}
|
||||||
|
void DataStreamExternalBuffer::writeData(char const* data, size_t len) {
|
||||||
|
m_buffer.writeFull(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,9 @@ public:
|
|||||||
|
|
||||||
void reset(char const* externalData, size_t len);
|
void reset(char const* externalData, size_t len);
|
||||||
|
|
||||||
|
void readData(char* data, size_t len) override;
|
||||||
|
void writeData(char const* data, size_t len) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExternalBuffer m_buffer;
|
ExternalBuffer m_buffer;
|
||||||
};
|
};
|
||||||
|
@ -127,8 +127,8 @@ void MainMixer::setVolume(float volume, float rampTime) {
|
|||||||
m_mixer->setVolume(volume, rampTime);
|
m_mixer->setVolume(volume, rampTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMixer::read(int16_t* sampleData, size_t frameCount) {
|
void MainMixer::read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction extraMixFunction) {
|
||||||
m_mixer->read(sampleData, frameCount);
|
m_mixer->read(sampleData, frameCount, extraMixFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public:
|
|||||||
MixerPtr mixer() const;
|
MixerPtr mixer() const;
|
||||||
|
|
||||||
void setVolume(float volume, float rampTime = 0.0f);
|
void setVolume(float volume, float rampTime = 0.0f);
|
||||||
void read(int16_t* sampleData, size_t frameCount);
|
void read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction = {});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UniverseClientPtr m_universeClient;
|
UniverseClientPtr m_universeClient;
|
||||||
|
@ -156,6 +156,7 @@ SET (star_game_HEADERS
|
|||||||
StarVehicle.hpp
|
StarVehicle.hpp
|
||||||
StarVehicleDatabase.hpp
|
StarVehicleDatabase.hpp
|
||||||
StarVersioningDatabase.hpp
|
StarVersioningDatabase.hpp
|
||||||
|
StarVoice.hpp
|
||||||
StarWarping.hpp
|
StarWarping.hpp
|
||||||
StarWeather.hpp
|
StarWeather.hpp
|
||||||
StarWeatherTypes.hpp
|
StarWeatherTypes.hpp
|
||||||
@ -414,6 +415,7 @@ SET (star_game_SOURCES
|
|||||||
StarVehicle.cpp
|
StarVehicle.cpp
|
||||||
StarVehicleDatabase.cpp
|
StarVehicleDatabase.cpp
|
||||||
StarVersioningDatabase.cpp
|
StarVersioningDatabase.cpp
|
||||||
|
StarVoice.cpp
|
||||||
StarWarping.cpp
|
StarWarping.cpp
|
||||||
StarWeather.cpp
|
StarWeather.cpp
|
||||||
StarWeatherTypes.cpp
|
StarWeatherTypes.cpp
|
||||||
|
@ -383,6 +383,15 @@ void NetworkedAnimator::setGlobalTag(String tagName, String tagValue) {
|
|||||||
m_globalTags.set(move(tagName), move(tagValue));
|
m_globalTags.set(move(tagName), move(tagValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkedAnimator::removeGlobalTag(String const& tagName) {
|
||||||
|
m_globalTags.remove(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
String const* NetworkedAnimator::globalTagPtr(String const& tagName) const {
|
||||||
|
return m_globalTags.ptr(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void NetworkedAnimator::setPartTag(String const& partType, String tagName, String tagValue) {
|
void NetworkedAnimator::setPartTag(String const& partType, String tagName, String tagValue) {
|
||||||
m_partTags[partType].set(move(tagName), move(tagValue));
|
m_partTags[partType].set(move(tagName), move(tagValue));
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,8 @@ public:
|
|||||||
// Drawables can also have a <frame> tag which will be set to whatever the
|
// Drawables can also have a <frame> tag which will be set to whatever the
|
||||||
// current state frame is (1 indexed, so the first frame is 1).
|
// current state frame is (1 indexed, so the first frame is 1).
|
||||||
void setGlobalTag(String tagName, String tagValue);
|
void setGlobalTag(String tagName, String tagValue);
|
||||||
|
void removeGlobalTag(String const& tagName);
|
||||||
|
String const* globalTagPtr(String const& tagName) const;
|
||||||
void setPartTag(String const& partType, String tagName, String tagValue);
|
void setPartTag(String const& partType, String tagName, String tagValue);
|
||||||
|
|
||||||
void setProcessingDirectives(Directives const& directives);
|
void setProcessingDirectives(Directives const& directives);
|
||||||
|
@ -2464,4 +2464,53 @@ Vec2F Player::cameraPosition() {
|
|||||||
return position();
|
return position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkedAnimatorPtr Player::effectsAnimator() {
|
||||||
|
return m_effectsAnimator;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String secretProprefix = strf("{:c}JsonProperty{:c}", 0, 0);
|
||||||
|
|
||||||
|
Maybe<StringView> Player::getSecretPropertyView(String const& name) const {
|
||||||
|
if (auto tag = m_effectsAnimator->globalTagPtr(secretProprefix + name)) {
|
||||||
|
auto& view = tag->utf8();
|
||||||
|
DataStreamExternalBuffer buffer(view.data(), view.size());
|
||||||
|
try {
|
||||||
|
uint8_t typeIndex = buffer.read<uint8_t>() - 1;
|
||||||
|
if ((Json::Type)typeIndex == Json::Type::String) {
|
||||||
|
size_t len = buffer.readVlqU();
|
||||||
|
size_t pos = buffer.pos();
|
||||||
|
if (pos + len == buffer.size())
|
||||||
|
return StringView(buffer.ptr() + pos, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (StarException const& e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Json Player::getSecretProperty(String const& name, Json defaultValue) const {
|
||||||
|
if (auto tag = m_effectsAnimator->globalTagPtr(secretProprefix + name)) {
|
||||||
|
DataStreamExternalBuffer buffer(tag->utf8Ptr(), tag->utf8Size());
|
||||||
|
try
|
||||||
|
{ return buffer.read<Json>(); }
|
||||||
|
catch (StarException const& e)
|
||||||
|
{ Logger::error("Exception reading secret player property '{}': {}", name, e.what()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
return move(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setSecretProperty(String const& name, Json const& value) {
|
||||||
|
if (value) {
|
||||||
|
DataStreamBuffer ds;
|
||||||
|
ds.write(value);
|
||||||
|
auto& data = ds.data();
|
||||||
|
m_effectsAnimator->setGlobalTag(secretProprefix + name, String(data.ptr(), data.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_effectsAnimator->removeGlobalTag(secretProprefix + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -447,6 +447,28 @@ public:
|
|||||||
|
|
||||||
using Entity::setTeam;
|
using Entity::setTeam;
|
||||||
|
|
||||||
|
NetworkedAnimatorPtr effectsAnimator();
|
||||||
|
|
||||||
|
// We need to store ephemeral/large/always-changing networked properties that other clients can read. Candidates:
|
||||||
|
// genericProperties:
|
||||||
|
// Non-starter, is not networked.
|
||||||
|
// statusProperties:
|
||||||
|
// Nope! Changes to the status properties aren't networked efficiently - one change resends the whole map.
|
||||||
|
// We can't fix that because it would break compatibility with vanilla servers.
|
||||||
|
// effectsAnimator's globalTags:
|
||||||
|
// Cursed, but viable.
|
||||||
|
// Efficient networking due to using a NetElementMapWrapper.
|
||||||
|
// Unfortunately values are Strings, so to work with Json we need to serialize/deserialize. Whatever.
|
||||||
|
// Additionally, this is compatible with vanilla networking.
|
||||||
|
// I call this a 'secret property'.
|
||||||
|
|
||||||
|
// If the secret property exists as a serialized Json string, returns a view to it without deserializing.
|
||||||
|
Maybe<StringView> getSecretPropertyView(String const& name) const;
|
||||||
|
// Gets a secret Json property. It will be de-serialized.
|
||||||
|
Json getSecretProperty(String const& name, Json defaultValue = Json()) const;
|
||||||
|
// Sets a secret Json property. It will be serialized.
|
||||||
|
void setSecretProperty(String const& name, Json const& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class State {
|
enum class State {
|
||||||
Idle,
|
Idle,
|
||||||
|
35
source/game/StarVoice.cpp
Normal file
35
source/game/StarVoice.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "StarVoice.hpp"
|
||||||
|
|
||||||
|
namespace Star {
|
||||||
|
|
||||||
|
STAR_EXCEPTION(VoiceException, StarException);
|
||||||
|
|
||||||
|
void Voice::mix(int16_t* buffer, size_t frames, unsigned channels) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if (s_singleton)
|
||||||
|
throw VoiceException("Singleton Voice has been constructed twice");
|
||||||
|
|
||||||
|
s_singleton = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Voice::~Voice() {
|
||||||
|
s_singleton = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
source/game/StarVoice.hpp
Normal file
32
source/game/StarVoice.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef STAR_VOICE_HPP
|
||||||
|
#define STAR_VOICE_HPP
|
||||||
|
#include "StarString.hpp"
|
||||||
|
|
||||||
|
namespace Star {
|
||||||
|
|
||||||
|
STAR_CLASS(Voice);
|
||||||
|
|
||||||
|
class Voice {
|
||||||
|
public:
|
||||||
|
void mix(int16_t* buffer, size_t frames, unsigned channels);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
Voice();
|
||||||
|
~Voice();
|
||||||
|
|
||||||
|
Voice(Voice const&) = delete;
|
||||||
|
Voice& operator=(Voice const&) = delete;
|
||||||
|
private:
|
||||||
|
static Voice* s_singleton;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user