Audio device reconnection handler

This commit is contained in:
igor725 2024-05-01 00:24:00 +03:00
parent 7df783272d
commit 8feac0644a
No known key found for this signature in database
GPG Key ID: 46F13BBE46F8569D
3 changed files with 152 additions and 4 deletions

View File

@ -163,6 +163,51 @@ std::string getTitle(int handle, uint64_t frame, size_t fps, FlipRate maxFPS) {
} // namespace
class EventSDL {
struct SDLEvent {
uint32_t type;
SDLEventFunc func;
void* userData;
};
std::vector<SDLEvent> m_events = {};
public:
void addListener(uint32_t type, SDLEventFunc func, void* userData) { m_events.push_back({type, func, userData}); }
bool removeListener(uint32_t type, SDLEventFunc func) {
for (auto it = m_events.begin(); it != m_events.end(); ++it) {
if (it->type == type && it->func == func) {
m_events.erase(it);
return true;
}
}
return false;
}
bool removeListener(SDLEventFunc func) {
bool removed = false;
for (auto it = m_events.begin(); it != m_events.end();) {
if (it->func == func) {
it = m_events.erase(it);
continue;
}
++it;
}
return removed;
}
void call(SDL_Event* event) {
for (auto& handler: m_events) {
if (event->type == handler.type) handler.func(event, handler.userData);
}
}
};
class VideoOut: public IVideoOut, private IEventsGraphics {
std::array<Context, WindowsMAX> m_windows;
@ -184,8 +229,16 @@ class VideoOut: public IVideoOut, private IEventsGraphics {
std::queue<Message> m_messages;
EventSDL m_sdlEvents;
uint64_t m_vblankTime = (uint64_t)(1e6 / 59.0); // in us
void SDLEventReg(uint32_t type, SDLEventFunc func, void* userData) { m_sdlEvents.addListener(type, func, userData); }
bool SDLEventUnreg(uint32_t type, SDLEventFunc func) { return m_sdlEvents.removeListener(type, func); }
bool SDLEventUnreg(SDLEventFunc func) { return m_sdlEvents.removeListener(func); }
void vblankEnd(int handle, uint64_t curTime, uint64_t curProcTime);
// Callback Graphics
@ -782,7 +835,7 @@ std::thread VideoOut::createSDLThread() {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
// SDL polling helper
auto func_pollSDL = [](auto& window) {
auto func_pollSDL = [this](auto& window) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
@ -800,6 +853,8 @@ std::thread VideoOut::createSDLThread() {
default: break;
}
m_sdlEvents.call(&event);
}
};
// -

View File

@ -7,12 +7,15 @@
namespace vulkan {
struct SwapchainData;
}
} // namespace vulkan
constexpr int VIDEO_OUT_EVENT_FLIP = 0;
constexpr int VIDEO_OUT_EVENT_VBLANK = 1;
class IGraphics;
union SDL_Event;
typedef void (*SDLEventFunc)(SDL_Event*, void*);
class IVideoOut {
CLASS_NO_COPY(IVideoOut);
@ -154,6 +157,32 @@ class IVideoOut {
*/
virtual int SDLInit(uint32_t flags) = 0;
/**
* @brief Register SDL event listener
*
* @param type
* @param eventFunc
* @return int
*/
virtual void SDLEventReg(uint32_t type, SDLEventFunc eventFunc, void* userData) = 0;
/**
* @brief Unrgister SDL event listener by type and function pointer
*
* @param type
* @param eventFunc
* @return int
*/
virtual bool SDLEventUnreg(uint32_t type, SDLEventFunc eventFunc) = 0;
/**
* @brief Unrgister all the SDL event listeners by function pointer
*
* @param eventFunc
* @return int
*/
virtual bool SDLEventUnreg(SDLEventFunc eventFunc) = 0;
/**
* @brief Notify a gpu visible memory range
*

View File

@ -6,6 +6,7 @@
#include <SDL2/SDL.h>
#include <array>
#include <chrono>
#include <core/videoout/videoout.h>
#include <mutex>
LOG_DEFINE_MODULE(libSceAudioOut);
@ -28,6 +29,7 @@ struct PortOut {
SDL_AudioFormat sdlFormat = AUDIO_F32;
float volumeModifier = 0.5f;
std::vector<uint8_t> mixedAudio;
std::string deviceName;
};
class PortsOut {
@ -39,6 +41,65 @@ class PortsOut {
static inline uint8_t rangeEnd(uint16_t range) { return range & 0xFF; }
public:
PortsOut() {
auto handler = [](SDL_Event* event, void* ud) {
if (event->adevice.iscapture) return; // Ignoring capture device events
PortsOut* self = (PortsOut*)ud;
switch (event->type) {
case SDL_AUDIODEVICEADDED: self->AlertConnect(event->adevice.which); break;
case SDL_AUDIODEVICEREMOVED: self->AlertDisconnect(event->adevice.which); break;
}
};
accessVideoOut().SDLEventReg(SDL_AUDIODEVICEADDED, handler, this);
accessVideoOut().SDLEventReg(SDL_AUDIODEVICEREMOVED, handler, this);
}
void AlertConnect(int devIndex) {
LOG_USE_MODULE(libSceAudioOut);
auto devName = SDL_GetAudioDeviceName(devIndex, 0);
for (int i = 0; i < m_ports.size(); i++) {
auto& port = m_ports[i];
if (!port.open || port.device != 0 || port.deviceName.length() == 0) continue;
/**
* SDL appends to reconnected device's name "(<number>)" string.
* So we have to politely ignore it.
*/
if (port.deviceName.compare(0, std::string::npos, devName, 0, port.deviceName.length()) != 0) continue;
SDL_AudioSpec fmt {
.freq = static_cast<int>(port.freq),
.format = port.sdlFormat,
.channels = static_cast<uint8_t>(port.channelsNum),
.samples = static_cast<uint16_t>(port.samplesNum),
.callback = nullptr,
.userdata = nullptr,
};
if ((port.device = SDL_OpenAudioDevice(devName, 0, &fmt, NULL, 0)) == 0) {
LOG_ERR(L"Failed to reopen %S audio device: %S", devName, SDL_GetError());
return;
}
LOG_INFO(L"Welcome back, %S!", devName);
return;
}
}
void AlertDisconnect(int devIndex) {
LOG_USE_MODULE(libSceAudioOut);
for (int i = 0; i < m_ports.size(); i++) {
auto& port = m_ports[i];
if (!port.open || (port.device != devIndex)) continue;
SDL_CloseAudioDevice(port.device);
port.device = 0;
LOG_ERR(L"Oh no! %S got disconnected", port.deviceName.c_str());
return;
}
}
PortOut* GetPort(int handle) {
if (handle < 1 || handle > m_ports.size()) return nullptr;
auto port = &m_ports[handle - 1];
@ -90,7 +151,8 @@ struct Pimpl {
boost::mutex mutexInt;
PortsOut portsOut;
Pimpl() = default;
Pimpl(): portsOut() {}
};
Pimpl* getData() {
@ -267,8 +329,8 @@ EXPORT SYSV_ABI int32_t sceAudioOutOpen(int32_t userId, SceAudioOutPortType type
port->volume[i] = AudioOut::VOLUME_0DB;
}
} else {
SDL_AudioSpec fmt_curr;
if ((*jData)["device"] == "[default]") {
SDL_AudioSpec fmt_curr;
SDL_GetDefaultAudioInfo((char**)&dname, &fmt_curr, 0);
} else if ((*jData)["device"] == "[null]") {
LOG_INFO(L"%S audio output device is nulled!", getDevName(type));
@ -281,6 +343,7 @@ EXPORT SYSV_ABI int32_t sceAudioOutOpen(int32_t userId, SceAudioOutPortType type
} catch (const json::exception& e) {
LOG_ERR(L"Invalid audio device name: %S", e.what());
dname = NULL;
SDL_GetDefaultAudioInfo((char**)&dname, &fmt_curr, 0);
}
}
@ -296,6 +359,7 @@ EXPORT SYSV_ABI int32_t sceAudioOutOpen(int32_t userId, SceAudioOutPortType type
LOG_INFO(L"%S audio device %S opened for user #%d", getDevName(type), dname, userId);
port->mixedAudio.resize(port->sampleSize * port->samplesNum * port->channelsNum);
port->deviceName.assign(dname);
return handle;
}