Skip to content

Commit f7e58d6

Browse files
committed
Revert "audio: Improve port state guards. (shadps4-emu#1998)"
This reverts commit 55b5017.
1 parent 53f8406 commit f7e58d6

File tree

3 files changed

+83
-103
lines changed

3 files changed

+83
-103
lines changed

src/core/libraries/audio/audioout.cpp

Lines changed: 73 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
#include <memory>
55
#include <mutex>
6-
#include <stop_token>
7-
#include <thread>
6+
#include <shared_mutex>
87
#include <magic_enum/magic_enum.hpp>
98

109
#include "common/assert.h"
1110
#include "common/config.h"
1211
#include "common/logging/log.h"
12+
#include "common/polyfill_thread.h"
1313
#include "common/thread.h"
1414
#include "core/libraries/audio/audioout.h"
1515
#include "core/libraries/audio/audioout_backend.h"
@@ -18,7 +18,7 @@
1818

1919
namespace Libraries::AudioOut {
2020

21-
std::mutex port_open_mutex{};
21+
std::shared_mutex ports_mutex;
2222
std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
2323

2424
static std::unique_ptr<AudioOutBackend> audio;
@@ -93,20 +93,17 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
9393
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
9494
}
9595

96-
std::unique_lock open_lock{port_open_mutex};
96+
std::scoped_lock lock(ports_mutex);
9797
auto& port = ports_out.at(handle - 1);
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;
98+
if (!port.impl) {
99+
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
107100
}
108-
// Stop outside of port lock scope to prevent deadlocks.
101+
109102
port.output_thread.Stop();
103+
std::free(port.output_buffer);
104+
port.output_buffer = nullptr;
105+
port.output_ready = false;
106+
port.impl = nullptr;
110107
return ORBIS_OK;
111108
}
112109

@@ -175,34 +172,35 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
175172
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
176173
}
177174

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;
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();
205202
}
203+
206204
return ORBIS_OK;
207205
}
208206

@@ -281,16 +279,15 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
281279
while (true) {
282280
timer.Start();
283281
{
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;
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;
288286
}
287+
port->impl->Output(port->output_buffer);
288+
port->output_ready = false;
289289
}
290290
port->output_cv.notify_one();
291-
if (stop.stop_requested()) {
292-
break;
293-
}
294291
timer.End();
295292
}
296293
}
@@ -335,30 +332,27 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
335332
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
336333
}
337334

338-
std::unique_lock open_lock{port_open_mutex};
335+
std::scoped_lock lock{ports_mutex};
339336
const auto port =
340-
std::ranges::find_if(ports_out, [&](const PortOut& p) { return !p.IsOpen(); });
337+
std::ranges::find_if(ports_out, [&](const PortOut& p) { return p.impl == nullptr; });
341338
if (port == ports_out.end()) {
342339
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
343340
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
344341
}
345342

346-
{
347-
std::unique_lock port_lock(port->mutex);
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);
348348

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);
349+
port->impl = audio->Open(*port);
354350

355-
port->impl = audio->Open(*port);
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); });
356355

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-
}
362356
return std::distance(ports_out.begin(), port) + 1;
363357
}
364358

@@ -373,13 +367,14 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
373367
}
374368

375369
auto& port = ports_out.at(handle - 1);
370+
if (!port.impl) {
371+
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
372+
}
373+
376374
{
377-
std::unique_lock lock{port.mutex};
378-
if (!port.IsOpen()) {
379-
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
380-
}
375+
std::unique_lock lock{port.output_mutex};
381376
port.output_cv.wait(lock, [&] { return !port.output_ready; });
382-
if (ptr != nullptr && port.IsOpen()) {
377+
if (ptr != nullptr) {
383378
std::memcpy(port.output_buffer, ptr, port.BufferSize());
384379
port.output_ready = true;
385380
}
@@ -493,19 +488,19 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
493488
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
494489
}
495490

491+
std::scoped_lock lock(ports_mutex);
496492
auto& port = ports_out.at(handle - 1);
497-
{
498-
std::unique_lock lock{port.mutex};
499-
if (!port.IsOpen()) {
500-
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
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-
}
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];
506500
}
507-
port.impl->SetVolume(port.volume);
508501
}
502+
503+
port.impl->SetVolume(port.volume);
509504
return ORBIS_OK;
510505
}
511506

src/core/libraries/audio/audioout.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33

44
#pragma once
55

6-
#include <condition_variable>
76
#include <memory>
8-
#include <mutex>
97

108
#include "common/bit_field.h"
119
#include "core/libraries/kernel/threads.h"
@@ -76,10 +74,10 @@ struct AudioFormatInfo {
7674
};
7775

7876
struct PortOut {
79-
std::mutex mutex;
8077
std::unique_ptr<PortBackend> impl{};
8178

8279
void* output_buffer;
80+
std::mutex output_mutex;
8381
std::condition_variable_any output_cv;
8482
bool output_ready;
8583
Kernel::Thread output_thread{};
@@ -90,10 +88,6 @@ struct PortOut {
9088
u32 buffer_frames;
9189
std::array<s32, 8> volume;
9290

93-
[[nodiscard]] bool IsOpen() const {
94-
return impl != nullptr;
95-
}
96-
9791
[[nodiscard]] u32 BufferSize() const {
9892
return buffer_frames * format_info.FrameSize();
9993
}

src/core/libraries/audio/sdl_audio.cpp

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Libraries::AudioOut {
1414
class SDLPortBackend : public PortBackend {
1515
public:
1616
explicit SDLPortBackend(const PortOut& port)
17-
: frame_size(port.format_info.FrameSize()), guest_buffer_size(port.BufferSize()) {
17+
: frame_size(port.format_info.FrameSize()), buffer_size(port.BufferSize()) {
1818
// We want the latency for delivering frames out to be as small as possible,
1919
// so set the sample frames hint to the number of frames per buffer.
2020
const auto samples_num_str = std::to_string(port.buffer_frames);
@@ -33,7 +33,7 @@ class SDLPortBackend : public PortBackend {
3333
LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError());
3434
return;
3535
}
36-
CalculateQueueThreshold();
36+
queue_threshold = CalculateQueueThreshold();
3737
if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(),
3838
port.format_info.num_channels)) {
3939
LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}",
@@ -71,9 +71,9 @@ class SDLPortBackend : public PortBackend {
7171
queue_threshold);
7272
SDL_ClearAudioStream(stream);
7373
// Recalculate the threshold in case this happened because of a device change.
74-
CalculateQueueThreshold();
74+
queue_threshold = CalculateQueueThreshold();
7575
}
76-
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(guest_buffer_size))) {
76+
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(buffer_size))) {
7777
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
7878
}
7979
}
@@ -91,7 +91,7 @@ class SDLPortBackend : public PortBackend {
9191
}
9292

9393
private:
94-
void CalculateQueueThreshold() {
94+
[[nodiscard]] u32 CalculateQueueThreshold() const {
9595
SDL_AudioSpec discard;
9696
int sdl_buffer_frames;
9797
if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard,
@@ -100,22 +100,13 @@ class SDLPortBackend : public PortBackend {
100100
SDL_GetError());
101101
sdl_buffer_frames = 0;
102102
}
103-
const auto sdl_buffer_size = sdl_buffer_frames * frame_size;
104-
const auto new_threshold = std::max(guest_buffer_size, sdl_buffer_size) * 4;
105-
if (host_buffer_size != sdl_buffer_size || queue_threshold != new_threshold) {
106-
host_buffer_size = sdl_buffer_size;
107-
queue_threshold = new_threshold;
108-
LOG_INFO(Lib_AudioOut,
109-
"SDL audio buffers: guest = {} bytes, host = {} bytes, threshold = {} bytes",
110-
guest_buffer_size, host_buffer_size, queue_threshold);
111-
}
103+
return std::max<u32>(buffer_size, sdl_buffer_frames * frame_size) * 4;
112104
}
113105

114106
u32 frame_size;
115-
u32 guest_buffer_size;
116-
u32 host_buffer_size{};
117-
u32 queue_threshold{};
118-
SDL_AudioStream* stream{};
107+
u32 buffer_size;
108+
u32 queue_threshold;
109+
SDL_AudioStream* stream;
119110
};
120111

121112
std::unique_ptr<PortBackend> SDLAudioOut::Open(PortOut& port) {

0 commit comments

Comments
 (0)