2
2
// SPDX-License-Identifier: GPL-2.0-or-later
3
3
4
4
#include < memory>
5
- #include < mutex>
6
- #include < shared_mutex>
7
5
#include < magic_enum/magic_enum.hpp>
8
6
9
7
#include " common/assert.h"
18
16
19
17
namespace Libraries ::AudioOut {
20
18
21
- std::shared_mutex ports_mutex ;
19
+ std::mutex port_open_mutex{} ;
22
20
std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
23
21
24
22
static std::unique_ptr<AudioOutBackend> audio;
@@ -93,17 +91,20 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
93
91
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
94
92
}
95
93
96
- std::scoped_lock lock (ports_mutex) ;
94
+ std::unique_lock open_lock{port_open_mutex} ;
97
95
auto & port = ports_out.at (handle - 1 );
98
- if (!port.impl ) {
99
- return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
96
+ {
97
+ std::unique_lock lock{port.mutex };
98
+ if (!port.IsOpen ()) {
99
+ return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
100
+ }
101
+ std::free (port.output_buffer );
102
+ port.output_buffer = nullptr ;
103
+ port.output_ready = false ;
104
+ port.impl = nullptr ;
100
105
}
101
-
106
+ // Stop outside of port lock scope to prevent deadlocks.
102
107
port.output_thread .Stop ();
103
- std::free (port.output_buffer );
104
- port.output_buffer = nullptr ;
105
- port.output_ready = false ;
106
- port.impl = nullptr ;
107
108
return ORBIS_OK;
108
109
}
109
110
@@ -172,35 +173,34 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
172
173
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
173
174
}
174
175
175
- std::scoped_lock lock (ports_mutex );
176
- const auto & port = ports_out. at (handle - 1 );
177
- if (!port. impl ) {
178
- return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
179
- }
180
-
181
- state-> rerouteCounter = 0 ;
182
- state-> volume = 127 ;
183
-
184
- switch (port. type ) {
185
- case OrbisAudioOutPort::Main:
186
- case OrbisAudioOutPort::Bgm:
187
- case OrbisAudioOutPort::Voice:
188
- state-> output = 1 ;
189
- state-> channel = port. format_info . num_channels > 2 ? 2 : port. format_info . num_channels ;
190
- break ;
191
- case OrbisAudioOutPort::Personal:
192
- case OrbisAudioOutPort::Padspk:
193
- state-> output = 4 ;
194
- state->channel = 1 ;
195
- break ;
196
- case OrbisAudioOutPort::Aux:
197
- state-> output = 0 ;
198
- state-> channel = 0 ;
199
- break ;
200
- default :
201
- UNREACHABLE () ;
176
+ auto & port = ports_out. at (handle - 1 );
177
+ {
178
+ std::unique_lock lock{port. mutex };
179
+ if (!port. IsOpen ()) {
180
+ return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
181
+ }
182
+ switch (port. type ) {
183
+ case OrbisAudioOutPort::Main:
184
+ case OrbisAudioOutPort::Bgm:
185
+ case OrbisAudioOutPort::Voice:
186
+ state-> output = 1 ;
187
+ state-> channel = port. format_info . num_channels > 2 ? 2 : port. format_info . num_channels ;
188
+ break ;
189
+ case OrbisAudioOutPort::Personal:
190
+ case OrbisAudioOutPort::Padspk:
191
+ state-> output = 4 ;
192
+ state-> channel = 1 ;
193
+ break ;
194
+ case OrbisAudioOutPort::Aux:
195
+ state->output = 0 ;
196
+ state-> channel = 0 ;
197
+ break ;
198
+ default :
199
+ UNREACHABLE () ;
200
+ }
201
+ state-> rerouteCounter = 0 ;
202
+ state-> volume = 127 ;
202
203
}
203
-
204
204
return ORBIS_OK;
205
205
}
206
206
@@ -279,15 +279,16 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
279
279
while (true ) {
280
280
timer.Start ();
281
281
{
282
- std::unique_lock lock{port->output_mutex };
283
- Common::CondvarWait (port->output_cv , lock, stop, [&] { return port->output_ready ; });
284
- if (stop. stop_requested ()) {
285
- break ;
282
+ std::unique_lock lock{port->mutex };
283
+ if (port->output_cv . wait ( lock, stop, [&] { return port->output_ready ; })) {
284
+ port-> impl -> Output (port-> output_buffer );
285
+ port-> output_ready = false ;
286
286
}
287
- port->impl ->Output (port->output_buffer );
288
- port->output_ready = false ;
289
287
}
290
288
port->output_cv .notify_one ();
289
+ if (stop.stop_requested ()) {
290
+ break ;
291
+ }
291
292
timer.End ();
292
293
}
293
294
}
@@ -332,27 +333,30 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
332
333
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
333
334
}
334
335
335
- std::scoped_lock lock{ports_mutex };
336
+ std::unique_lock open_lock{port_open_mutex };
336
337
const auto port =
337
338
std::ranges::find_if (ports_out, [&](const PortOut& p) { return p.impl == nullptr ; });
338
339
if (port == ports_out.end ()) {
339
340
LOG_ERROR (Lib_AudioOut, " Audio ports are full" );
340
341
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
341
342
}
342
343
343
- port->type = port_type;
344
- port->format_info = GetFormatInfo (format);
345
- port->sample_rate = sample_rate;
346
- port->buffer_frames = length;
347
- port->volume .fill (SCE_AUDIO_OUT_VOLUME_0DB);
344
+ {
345
+ std::unique_lock port_lock (port->mutex );
348
346
349
- port->impl = audio->Open (*port);
347
+ port->type = port_type;
348
+ port->format_info = GetFormatInfo (format);
349
+ port->sample_rate = sample_rate;
350
+ port->buffer_frames = length;
351
+ port->volume .fill (SCE_AUDIO_OUT_VOLUME_0DB);
350
352
351
- port->output_buffer = std::malloc (port->BufferSize ());
352
- port->output_ready = false ;
353
- port->output_thread .Run (
354
- [port](const std::stop_token& stop) { AudioOutputThread (&*port, stop); });
353
+ port->impl = audio->Open (*port);
355
354
355
+ port->output_buffer = std::malloc (port->BufferSize ());
356
+ port->output_ready = false ;
357
+ port->output_thread .Run (
358
+ [port](const std::stop_token& stop) { AudioOutputThread (&*port, stop); });
359
+ }
356
360
return std::distance (ports_out.begin (), port) + 1 ;
357
361
}
358
362
@@ -367,14 +371,13 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
367
371
}
368
372
369
373
auto & port = ports_out.at (handle - 1 );
370
- if (!port.impl ) {
371
- return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
372
- }
373
-
374
374
{
375
- std::unique_lock lock{port.output_mutex };
375
+ std::unique_lock lock{port.mutex };
376
+ if (!port.IsOpen ()) {
377
+ return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
378
+ }
376
379
port.output_cv .wait (lock, [&] { return !port.output_ready ; });
377
- if (ptr != nullptr ) {
380
+ if (ptr != nullptr && port. IsOpen () ) {
378
381
std::memcpy (port.output_buffer , ptr, port.BufferSize ());
379
382
port.output_ready = true ;
380
383
}
@@ -488,19 +491,19 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
488
491
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
489
492
}
490
493
491
- std::scoped_lock lock (ports_mutex);
492
494
auto & port = ports_out.at (handle - 1 );
493
- if (!port.impl ) {
494
- return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
495
- }
496
-
497
- for (int i = 0 ; i < port.format_info .num_channels ; i++, flag >>= 1u ) {
498
- if (flag & 0x1u ) {
499
- port.volume [i] = vol[i];
495
+ {
496
+ std::unique_lock lock{port.mutex };
497
+ if (!port.IsOpen ()) {
498
+ return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
500
499
}
500
+ for (int i = 0 ; i < port.format_info .num_channels ; i++, flag >>= 1u ) {
501
+ if (flag & 0x1u ) {
502
+ port.volume [i] = vol[i];
503
+ }
504
+ }
505
+ port.impl ->SetVolume (port.volume );
501
506
}
502
-
503
- port.impl ->SetVolume (port.volume );
504
507
return ORBIS_OK;
505
508
}
506
509
0 commit comments