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