Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
AudioRuntime.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "DataBanks.hpp"
4#include "LegacyWave.hpp"
5#include "ResourceRuntime.hpp"
6
7#include <cstdint>
8#include <expected>
9#include <memory>
10#include <optional>
11#include <span>
12#include <string>
13#include <string_view>
14#include <vector>
15
16namespace monopoly::audio
17{
18 // Source/artlib/L_Sound.cpp::LE_SOUND_SetPitchBufSnd.
19 // Zero restores the original recording rate; DirectSound 7 clamps
20 // explicit frequencies to 100..100000 Hz. SDL additionally constrains
21 // its stream ratio to 0.01..100.
23 std::uint32_t requestedHertz,
24 std::uint32_t originalHertz) noexcept
25 {
26 if (originalHertz == 0U || requestedHertz == 0U)
27 return 1.0F;
28 const auto effectiveHertz = requestedHertz < 100U ? 100U :
29 requestedHertz > 100'000U ? 100'000U : requestedHertz;
30 const float ratio = static_cast<float>(effectiveHertz) /
31 static_cast<float>(originalHertz);
32 return ratio < 0.01F ? 0.01F : ratio > 100.0F ? 100.0F : ratio;
33 }
34
36 {
37 float left{1.0F};
38 float right{1.0F};
39 };
40
41 // Source/artlib/L_Sound.cpp::LE_SOUND_SetPanBufSnd. The retail
42 // nLogVolume table encodes this percentage balance as DirectSound dB:
43 // -100 is full left, 0 leaves both channels unchanged, +100 is full right.
45 std::int32_t percentage) noexcept
46 {
47 const auto pan = percentage < -100 ? -100 :
48 percentage > 100 ? 100 : percentage;
49 if (pan <= 0)
50 return {1.0F, static_cast<float>(pan + 100) / 100.0F};
51 return {static_cast<float>(100 - pan) / 100.0F, 1.0F};
52 }
53
55 {
56 std::uint8_t volume{};
57 std::int8_t panning{};
58 };
59
60 // Source/artlib/L_Rend2D.cpp::SequenceMoved. Monopoly's 2D slots use
61 // the 800-pixel virtual screen and fade sounds to silence 1000 pixels
62 // beyond either edge.
64 std::uint8_t baseVolume, std::int32_t centerX,
65 std::int32_t left = 0, std::int32_t right = 800,
66 std::int32_t quietDistance = 1000) noexcept
67 {
68 std::int32_t volume = baseVolume > 100U ? 100 : baseVolume;
69 if (right <= left || quietDistance <= 0)
70 return {static_cast<std::uint8_t>(volume), 0};
71
72 const auto rawPan = static_cast<std::int32_t>(
73 (200LL * (static_cast<std::int64_t>(centerX) - left)) /
74 (right - left) - 100LL);
75 std::int32_t pan = rawPan;
76 std::int64_t distance{};
77 if (pan < -100)
78 {
79 pan = -100;
80 distance = static_cast<std::int64_t>(left) - centerX;
81 }
82 else if (pan > 100)
83 {
84 pan = 100;
85 distance = static_cast<std::int64_t>(centerX) - right;
86 }
87
89 volume = 0;
90 else if (distance > 0)
91 volume = static_cast<std::int32_t>(
92 static_cast<std::int64_t>(volume) *
94
95 return {static_cast<std::uint8_t>(volume),
96 static_cast<std::int8_t>(pan)};
97 }
98
99 enum class PlaybackDomain : std::uint8_t
100 {
101 Sequence,
102 Interface,
103 Music,
104 Voice
105 };
106
108 {
110 std::uint64_t id{};
111 auto operator<=>(const PlaybackKey&) const = default;
112 };
114 {
115 public:
116 explicit Runtime(
117 std::shared_ptr<const data::ResourceSnapshot> resources);
118 ~Runtime();
119
120 Runtime(const Runtime&) = delete;
121 Runtime& operator=(const Runtime&) = delete;
122
123 [[nodiscard]] std::expected<void, std::string> ensureReady();
124 [[nodiscard]] std::expected<void, std::string> play(
126 data::DataId waveDataId,
127 float gain = 1.0F,
128 bool loop = false,
129 std::uint32_t pitchHertz = 0U,
130 std::int32_t panPercentage = 0);
131 [[nodiscard]] std::expected<void, std::string> playFile(
133 std::string_view relativePath,
134 float gain = 1.0F,
135 bool loop = false,
136 std::uint32_t pitchHertz = 0U,
137 std::int32_t panPercentage = 0);
138 void stop(PlaybackKey key) noexcept;
139 void stopAll() noexcept;
140 void setGain(PlaybackKey key, float gain) noexcept;
144 // Sequence clocks observe PCM consumed by SDL's device stream. Parent
145 // render time is deliberately absent: pitch, stalls and pause affect it.
147 [[nodiscard]] std::expected<void, std::string> synchronizeSequence(
148 PlaybackKey key, std::int32_t position, bool paused, std::uint64_t seekGeneration);
149 void update() noexcept;
151 [[nodiscard]] data::BoardEdition boardEdition() const noexcept
152 {
153 return resources_ ? resources_->context().board : data::BoardEdition::Usa;
154 }
156 {
157 return resources_ ? resources_->context().language : data::LanguageId::EnglishUs;
158 }
159
160 private:
161 struct Voice;
162 [[nodiscard]] std::expected<void, std::string> ensureAudio();
163 [[nodiscard]] std::expected<void, std::string> seekVoice(Voice& voice, std::int32_t tick);
164 [[nodiscard]] std::expected<void, std::string> queueVoiceFromByte(Voice& voice, std::uint64_t offset);
165 [[nodiscard]] Voice* find(PlaybackKey key) noexcept;
166 [[nodiscard]] const Voice* find(PlaybackKey key) const noexcept;
167
168 std::shared_ptr<const data::ResourceSnapshot> resources_;
169 std::vector<std::unique_ptr<Voice>> voices_;
170 bool ownsAudioSubsystem_{};
171 bool audioReady_{};
172 };
173}
std::size_t offset
std::atomic< float > left
std::atomic< float > right
std::uint64_t key
unsigned index
std::uint8_t volume
data::BoardEdition boardEdition() const noexcept
data::LanguageId language() const noexcept
std::expected< void, std::string > synchronizeSequence(PlaybackKey key, std::int32_t position, bool paused, std::uint64_t seekGeneration)
void stop(PlaybackKey key) noexcept
void setPitch(PlaybackKey key, std::uint32_t hertz) noexcept
void setGain(PlaybackKey key, float gain) noexcept
void setLooping(PlaybackKey key, bool loop) noexcept
void setPanning(PlaybackKey key, std::int32_t percentage) noexcept
Runtime & operator=(const Runtime &)=delete
std::expected< void, std::string > playFile(PlaybackKey key, std::string_view relativePath, float gain=1.0F, bool loop=false, std::uint32_t pitchHertz=0U, std::int32_t panPercentage=0)
bool active(PlaybackKey key) const noexcept
std::expected< void, std::string > play(PlaybackKey key, data::DataId waveDataId, float gain=1.0F, bool loop=false, std::uint32_t pitchHertz=0U, std::int32_t panPercentage=0)
Runtime(const Runtime &)=delete
std::optional< std::int32_t > positionTicks(PlaybackKey key) const noexcept
std::expected< void, std::string > ensureReady()
constexpr Legacy2DSoundMix legacy2DSoundMix(std::uint8_t baseVolume, std::int32_t centerX, std::int32_t left=0, std::int32_t right=800, std::int32_t quietDistance=1000) noexcept
constexpr StereoPanGains legacyPanGains(std::int32_t percentage) noexcept
constexpr float legacyPitchFrequencyRatio(std::uint32_t requestedHertz, std::uint32_t originalHertz) noexcept
std::uint32_t DataId
Definition DataBanks.hpp:10
auto operator<=>(const PlaybackKey &) const =default