mirror of
https://github.com/mihawk-99/PS5_RetroArch.git
synced 2026-10-18 23:09:29 -05:00
From mihawk-99/PS5_RetroArch#53 and #52 (Jorge Feitosa), renumbered (0131 to 0133 were taken), taken together: #52 fixes the read loop #53 turns on. - src/microphone_ps5.cpp (0134): a native AudioIn microphone driver, the default one, for the first controller's user. It captures only while a core has its microphone on, sends silence under the DualSense mute, and keeps PCM only in a bounded queue in memory. RetroArch is configured with the microphone; the title links a host stub for libSceAudioIn's imports. - 0135: a microphone read that gets no frames returns silence instead of looping forever (a paused core, an empty nonblocking device, a failed capture). - tools/build-melondsds.sh: melonDS DS at pinned revisions (git objects by SHA), software renderer and a native-memory JIT, staged only with PS5_WITH_MELONDSDS=1; DeSmuME stays the DS default. The DualSense touchpad drives its stylus as it drives DeSmuME's. Changed from the pull requests: the README and the 0134 entry describe what happens, not an opt-in the code never had. RetroArch's microphone is on by default, as upstream; melonDS DS listens only while its microphone button is held (its default), and Settings > Audio > Microphone turns it off for every core. tests/test_microphone_read.py applies the port's edits to vendor/retroarch itself rather than reading build/ra-conf, which a tree configured before 0135 would fail. The SwanStation and melonDS DS title changes are merged into one licence-file rule. Evidence: tests/test_microphone_ps5.py (the driver against a simulated AudioIn: lifecycle, mute, input failure); tests/test_microphone_read.py (it times out with upstream's loop); tests/test_touchpad_stylus.py (melonDS DS's relative stylus); tools/build-melondsds.sh, core ABI PASS (25 exports, kernel_web, sha256 b03d4c93...). A title built with both optional cores: 61 notice parts, check-notices covers every shipped executable, check-manifest PASS, 78 melonDS DS options in the catalog. Gates (default build): format, unit (204), build (staged cores reused), integration, evidence PASS. Console: with this series' deploy.
113 lines
4.1 KiB
C++
113 lines
4.1 KiB
C++
/* Exercise the actual native driver with a paced AudioIn substitute. */
|
|
#include <audio/microphone_driver.h>
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <thread>
|
|
|
|
extern "C" microphone_driver_t microphone_ps5;
|
|
static std::atomic<int> calls{0}, opens{0}, closes{0}, muted{0}, fail_read{0};
|
|
static bool reject_48k = false;
|
|
extern "C" int sceAudioInInit()
|
|
{
|
|
return 0;
|
|
}
|
|
extern "C" int ps5_input_user_id(unsigned port)
|
|
{
|
|
return port == 0 ? 123 : -1;
|
|
}
|
|
extern "C" int sceAudioInOpen(int user, int, int, unsigned grain, unsigned rate, unsigned format)
|
|
{
|
|
assert(user == 123 && grain == 256 && format == 0);
|
|
++opens;
|
|
return reject_48k && rate == 48000 ? -2 : 7;
|
|
}
|
|
extern "C" int sceAudioInInput(int handle, void *dest)
|
|
{
|
|
assert(handle == 7);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
|
int sequence = ++calls;
|
|
if (fail_read.load())
|
|
return -42;
|
|
std::fill_n(static_cast<int16_t *>(dest), 256, int16_t(sequence));
|
|
return 256;
|
|
}
|
|
extern "C" int sceAudioInGetSilentState(int)
|
|
{
|
|
return muted.load();
|
|
}
|
|
extern "C" int sceAudioInClose(int handle)
|
|
{
|
|
assert(handle == 7);
|
|
++closes;
|
|
return 0;
|
|
}
|
|
static void wait_calls(int target)
|
|
{
|
|
for (int i = 0; i < 1000 && calls.load() < target; ++i)
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
assert(calls.load() >= target);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
auto &api = microphone_ps5;
|
|
void *driver = api.init();
|
|
assert(driver && opens == 0);
|
|
unsigned rate = 0;
|
|
void *mic = api.open_mic(driver, "default", 47605, 64, &rate);
|
|
assert(mic && rate == 48000 && !api.mic_alive(driver, mic));
|
|
assert(!api.open_mic(driver, "default", 48000, 64, nullptr));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(8));
|
|
assert(calls == 0); // Opening the handle does not capture before core opt-in.
|
|
int16_t samples[256];
|
|
assert(api.read(driver, mic, samples, sizeof(samples)) == -1);
|
|
assert(api.start_mic(driver, mic));
|
|
assert(api.start_mic(driver, mic)); // An already-active start must be harmless.
|
|
wait_calls(24);
|
|
int before = calls.load();
|
|
assert(api.read(driver, mic, samples, sizeof(samples)) == sizeof(samples));
|
|
assert(samples[0] >= before - 18 &&
|
|
samples[0] > 1); // Drops old audio instead of growing without bound.
|
|
assert(std::all_of(samples, samples + 256, [](int16_t x) { return x > 0; }));
|
|
int16_t across_wrap[5000];
|
|
assert(api.read(driver, mic, across_wrap, sizeof(across_wrap)) == sizeof(across_wrap));
|
|
assert(across_wrap[0] > 0 && std::is_sorted(across_wrap, across_wrap + 5000));
|
|
muted = 4;
|
|
wait_calls(calls.load() + 4);
|
|
assert(api.read(driver, mic, samples, sizeof(samples)) == sizeof(samples));
|
|
assert(std::all_of(samples, samples + 256, [](int16_t x) { return x == 0; }));
|
|
assert(api.stop_mic(driver, mic));
|
|
int stopped = calls.load();
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(8));
|
|
assert(calls == stopped && !api.mic_alive(driver, mic));
|
|
assert(api.read(driver, mic, nullptr, 0) == 0);
|
|
assert(api.read(driver, mic, samples, 1) == -1);
|
|
api.close_mic(driver, mic);
|
|
assert(closes == 1);
|
|
|
|
// Unsupported 48k falls back to the independently device-tested 16k route.
|
|
reject_48k = true;
|
|
mic = api.open_mic(driver, "default", 47605, 64, &rate);
|
|
assert(mic && rate == 16000);
|
|
api.set_nonblock_state(driver, true);
|
|
assert(api.start_mic(driver, mic));
|
|
int got = api.read(driver, mic, samples, sizeof(samples));
|
|
assert(got >= 0 && got <= int(sizeof(samples)) && got % 2 == 0);
|
|
muted = 0;
|
|
fail_read = 1;
|
|
wait_calls(calls.load() + 1);
|
|
for (int i = 0; i < 100 && api.mic_alive(driver, mic); ++i)
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
assert(!api.mic_alive(driver, mic));
|
|
assert(api.read(driver, mic, samples, sizeof(samples)) == -1);
|
|
api.close_mic(driver, mic);
|
|
api.free(driver);
|
|
assert(closes == 2);
|
|
std::puts(
|
|
"microphone backend: opt-in, bounded queue, mute, stop, fallback and input failure PASS");
|
|
}
|