1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
#include "StarMainMixer.hpp"
#include "StarJsonExtra.hpp"
#include "StarRoot.hpp"
#include "StarConfiguration.hpp"
#include "StarUniverseClient.hpp"
#include "StarPlayer.hpp"
#include "StarAssets.hpp"
#include "StarWorldClient.hpp"
#include "StarWorldPainter.hpp"
#include "StarVoice.hpp"
namespace Star {
MainMixer::MainMixer(unsigned sampleRate, unsigned channels) {
m_mixer = make_shared<Mixer>(sampleRate, channels);
}
void MainMixer::setUniverseClient(UniverseClientPtr universeClient) {
m_universeClient = std::move(universeClient);
}
void MainMixer::setWorldPainter(WorldPainterPtr worldPainter) {
m_worldPainter = std::move(worldPainter);
}
void MainMixer::update(float dt, bool muteSfx, bool muteMusic) {
auto assets = Root::singleton().assets();
auto updateGroupVolume = [&](MixerGroup group, bool muted, String const& settingName) {
if (m_mutedGroups.contains(group) != muted) {
if (muted) {
m_mutedGroups.add(group);
m_mixer->setGroupVolume(group, 0, 1.0f);
} else {
m_mutedGroups.remove(group);
m_mixer->setGroupVolume(group, m_groupVolumes[group], 1.0f);
}
} else if (!m_mutedGroups.contains(group)) {
float volumeSetting = Root::singleton().configuration()->get(settingName).toFloat() / 100.0f;
volumeSetting = perceptualToAmplitude(volumeSetting);
if (!m_groupVolumes.contains(group) || volumeSetting != m_groupVolumes[group]) {
m_mixer->setGroupVolume(group, volumeSetting);
m_groupVolumes[group] = volumeSetting;
}
}
};
updateGroupVolume(MixerGroup::Effects, muteSfx, "sfxVol");
updateGroupVolume(MixerGroup::Music, muteMusic, "musicVol");
updateGroupVolume(MixerGroup::Cinematic, false, "sfxVol");
updateGroupVolume(MixerGroup::Instruments, muteSfx, "instrumentVol");
WorldClientPtr currentWorld;
if (m_universeClient)
currentWorld = m_universeClient->worldClient();
if (currentWorld) {
for (auto audioInstance : currentWorld->pullPendingAudio())
m_mixer->play(audioInstance);
for (auto audioInstance : currentWorld->pullPendingMusic()) {
audioInstance->setMixerGroup(MixerGroup::Music);
m_mixer->play(audioInstance);
}
if (m_universeClient && m_universeClient->mainPlayer()->underwater()) {
if (!m_mixer->hasEffect("lowpass"))
m_mixer->addEffect("lowpass", m_mixer->lowpass(32), 0.50f);
if (!m_mixer->hasEffect("echo"))
m_mixer->addEffect("echo", m_mixer->echo(0.2f, 0.6f, 0.4f), 0.50f);
} else {
if (m_mixer->hasEffect("lowpass"))
m_mixer->removeEffect("lowpass", 0.5f);
if (m_mixer->hasEffect("echo"))
m_mixer->removeEffect("echo", 0.5f);
}
float baseMaxDistance = assets->json("/sfx.config:baseMaxDistance").toFloat();
Vec2F stereoAdjustmentRange = jsonToVec2F(assets->json("/sfx.config:stereoAdjustmentRange"));
float attenuationGamma = assets->json("/sfx.config:attenuationGamma").toFloat();
auto playerPos = m_universeClient->mainPlayer()->position();
auto cameraPos = m_worldPainter->camera().centerWorldPosition();
auto worldGeometry = currentWorld->geometry();
Mixer::PositionalAttenuationFunction attenuationFunction = [&](unsigned channel, Vec2F pos, float rangeMultiplier) {
Vec2F playerDiff = worldGeometry.diff(pos, playerPos);
Vec2F cameraDiff = worldGeometry.diff(pos, cameraPos);
float playerMagSq = playerDiff.magnitudeSquared();
float cameraMagSq = cameraDiff.magnitudeSquared();
Vec2F diff;
float diffMagnitude;
if (playerMagSq < cameraMagSq) {
diff = playerDiff;
diffMagnitude = sqrt(playerMagSq);
}
else {
diff = cameraDiff;
diffMagnitude = sqrt(cameraMagSq);
}
if (diffMagnitude == 0.0f)
return 0.0f;
Vec2F diffNorm = diff / diffMagnitude;
float stereoIncidence = channel == 0 ? -diffNorm[0] : diffNorm[0];
float maxDistance = baseMaxDistance * rangeMultiplier * lerp((stereoIncidence + 1.0f) / 2.0f, stereoAdjustmentRange[0], stereoAdjustmentRange[1]);
return pow(clamp(diffMagnitude / maxDistance, 0.0f, 1.0f), 1.0f / attenuationGamma);
};
if (Voice* voice = Voice::singletonPtr())
voice->update(dt, attenuationFunction);
m_mixer->update(dt, attenuationFunction);
} else {
if (m_mixer->hasEffect("lowpass"))
m_mixer->removeEffect("lowpass", 0);
if (m_mixer->hasEffect("echo"))
m_mixer->removeEffect("echo", 0);
if (Voice* voice = Voice::singletonPtr())
voice->update(dt);
m_mixer->update(dt);
}
}
MixerPtr MainMixer::mixer() const {
return m_mixer;
}
void MainMixer::setSpeed(float speed) {
m_mixer->setSpeed(max(speed, 0.0f));
}
void MainMixer::setVolume(float volume, float rampTime) {
m_mixer->setVolume(volume, rampTime);
}
void MainMixer::read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction extraMixFunction) {
m_mixer->read(sampleData, frameCount, extraMixFunction);
}
}
|