Files
NX2navi/runtime/ov_audio_input.c
2026-11-09 08:19:19 -05:00

181 lines
6.2 KiB
C

/* Polling audio input for the public nn::audio calls Ocarina imports.
SDL converts the default recording device to the format the caller requests. */
#include "audio_capture.h"
#include "host.h"
#include "ipc.h"
#include "platform/platform.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
#define BAD_INPUT 0x70899u
typedef struct {
uint64_t key;
MicCapture *mic;
int format[3], bpf, running;
uint64_t frames_read, next_log;
} Input;
static Input inputs[8];
static const char input_name[] = "WindowsDefaultMicrophone";
static Input *find(uint64_t key) {
for (int i = 0; i < 8; i++)
if (inputs[i].key == key && key)
return &inputs[i];
return NULL;
}
static void name_out(uint64_t addr) {
char *p = ipc_guest(addr, 256);
memset(p, 0, 256);
memcpy(p, input_name, sizeof input_name);
}
static void list_devices(Cpu *c) {
int n = mic_device_count();
if ((int)c->x[1] > 0 && n > 0)
name_out(c->x[0]);
rt_log("mic: input enumeration: %d Windows recording devices", n);
ret_x(c, (int)c->x[1] > 0 && n > 0 ? 1 : 0);
}
static void default_device(Cpu *c) {
name_out(c->x[0]);
ret_x(c, 0);
}
static void close_input(Cpu *c) {
Input *s = find(c->x[0]);
if (s) {
rt_log("mic: close, %llu real audio frames delivered", (unsigned long long)s->frames_read);
mic_close(s->mic);
memset(s, 0, sizeof *s);
}
if (c->x[0]) {
uint64_t zero = 0;
memcpy(ipc_guest(c->x[0], 8), &zero, 8);
}
ret_x(c, 0);
}
static void open_input(Cpu *c) {
uint64_t key = c->x[0];
int f[3];
memcpy(f, ipc_guest(c->x[4], 12), 12);
int frames = (int)c->x[5];
rt_log("mic: OpenInputStream format=%d channels=%d rate=%d capacity=%d", f[0], f[1], f[2], frames);
Input *s = find(key);
if (s) {
mic_close(s->mic);
memset(s, 0, sizeof *s);
}
for (int i = 0; !s && i < 8; i++)
if (!inputs[i].key)
s = &inputs[i];
uint64_t zero = 0;
memcpy(ipc_guest(key, 8), &zero, 8);
if (!s) {
ret_x(c, BAD_INPUT);
return;
}
MicCapture *m = mic_open(f[0], f[1], f[2], frames);
if (!m) {
rt_log("mic: cannot open Windows default input: %s", mic_error());
ret_x(c, BAD_INPUT);
return;
}
s->key = key;
s->mic = m;
memcpy(s->format, f, 12);
s->bpf = f[1] * (f[0] == 1 ? 1 : f[0] == 2 ? 2 : 4);
s->next_log = (uint64_t)f[2] * 5;
uint64_t impl = key + 8;
memcpy(ipc_guest(key, 8), &impl, 8);
rt_log("mic: Windows default input opened; bounded buffer %d frames", mic_capacity(m));
ret_x(c, 0);
}
static void start_input(Cpu *c) {
Input *s = find(c->x[0]);
if (!s || !mic_start(s->mic)) {
rt_log("mic: start failed: %s", mic_error());
ret_x(c, BAD_INPUT);
return;
}
s->running = 1;
rt_log("mic: capture started");
ret_x(c, 0);
}
static void stop_input(Cpu *c) {
Input *s = find(c->x[0]);
if (s) {
mic_stop(s->mic);
s->running = 0;
}
ret_x(c, 0);
}
static void purge_input(Cpu *c) {
Input *s = find(c->x[0]);
if (s)
mic_purge(s->mic);
ret_x(c, 0);
}
static void filled(Cpu *c) {
Input *s = find(c->x[0]);
ret_x(c, s ? mic_available(s->mic) : 0);
}
static void state(Cpu *c) {
Input *s = find(c->x[0]);
ret_x(c, s ? (s->running ? 2 : 1) : 0);
}
static void read_input(Cpu *c) {
Input *s = find(c->x[0]);
int frames = (int)c->x[2];
if (!s || frames <= 0) {
ret_x(c, 0);
return;
}
int available = mic_available(s->mic);
if (frames > available)
frames = available;
if (!frames) {
ret_x(c, 0);
return;
}
int n = mic_read(s->mic, ipc_guest(c->x[1], (uint64_t)frames * s->bpf), frames);
s->frames_read += n;
if (s->frames_read >= s->next_log) {
rt_log("mic: delivered %llu real frames (%d Hz)", (unsigned long long)s->frames_read, s->format[2]);
s->next_log = s->frames_read + (uint64_t)s->format[2] * 30;
}
ret_x(c, n);
}
static void statistics(Cpu *c) { memset(ipc_guest(c->x[8], 24), 0, 24); }
static PlatMutex input_lock = PLAT_MUTEX_INIT;
#define LOCKED_HANDLER(fn) \
static void fn##_locked(Cpu *c) { \
plat_mutex_lock(&input_lock); \
fn(c); \
plat_mutex_unlock(&input_lock); \
}
LOCKED_HANDLER(list_devices)
LOCKED_HANDLER(default_device)
LOCKED_HANDLER(open_input)
LOCKED_HANDLER(close_input)
LOCKED_HANDLER(start_input)
LOCKED_HANDLER(stop_input)
LOCKED_HANDLER(purge_input)
LOCKED_HANDLER(filled)
LOCKED_HANDLER(state)
LOCKED_HANDLER(read_input)
LOCKED_HANDLER(statistics)
HOST_OVERRIDE("_ZN2nn5audio23ListVirtualInputDevicesEPNS0_15AudioDeviceNameEi", list_devices_locked);
HOST_OVERRIDE("_ZN2nn5audio28GetDefaultVirtualInputDeviceEPNS0_15AudioDeviceNameE", default_device_locked);
HOST_OVERRIDE("_ZN2nn5audio26GetTrueRawAudioInputDeviceEPNS0_15AudioDeviceNameE", default_device_locked);
HOST_OVERRIDE("_ZN2nn5audio15OpenInputStreamEPNS0_15InputStreamTypeEPvmRKNS0_15AudioDeviceNameERKNS0_12StreamFormatEi",
open_input_locked);
HOST_OVERRIDE("_ZN2nn5audio16CloseInputStreamEPNS0_15InputStreamTypeE", close_input_locked);
HOST_OVERRIDE("_ZN2nn5audio16StartInputStreamEPNS0_15InputStreamTypeE", start_input_locked);
HOST_OVERRIDE("_ZN2nn5audio15StopInputStreamEPNS0_15InputStreamTypeE", stop_input_locked);
HOST_OVERRIDE("_ZN2nn5audio16PurgeInputStreamEPNS0_15InputStreamTypeE", purge_input_locked);
HOST_OVERRIDE("_ZN2nn5audio30GetInputStreamFilledFrameCountEPKNS0_15InputStreamTypeE", filled_locked);
HOST_OVERRIDE("_ZN2nn5audio19GetInputStreamStateEPKNS0_15InputStreamTypeE", state_locked);
HOST_OVERRIDE("_ZN2nn5audio15ReadInputStreamEPNS0_15InputStreamTypeEPvi", read_input_locked);
HOST_OVERRIDE("_ZN2nn5audio24GetInputStreamStatisticsEPNS0_15InputStreamTypeEb", statistics_locked);