mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
Add launcher microphone selection and SDL AudioIn capture
This commit is contained in:
1 parent
98b5d88d67
commit
d1372abf46
9 files changed
+164
-24
No files matched your search
+100
-21
@@ -3,6 +3,7 @@
|
||||
#include "SDL.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/magicEnum.h"
|
||||
#include "common/stringUtils.h"
|
||||
@@ -38,6 +39,9 @@ constexpr int AUDIO_OUT_PORT_TYPE_AUX = 127;
|
||||
|
||||
constexpr uint32_t AUDIO_OUT_PARAM_FORMAT_MASK = 0x000000ffu;
|
||||
|
||||
constexpr int AUDIO_IN_SILENT_STATE_DEVICE_NONE = 0x1;
|
||||
constexpr uint32_t AUDIO_IN_GRAIN_MAX_ASYNC = 384;
|
||||
|
||||
static bool audio_out_port_type_is_valid(int type) {
|
||||
return (type >= AUDIO_OUT_PORT_TYPE_MAIN && type <= AUDIO_OUT_PORT_TYPE_PADSPK) ||
|
||||
type == AUDIO_OUT_PORT_TYPE_VIBRATION || type == AUDIO_OUT_PORT_TYPE_AUDIO3D ||
|
||||
@@ -91,7 +95,7 @@ public:
|
||||
|
||||
Id AudioInOpen(uint32_t samples_num, uint32_t freq, Format format, bool asynchronous);
|
||||
int AudioInClose(Id handle);
|
||||
bool AudioInValid(Id handle);
|
||||
int AudioInGetSilentState(Id handle);
|
||||
int AudioInInput(Id handle, void* dest);
|
||||
|
||||
static constexpr int OUT_PORTS_MAX = 32;
|
||||
@@ -113,13 +117,14 @@ private:
|
||||
};
|
||||
|
||||
struct PortIn {
|
||||
bool used = false;
|
||||
bool busy = false;
|
||||
bool asynchronous = false;
|
||||
uint32_t samples_num = 0;
|
||||
uint32_t freq = 0;
|
||||
uint32_t bytes_per_frame = 0;
|
||||
uint64_t last_input_time = 0;
|
||||
bool used = false;
|
||||
bool busy = false;
|
||||
bool asynchronous = false;
|
||||
uint32_t samples_num = 0;
|
||||
uint32_t freq = 0;
|
||||
uint32_t bytes_per_frame = 0;
|
||||
uint64_t last_input_time = 0;
|
||||
SDL_AudioDeviceID audio_device = 0;
|
||||
};
|
||||
|
||||
PortIn* GetAudioInPort(Id handle); // Caller holds m_mutex.
|
||||
@@ -135,6 +140,8 @@ private:
|
||||
static SDL_AudioFormat SdlFormat(Format format);
|
||||
static bool OpenSdlDevice(PortOut* port);
|
||||
static void CloseSdlDevice(PortOut* port);
|
||||
static void OpenSdlDevice(PortIn* port, Format format);
|
||||
static void CloseSdlDevice(PortIn* port);
|
||||
static const void* PrepareOutputBuffer(const PortOut& port, const void* data,
|
||||
std::vector<uint8_t>* buffer);
|
||||
static bool QueueSdlAudio(PortOut* port, const void* data, bool blocking);
|
||||
@@ -202,6 +209,9 @@ Audio::~Audio() {
|
||||
for (auto& port: m_out_ports) {
|
||||
CloseSdlDevice(&port);
|
||||
}
|
||||
for (auto& port: m_in_ports) {
|
||||
CloseSdlDevice(&port);
|
||||
}
|
||||
}
|
||||
|
||||
bool Audio::FormatIsFloat(Format format) {
|
||||
@@ -570,6 +580,38 @@ uint32_t Audio::AudioOutOutputs(OutputParam* params, uint32_t num, bool blocking
|
||||
return first_port.samples_num;
|
||||
}
|
||||
|
||||
void Audio::OpenSdlDevice(PortIn* port, Format format) {
|
||||
const auto& name = Config::GetAudioInputDevice();
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOGF("AudioIn: SDL init failed: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
SDL_AudioSpec desired {};
|
||||
desired.freq = static_cast<int>(port->freq);
|
||||
desired.format = SdlFormat(format);
|
||||
desired.channels = static_cast<Uint8>(port->bytes_per_frame / BytesPerSample(format));
|
||||
desired.samples = static_cast<Uint16>(port->samples_num);
|
||||
// SDL converts capture data to the guest format when allowed_changes is zero.
|
||||
port->audio_device = SDL_OpenAudioDevice(name.c_str(), 1, &desired, nullptr, 0);
|
||||
if (port->audio_device == 0) {
|
||||
LOGF("AudioIn: cannot open '%s': %s; using silence\n", name.c_str(), SDL_GetError());
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
return;
|
||||
}
|
||||
LOGF("AudioIn: opened '%s' (%u Hz, %u ch)\n", name.c_str(), port->freq, desired.channels);
|
||||
}
|
||||
|
||||
void Audio::CloseSdlDevice(PortIn* port) {
|
||||
if (port->audio_device != 0) {
|
||||
SDL_CloseAudioDevice(port->audio_device);
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
port->audio_device = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Audio::Id Audio::AudioInOpen(uint32_t samples_num, uint32_t freq, Format format, bool asynchronous) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
@@ -586,6 +628,7 @@ Audio::Id Audio::AudioInOpen(uint32_t samples_num, uint32_t freq, Format format,
|
||||
if (format == Format::Signed16bitStereo || format == Format::FloatStereo) {
|
||||
port.bytes_per_frame *= 2;
|
||||
}
|
||||
OpenSdlDevice(&port, format);
|
||||
|
||||
return Id::Create(id);
|
||||
}
|
||||
@@ -611,20 +654,29 @@ int Audio::AudioInClose(Id handle) {
|
||||
if (port->busy) {
|
||||
return AUDIO_IN_ERROR_BUSY;
|
||||
}
|
||||
CloseSdlDevice(port);
|
||||
*port = {};
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool Audio::AudioInValid(Id handle) {
|
||||
int Audio::AudioInGetSilentState(Id handle) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
return GetAudioInPort(handle) != nullptr;
|
||||
auto* port = GetAudioInPort(handle);
|
||||
if (port == nullptr) {
|
||||
return AUDIO_IN_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
if (port->audio_device == 0 ||
|
||||
SDL_GetAudioDeviceStatus(port->audio_device) == SDL_AUDIO_STOPPED) {
|
||||
return AUDIO_IN_SILENT_STATE_DEVICE_NONE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Audio::AudioInInput(Id handle, void* dest) {
|
||||
PortIn snapshot;
|
||||
{
|
||||
Common::LockGuard lock(m_mutex);
|
||||
auto* port = GetAudioInPort(handle);
|
||||
auto* port = GetAudioInPort(handle);
|
||||
if (port == nullptr) {
|
||||
return AUDIO_IN_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -634,6 +686,9 @@ int Audio::AudioInInput(Id handle, void* dest) {
|
||||
port->busy = true;
|
||||
snapshot = *port;
|
||||
}
|
||||
if (snapshot.audio_device != 0 && dest != nullptr) {
|
||||
SDL_PauseAudioDevice(snapshot.audio_device, 0);
|
||||
}
|
||||
|
||||
const uint64_t block_time = (1000000ULL * snapshot.samples_num) / snapshot.freq;
|
||||
uint64_t wait_time = 0;
|
||||
@@ -641,7 +696,7 @@ int Audio::AudioInInput(Id handle, void* dest) {
|
||||
if (dest != nullptr) {
|
||||
wait_time = block_time;
|
||||
}
|
||||
} else if (snapshot.last_input_time != 0) {
|
||||
} else if (snapshot.last_input_time != 0 && (snapshot.audio_device == 0 || dest == nullptr)) {
|
||||
const auto now = LibKernel::KernelGetProcessTime();
|
||||
const auto next = snapshot.last_input_time + block_time;
|
||||
if (next > now) {
|
||||
@@ -649,17 +704,44 @@ int Audio::AudioInInput(Id handle, void* dest) {
|
||||
}
|
||||
}
|
||||
Common::Thread::SleepMicro(wait_time);
|
||||
uint32_t frames = snapshot.samples_num;
|
||||
bool failed = false;
|
||||
if (dest != nullptr) {
|
||||
// No input device backend: return silence matching AUDIO_IN_SILENT_STATE_DEVICE_NONE.
|
||||
std::memset(dest, 0, snapshot.samples_num * snapshot.bytes_per_frame);
|
||||
if (snapshot.audio_device != 0) {
|
||||
while (!snapshot.asynchronous &&
|
||||
SDL_GetQueuedAudioSize(snapshot.audio_device) <
|
||||
frames * snapshot.bytes_per_frame &&
|
||||
SDL_GetAudioDeviceStatus(snapshot.audio_device) == SDL_AUDIO_PLAYING) {
|
||||
Common::Thread::SleepMicro(1000);
|
||||
}
|
||||
failed = SDL_GetAudioDeviceStatus(snapshot.audio_device) != SDL_AUDIO_PLAYING;
|
||||
if (!failed) {
|
||||
if (snapshot.asynchronous) {
|
||||
frames = AUDIO_IN_GRAIN_MAX_ASYNC;
|
||||
}
|
||||
frames = SDL_DequeueAudio(snapshot.audio_device, dest,
|
||||
frames * snapshot.bytes_per_frame) /
|
||||
snapshot.bytes_per_frame;
|
||||
}
|
||||
}
|
||||
if (snapshot.audio_device == 0 || failed) {
|
||||
std::memset(dest, 0, frames * snapshot.bytes_per_frame);
|
||||
}
|
||||
} else if (snapshot.audio_device != 0) {
|
||||
SDL_PauseAudioDevice(snapshot.audio_device, 1);
|
||||
SDL_ClearQueuedAudio(snapshot.audio_device);
|
||||
}
|
||||
{
|
||||
Common::LockGuard lock(m_mutex);
|
||||
auto& port = m_in_ports[handle.GetId()];
|
||||
auto& port = m_in_ports[handle.GetId()];
|
||||
if (failed) {
|
||||
LOGF("AudioIn: capture stopped; using silence\n");
|
||||
CloseSdlDevice(&port);
|
||||
}
|
||||
port.last_input_time = dest != nullptr ? LibKernel::KernelGetProcessTime() : 0;
|
||||
port.busy = false;
|
||||
}
|
||||
return dest != nullptr ? static_cast<int>(snapshot.samples_num) : 0;
|
||||
return dest != nullptr ? static_cast<int>(frames) : 0;
|
||||
}
|
||||
|
||||
namespace AudioOut {
|
||||
@@ -851,8 +933,6 @@ namespace AudioIn {
|
||||
|
||||
LIB_NAME("AudioIn", "AudioIn");
|
||||
|
||||
constexpr int AUDIO_IN_SILENT_STATE_DEVICE_NONE = 0x1;
|
||||
|
||||
static int OpenPort(int user_id, int type, int index, uint32_t len, uint32_t freq,
|
||||
uint32_t param, bool asynchronous) {
|
||||
LOGF("\t user_id = %d\n"
|
||||
@@ -935,12 +1015,11 @@ int KYTY_SYSV_ABI AudioInGetSilentState(int handle) {
|
||||
|
||||
EXIT_IF(g_audio == nullptr);
|
||||
|
||||
if (handle <= 0 || !g_audio->AudioInValid(Audio::Id(handle))) {
|
||||
if (handle <= 0) {
|
||||
return AUDIO_IN_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// Audio input has no device backend yet, so every valid port receives silence.
|
||||
return AUDIO_IN_SILENT_STATE_DEVICE_NONE;
|
||||
return g_audio->AudioInGetSilentState(Audio::Id(handle));
|
||||
}
|
||||
|
||||
} // namespace AudioIn
|
||||
|
||||
Reference in new issue
Block a user