CoriEngine
Loading...
Searching...
No Matches
Track.cpp
Go to the documentation of this file.
1#include "Track.hpp"
2#include <SDL3_mixer/SDL_mixer.h>
3
4namespace Cori {
5 namespace Audio {
6 std::expected<void, Core::CoriError<>> Track::SetSound(const std::shared_ptr<Sound>& sound) {
7 if (m_Valid) {
8 if (!m_ActiveSequence) {
9 if (sound->IsValid()) {
10 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Assigning Sound '{} (SoundID: {})' to Track '{} (TrackID: {})'", sound->m_Name, sound->GetID(), m_Name, m_ID);
11 if (sound->IsPlaceholder()) {
12 CORI_CORE_WARN_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "A Sound '{} (SoundID: {})' to be assigned to Track '{} (TrackID: {})' is a placeholder, likely failed to load the original sound.", sound->m_Name, sound->GetID(), m_Name, m_ID);
13 }
14
15 return Mixer::SetTrackSound(m_ID, sound.get());
16 }
17
18 return std::unexpected(Core::CoriError(std::format("Failed to assign Sound '{} (SoundID: {})' to Track '{} (TrackID: {})'. Sound object is invalid.", sound->m_Name, sound->GetID(), m_Name, m_ID)));
19 }
20
21 return std::unexpected(Core::CoriError(std::format("Failed to assign Sound '{} (SoundID: {})' to Track '{} (TrackID: {})'. A sequence is currently playing on this track. Can't assign Sound when a sequence is playing.", sound->m_Name, sound->GetID(), m_Name, m_ID)));
22
23 }
24
25
26 return std::unexpected(Core::CoriError(std::format("Failed to assign Sound '{} (SoundID: {})' to Track '{} (TrackID: {})'. Track object is invalid.", sound->m_Name, sound->GetID(), m_Name, m_ID)));
27 }
28
29 std::expected<void, Core::CoriError<>> Track::Start(const PlayParams& params) {
30 if (m_Valid) {
31 if (!m_ActiveSequence) {
32 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Playing Track '{} (TrackID: {})'", m_Name, m_ID);
33 return Mixer::PlayTrack(m_ID, params);
34 }
35
36 return std::unexpected(Core::CoriError(std::format("Failed to play Track '{} (TrackID: {})'. A sequence is currently playing on this track. Can't play Sound when a sequence is playing.", m_Name, m_ID)));
37 }
38
39 return std::unexpected(Core::CoriError(std::format("Failed to play Track '{} (TrackID: {})'. Track object is invalid.", m_Name, m_ID)));
40 }
41
42 std::expected<void, Core::CoriError<>> Track::Stop(const bool abruptStop, const int64_t fadeOutMS) {
43 if (m_Valid) {
44 if (m_ActiveSequence) {
45 SetTrackStopCallbackInternal([this] {
46 m_ActiveSequence = false;
47 });
48 if (abruptStop) {
49 m_ActiveSequence = false;
50 return StopInternal(fadeOutMS);
51 }
52 }
53
54 CORI_CORE_DEBUG_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Track '{}' (ID: {}) has been stopped.", m_Name, m_ID);
55
56 return {};
57 }
58
59 return std::unexpected(Core::CoriError(std::format("Failed to stop sequence on Track '{} (TrackID: {})'. Track object is invalid.", m_Name, m_ID)));
60 }
61
62 std::expected<void, Core::CoriError<>> Track::Pause() {
63 if (m_Valid) {
64 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Pausing Track '{} (TrackID: {})'", m_Name, m_ID);
65 return Mixer::PauseTrack(m_ID);
66 }
67
68 return std::unexpected(Core::CoriError(std::format("Failed to pause Track '{} (TrackID: {})'. Track object is invalid.", m_Name, m_ID)));
69 }
70
71 std::expected<void, Core::CoriError<>> Track::Resume() {
72 if (m_Valid) {
73 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Resuming Track '{} (TrackID: {})'", m_Name, m_ID);
74 return Mixer::ResumeTrack(m_ID);
75 }
76
77 return std::unexpected(Core::CoriError(std::format("Failed to resume Track '{} (TrackID: {})'. Track object is invalid.", m_Name, m_ID)));
78 }
79
80 bool Track::IsPaused() const {
81 if (m_Valid) {
82 return Mixer::IsTrackPaused(m_ID);
83 }
84
85 return false;
86 }
87
88 bool Track::IsPlaying() const {
89 if (m_Valid) {
90 return Mixer::IsTrackPlaying(m_ID);
91 }
92
93 return false;
94 }
95
96 std::expected<void, Core::CoriError<>> Track::SetGain(const float gain) {
97 if (m_Valid) {
98 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Setting Track '{} (TrackID: {})' gain to '{}'", m_Name, m_ID, gain);
99 return Mixer::SetTrackGain(m_ID, gain);
100 }
101
102 return std::unexpected(Core::CoriError(std::format("Failed to set Track '{} (TrackID: {})' gain. Track object is invalid.", m_Name, m_ID)));
103 }
104
105 float Track::GetGain() const {
106 if (m_Valid) {
107 return Mixer::GetTrackGain(m_ID);
108 }
109
110 return 1.0f;
111 }
112
113 std::expected<void, Core::CoriError<>> Track::SetTag(const char* tag) {
114 if (m_Valid) {
115 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Assigning Tag '{}' to Track '{} (TrackID: {})'", tag, m_Name, m_ID);
116 m_CurrentTag = std::string(tag);
117 return Mixer::TagTrack(m_ID, tag);
118 }
119
120 return std::unexpected(Core::CoriError(std::format("Failed to assign Tag '{}' to Track '{} (TrackID: {})'. Track object is invalid.", tag, m_Name, m_ID)));
121 }
122
123 void Track::RemoveTag(const char* tag, const bool preserveCachedTag) {
124 if (m_Valid) {
125 CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Removing Tag '{}' from Track '{} (TrackID: {})'", tag, m_Name, m_ID);
126 Mixer::UntagTrack(m_ID, tag);
127 if (!preserveCachedTag) {
128 m_CurrentTag.clear();
129 m_CurrentTag.shrink_to_fit();
130 }
131 }
132 }
133
134 std::string_view Track::GetTag() const {
135 if (m_Valid) {
136 return m_CurrentTag;
137 }
138
139 return "";
140 }
141
142 bool Track::IsValid() const {
143 return m_Valid;
144 }
145
147 return m_ID;
148 }
149
151 m_ClientCallBack = std::move(callback);
152 }
153
154 std::shared_ptr<Track> Track::Create(std::string name) {
155 return std::shared_ptr<Track>(new Track(std::move(name)));
156 }
157
159 Mixer::DestroyTrack(m_ID);
160 }
161
162 void Track::TrackStopCallback(void* userdata, [[maybe_unused]] MIX_Track* track) {
163 try {
164 Track* coriTrack = static_cast<Track*>(userdata);
165
166 if (coriTrack->m_EngineCallBack) {
167 coriTrack->m_EngineCallBack();
168 }
169
170 if (coriTrack->m_ClientCallBack) {
171 coriTrack->m_ClientCallBack();
172 } else {
173 CORI_CORE_DEBUG_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Track '{}' (ID: {}) finished playing.", coriTrack->m_Name, coriTrack->m_ID);
174 }
175 }
176 catch (const std::exception& e) {
177 CORI_CORE_ERROR("{}", e.what());
178 }
179
180 }
181
182 Track::Track(std::string name): m_Name(std::move(name)), m_ID(s_NextIndex.fetch_add(1, std::memory_order_relaxed)) {
183 auto result = Mixer::CreateTrack(this);
184 if (!result) {
185 CORI_CORE_ERROR_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Failed to create Track '{} (ID: {})'. Error: {}. Invalid Track object was created as a result, this should not crash as the engine prevents you from using an invalid Track object.", m_Name, m_ID, result.error().what());
186 } else {
187 m_Valid = true;
188 CORI_CORE_DEBUG_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Track '{} (ID: {})' created.", m_Name, m_ID);
189 }
190 }
191
192 std::expected<void, Core::CoriError<>> Track::PlaySoundWithParams(SoundWithParams& object) {
193 return SetSound(object.first).and_then([this, object] {
194 return Start(object.second);
195 });
196 }
197
198 std::expected<void, Core::CoriError<>> Track::StopInternal(const int64_t fadeOutMS) const {
199 if (m_Valid) {
200 if (!m_ActiveSequence) {
201 //CORI_CORE_TRACE_TAGGED({ Logger::Tags::Audio::Self, Logger::Tags::Audio::Track }, "Stopping Track Stopping Track '{} (TrackID: {})'", m_Name, m_ID);
202 return Mixer::StopTrack(m_ID, fadeOutMS);
203 }
204
205 return std::unexpected(Core::CoriError(std::format("Failed to stop Track '{} (TrackID: {})'. A sequence is currently playing on this track. Can't stop when a sequence is playing.", m_Name, m_ID)));
206 }
207
208 return std::unexpected(Core::CoriError(std::format("Failed to stop Track '{} (TrackID: {})'. Track object is invalid.", m_Name, m_ID)));
209 }
210
211 std::expected<void, Core::CoriError<>> Track::ProcessSequencePart(const SoundWithParams& part) {
212 m_SoundSequence.push_back(part);
213
214 return {};
215 }
216
217 void Track::SetTrackStopCallbackInternal(TrackStopCallbackFn callback) {
218 m_EngineCallBack = std::move(callback);
219 }
220 }
221}
#define CORI_CORE_ERROR(...)
Definition Logger.hpp:1035
#define CORI_CORE_DEBUG_TAGGED(...)
Definition Logger.hpp:1026
#define CORI_CORE_TRACE_TAGGED(...)
Definition Logger.hpp:1025
#define CORI_CORE_ERROR_TAGGED(...)
Definition Logger.hpp:1039
#define CORI_CORE_WARN_TAGGED(...)
Definition Logger.hpp:1038
const std::string m_Name
Definition Track.hpp:258
void RemoveTag(const char *tag, const bool preserveCachedTag=false)
Removes the tag from the Track.
Definition Track.cpp:123
static void TrackStopCallback(void *userdata, MIX_Track *track)
For internal use only!
Definition Track.cpp:162
std::string_view GetTag() const
Gets the active/cached Track tag.
Definition Track.cpp:134
std::expected< void, Core::CoriError<> > Start(const PlayParams &params=PlayParams{})
Starts the Track that has a preassigned Sound asset.
Definition Track.cpp:29
std::expected< void, Core::CoriError<> > Resume()
Resumes the Track.
Definition Track.cpp:71
std::expected< void, Core::CoriError<> > Stop(const bool abruptStop, const int64_t fadeOutMS=0)
Stops the Track.
Definition Track.cpp:42
bool IsValid() const
Check if the Track is valid.
Definition Track.cpp:142
float GetGain() const
Returns the current Track gain. The default gain for a Track is 1.0f.
Definition Track.cpp:105
TrackID GetID() const
Returns the TrackID associated with Track.
Definition Track.cpp:146
std::expected< void, Core::CoriError<> > SetSound(const std::shared_ptr< Sound > &sound)
Assigns the Sound asset to the Track.
Definition Track.cpp:6
bool IsPaused() const
Checks if the Track is paused.
Definition Track.cpp:80
std::expected< void, Core::CoriError<> > SetGain(const float gain)
Sets the Track gain.
Definition Track.cpp:96
void SetTrackStopCallback(TrackStopCallbackFn callback)
Sets a callback to be run when the Track stops playing.
Definition Track.cpp:150
std::expected< void, Core::CoriError<> > SetTag(const char *tag)
Assigns the tag to the Track.
Definition Track.cpp:113
static std::shared_ptr< Track > Create(std::string name)
Creates a Track object.
Definition Track.cpp:154
std::expected< void, Core::CoriError<> > Pause()
Pauses the Track.
Definition Track.cpp:62
bool IsPlaying() const
Checks if the Track is playing.
Definition Track.cpp:88
Custom error class mainly used in std::expected.
Definition Error.hpp:27
Everything connected to audio is in this namespace.
Definition IDDefs.hpp:4
std::pair< std::shared_ptr< Sound >, PlayParams > SoundWithParams
Definition Track.hpp:11
uint16_t TrackID
Definition IDDefs.hpp:5
std::function< void()> TrackStopCallbackFn
Definition Track.hpp:10
Global engine namespace.
Parameters to be used when playing sound, you can mix your audio playback however you want with these...
Definition Mixer.hpp:20
static constexpr char Self[]
Definition Logger.hpp:75
static constexpr char Track[]
Definition Logger.hpp:78