utility 2026.1.9
A comprehensive C++ utilities library tailored for the development of modern desktop and extended reality (XR) applications.
Loading...
Searching...
No Matches
audio_manager.cpp
1/*
2 Copyright (c) 2026 ETIB Corporation
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22#include "utility/sound/audio_manager.hpp"
23
25{
26 _device = alcOpenDevice(nullptr);
27 if (!_device) {
28 getLogger().warning()
29 << "Failed to open audio device; audio is disabled";
30 _running = false;
31 return;
32 }
33
34 _context = alcCreateContext(_device, nullptr);
35 if (!_context) {
36 getLogger().warning()
37 << "Failed to create audio context; audio is disabled";
38 alcCloseDevice(_device);
39 _device = nullptr;
40 _running = false;
41 return;
42 }
43
44 getLogger().info() << "Audio manager initialized successfully";
45
46 _audioThread = std::thread(&AudioManager::threadLoop, this);
47}
48
50{
51 stop();
52
53 if (_audioThread.joinable())
54 _audioThread.join();
55
56 alcDestroyContext(_context);
57 alcCloseDevice(_device);
58
59 getLogger().info() << "Audio manager shut down";
60}
61
63{
64 std::lock_guard<std::mutex> lock(_commandQueueMutex);
65 _commandQueue.push_back(command);
66}
67
68std::unique_ptr<utility::sound::AudioSource>
70 std::shared_ptr<AudioBuffer> buffer)
71{
72 auto id = _nextSourceID++;
73
74 auto audioSource = std::make_unique<AudioSource>(id, *this);
75
76 submitCommand({ AudioCommandType::CreateSource, id, {} });
77
78 {
79 std::lock_guard<std::mutex> lock(_sourcesMutex);
80 _sources[id] = 0; // Placeholder for ALuint source ID
81 }
82
83 getLogger().debug() << "Created audio source with ID: " << id;
84
85 audioSource->setBuffer(buffer);
86
87 return audioSource;
88}
89
91{
92 std::lock_guard lock(_sourcesMutex);
93
94 auto it = _sources.find(sourceID);
95
96 if (it == _sources.end())
97 return;
98
99 ALuint alId = it->second;
100
101 alSourceStop(alId);
102 alSourcei(alId, AL_BUFFER, 0);
103 alDeleteSources(1, &alId);
104
105 _sources.erase(it);
106}
107
109{
110 getLogger().info() << "Stopping audio manager";
111 _running = false;
112}
113
115{
116 return _running;
117}
118
119void utility::sound::AudioManager::threadLoop()
120{
121 alcMakeContextCurrent(_context);
122
123 while (_running) {
124 processCommands();
125 std::this_thread::sleep_for(std::chrono::milliseconds(1));
126 }
127
128 processCommands(); // Process any remaining commands before shutting down
129
130 alcMakeContextCurrent(nullptr);
131}
132
133void utility::sound::AudioManager::processCommands()
134{
135 std::deque<AudioCommand> commandsToProcess;
136
137 {
138 std::lock_guard<std::mutex> lock(_commandQueueMutex);
139 commandsToProcess.swap(_commandQueue);
140 }
141
142 while (!commandsToProcess.empty()) {
143 executeCommand(commandsToProcess.front());
144 commandsToProcess.pop_front();
145 }
146}
147
148void utility::sound::AudioManager::executeCommand(const AudioCommand &command)
149{
150 ALuint alId;
151 {
152 std::lock_guard<std::mutex> lock(_sourcesMutex);
153 auto it = _sources.find(command.sourceID);
154 if (it == _sources.end()) {
155 getLogger().warning()
156 << "Audio command failed: source not found (ID: "
157 << command.sourceID << ")";
158 return;
159 }
160 alId = it->second;
161 }
162
163 switch (command.type) {
164 case AudioCommandType::Play:
165 alSourcePlay(alId);
166 break;
167 case AudioCommandType::Stop:
168 alSourceStop(alId);
169 break;
170 case AudioCommandType::Pause:
171 alSourcePause(alId);
172 break;
173 case AudioCommandType::SetPosition:
174 // source->setPosition(command.data.position);
175 break;
176 case AudioCommandType::SetGain:
177 alSourcef(alId, AL_GAIN, command.data.gain);
178 break;
179 case AudioCommandType::SetPitch:
180 alSourcef(alId, AL_PITCH, command.data.pitch);
181 break;
182 case AudioCommandType::SetLooping:
183 alSourcei(alId, AL_LOOPING,
184 command.data.looping ? AL_TRUE : AL_FALSE);
185 break;
186 case AudioCommandType::CreateSource: {
187 ALuint newSource;
188 alGenSources(1, &newSource);
189 {
190 std::lock_guard<std::mutex> lock(_sourcesMutex);
191 _sources[command.sourceID] = newSource;
192 }
193 } break;
194 case AudioCommandType::SetBuffer:
195 alSourcei(alId, AL_BUFFER, command.data.bufferID);
196 break;
197 case AudioCommandType::DestroySource:
198 alSourceStop(alId);
199 alDeleteSources(1, &alId);
200 {
201 std::lock_guard<std::mutex> lock(_sourcesMutex);
202 _sources.erase(command.sourceID);
203 }
204 break;
205 default:
206 getLogger().warning() << "Unknown audio command type";
207 break;
208 }
209}
bool isRunning() const
Indicates whether the audio manager is running.
AudioManager()
Creates and initializes the audio subsystem.
void stop()
Requests termination of the audio thread.
~AudioManager()
Stops the audio thread and releases OpenAL resources.
void submitCommand(const AudioCommand &command)
Submits an audio command for asynchronous execution.
std::unique_ptr< AudioSource > createAudioSource(std::shared_ptr< AudioBuffer > buffer)
Creates a new audio source.
void destroyAudioSource(uint32_t sourceID)
Destroys an existing audio source.
Represents a command submitted to the audio thread.