Merge pull request #2286 from beebono/ds-features

Add hardware microphone support to drastic-sa and fix melonds-sa DirectBoot priority
This commit is contained in:
sydarn
2026-02-05 23:10:41 +01:00
committed by GitHub
8 changed files with 145 additions and 14 deletions

View File

@@ -60,7 +60,7 @@ controls_a[CONTROL_INDEX_SWAP_ORIENTATION_A] = 65535
controls_a[CONTROL_INDEX_SWAP_ORIENTATION_B] = 65535
controls_a[CONTROL_INDEX_LOAD_GAME] = 65535
controls_a[CONTROL_INDEX_QUIT] = 65535
controls_a[CONTROL_INDEX_FAKE_MICROPHONE] = 65535
controls_a[CONTROL_INDEX_FAKE_MICROPHONE] = 327
controls_a[CONTROL_INDEX_UI_UP] = 1217
controls_a[CONTROL_INDEX_UI_DOWN] = 1153
controls_a[CONTROL_INDEX_UI_LEFT] = 1216
@@ -98,7 +98,7 @@ controls_b[CONTROL_INDEX_SWAP_ORIENTATION_A] = 65535
controls_b[CONTROL_INDEX_SWAP_ORIENTATION_B] = 65535
controls_b[CONTROL_INDEX_LOAD_GAME] = 65535
controls_b[CONTROL_INDEX_QUIT] = 65535
controls_b[CONTROL_INDEX_FAKE_MICROPHONE] = 65535
controls_b[CONTROL_INDEX_FAKE_MICROPHONE] = 327
controls_b[CONTROL_INDEX_UI_UP] = 1037
controls_b[CONTROL_INDEX_UI_DOWN] = 1038
controls_b[CONTROL_INDEX_UI_LEFT] = 1039

View File

@@ -31,6 +31,9 @@ makeinstall_target() {
cp -rf ${PKG_BUILD}/drastic_aarch64/* ${INSTALL}/usr/config/drastic/
cp -rf ${PKG_DIR}/config/${DEVICE}/* ${INSTALL}/usr/config/drastic/config/
cp -rf ${PKG_DIR}/config/drastic.gptk ${INSTALL}/usr/config/drastic/
mkdir -p ${INSTALL}/usr/config/drastic/microphone
cp -f ${PKG_DIR}/sources/microphone.wav ${INSTALL}/usr/config/drastic/microphone/
}
post_install() {

View File

@@ -16,6 +16,7 @@ PLATFORM="nds"
HIRES3D=$(get_setting hires_3d "${PLATFORM}" "${GAME}")
THREADED3D=$(get_setting threaded_3d "${PLATFORM}" "${GAME}")
FOLLOW3D=$(get_setting follow_3d_renderer "${PLATFORM}" "${GAME}")
MICTHRESH=$(get_setting microphone_sensitivity "${PLATFORM}" "${GAME}")
#load gptokeyb support files
control-gen_init.sh
@@ -92,12 +93,9 @@ if [ "${HW_DEVICE}" = "S922X" ]; then
fi
$GPTOKEYB "drastic" -c "drastic.gptk" &
# Fix actual touch inputs by replacing touch->mouse translation
# Fix actual touch inputs by replacing touch->mouse translation and add hw mic support
export LD_PRELOAD="/usr/lib/libdrastouch.so"
export SDL_TOUCH_MOUSE_EVENTS="0"
export DSHOOK_MIC_THRESH="${MICTHRESH}"
./drastic "$1"
kill -9 $(pidof gptokeyb)
if echo "${UI_SERVICE}" | grep "sway"; then
kill -9 $(pidof drastic_sense.sh)
fi

View File

@@ -2,6 +2,9 @@
#include <dlfcn.h>
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
static int ds_screen_width = 256;
static int ds_screen_height = 192;
@@ -14,6 +17,14 @@ static int logical_width = -1;
static int logical_height = -1;
static int actual_touch = 0;
// Microphone monitoring
static SDL_AudioDeviceID mic_device = 0;
static int mic_key_held = 0;
static float mic_threshold = 0.0f;
static int mic_enabled = 0;
static float mic_baseline = 0.0f;
static int mic_baseline_samples = 0;
static SDL_Texture* screens[4];
static SDL_Texture* stylus_tex[2];
static SDL_Rect touch_rect_storage = {0};
@@ -26,6 +37,14 @@ static int (*real_SDL_RenderSetLogicalSize)(SDL_Renderer*, int, int) = NULL;
static SDL_Texture* (*real_SDL_CreateTexture)(SDL_Renderer*, Uint32, int, int, int) = NULL;
static int (*real_SDL_RenderCopy)(SDL_Renderer*, SDL_Texture*, const SDL_Rect*, const SDL_Rect*) = NULL;
static int (*real_SDL_PollEvent)(SDL_Event*) = NULL;
static int (*real_SDL_PushEvent)(SDL_Event*) = NULL;
static Uint32 (*real_SDL_WasInit)(Uint32) = NULL;
static int (*real_SDL_InitSubSystem)(Uint32) = NULL;
static SDL_AudioDeviceID (*real_SDL_OpenAudioDevice)(const char*, int, const SDL_AudioSpec*, SDL_AudioSpec*, int) = NULL;
static void (*real_SDL_PauseAudioDevice)(SDL_AudioDeviceID, int) = NULL;
static void (*real_SDL_CloseAudioDevice)(SDL_AudioDeviceID) = NULL;
static int (*real_SDL_GetNumAudioDevices)(int) = NULL;
static const char* (*real_SDL_GetAudioDeviceName)(int, int) = NULL;
SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int w, int h, Uint32 flags) {
int num_displays = SDL_GetNumVideoDisplays();
@@ -156,6 +175,53 @@ int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
return real_SDL_RenderCopy(renderer, texture, srcrect, dstrect);
}
void mic_audio_callback(void* userdata, Uint8* stream, int len) {
if (!mic_enabled) return;
Sint16* samples = (Sint16*)stream;
int sample_count = len / 2;
// Calculate RMS amplitude
float sum = 0.0f;
for (int i = 0; i < sample_count; i++) {
float sample = samples[i] / 32768.0f; // Normalize to [-1.0..1.0]
sum += sample * sample;
}
float rms = sqrtf(sum / sample_count);
// Build ambient baseline over first ~3 seconds
if (mic_baseline_samples < 60) {
mic_baseline = (mic_baseline * mic_baseline_samples + rms) / (mic_baseline_samples + 1);
mic_baseline_samples++;
return; // Don't trigger during calibration
}
mic_baseline = mic_baseline * 0.999f + rms * 0.001f;
// Trigger only if significantly above baseline
float trigger_level = mic_baseline + mic_threshold;
int should_hold = (rms > trigger_level);
if (should_hold && !mic_key_held) {
// Noise detected, press Scroll Lock
SDL_Event key_event = {0};
key_event.type = SDL_KEYDOWN;
key_event.key.state = SDL_PRESSED;
key_event.key.keysym.scancode = SDL_SCANCODE_SCROLLLOCK;
key_event.key.keysym.sym = SDLK_SCROLLLOCK;
real_SDL_PushEvent(&key_event);
mic_key_held = 1;
} else if (!should_hold && mic_key_held) {
// Noise dropped below threshold, release Scroll Lock
SDL_Event key_event = {0};
key_event.type = SDL_KEYUP;
key_event.key.state = SDL_RELEASED;
key_event.key.keysym.scancode = SDL_SCANCODE_SCROLLLOCK;
key_event.key.keysym.sym = SDLK_SCROLLLOCK;
real_SDL_PushEvent(&key_event);
mic_key_held = 0;
}
}
int SDL_PollEvent(SDL_Event* event) {
// Loop required to filter events we don't want to pass along
while (1) {
@@ -188,7 +254,7 @@ int SDL_PollEvent(SDL_Event* event) {
event->button.state = SDL_PRESSED;
event->button.x = x;
event->button.y = y;
SDL_PushEvent(event);
real_SDL_PushEvent(event);
// Jump to new position
event->type = SDL_MOUSEMOTION;
@@ -253,6 +319,60 @@ static void init(void) {
real_SDL_CreateTexture = dlsym(RTLD_NEXT, "SDL_CreateTexture");
real_SDL_RenderCopy = dlsym(RTLD_NEXT, "SDL_RenderCopy");
real_SDL_PollEvent = dlsym(RTLD_NEXT, "SDL_PollEvent");
real_SDL_PushEvent = dlsym(RTLD_NEXT, "SDL_PushEvent");
real_SDL_WasInit = dlsym(RTLD_NEXT, "SDL_WasInit");
real_SDL_InitSubSystem = dlsym(RTLD_NEXT, "SDL_InitSubSystem");
real_SDL_OpenAudioDevice = dlsym(RTLD_NEXT, "SDL_OpenAudioDevice");
real_SDL_PauseAudioDevice = dlsym(RTLD_NEXT, "SDL_PauseAudioDevice");
real_SDL_CloseAudioDevice = dlsym(RTLD_NEXT, "SDL_CloseAudioDevice");
real_SDL_GetNumAudioDevices = dlsym(RTLD_NEXT, "SDL_GetNumAudioDevices");
real_SDL_GetAudioDeviceName = dlsym(RTLD_NEXT, "SDL_GetAudioDeviceName");
const char* threshold_str = getenv("DSHOOK_MIC_THRESH");
if (threshold_str) {
mic_threshold = atof(threshold_str);
if (mic_threshold > 0.0f) {
mic_enabled = 1;
// Make sure SDL audio is ready, just in case
if (real_SDL_WasInit(SDL_INIT_AUDIO) == 0)
real_SDL_InitSubSystem(SDL_INIT_AUDIO);
int num_devices = real_SDL_GetNumAudioDevices(1);
const char* device_name = NULL;
for (int i = 0; i < num_devices; i++) {
const char* name = real_SDL_GetAudioDeviceName(i, 1);
// Use first available device (or look for Built-in)
if (name && (i == 0 || strstr(name, "Built-in"))) {
device_name = name;
break;
}
}
// Configure audio capture
SDL_AudioSpec desired_spec = {0};
desired_spec.freq = 44100;
desired_spec.format = AUDIO_S16SYS;
desired_spec.channels = 1;
desired_spec.samples = 2048;
desired_spec.callback = mic_audio_callback;
SDL_AudioSpec obtained_spec;
mic_device = real_SDL_OpenAudioDevice(device_name, 1, &desired_spec, &obtained_spec, 0);
if (mic_device > 0) // Opened, start capture
real_SDL_PauseAudioDevice(mic_device, 0);
else // Couldn't open, fallback to disable
mic_enabled = 0;
}
}
}
__attribute__((destructor))
static void cleanup(void) {
if (mic_device > 0 && real_SDL_CloseAudioDevice) {
real_SDL_CloseAudioDevice(mic_device);
}
}
// Major thanks/credit to Shaun Inman for providing the basis of this hook library!

View File

@@ -68,12 +68,12 @@ fi
if [ "$PLATFORM" = "ndsiware" ]; then
sed -i '/^DirectBoot=/c\DirectBoot=0' /storage/.config/melonDS/melonDS.ini
else
if [ "$DBOOT" = "1" ]; then
sed -i '/^DirectBoot=/c\DirectBoot=1' /storage/.config/melonDS/melonDS.ini
sed -i '/^ExternalBIOSEnable=/c\ExternalBIOSEnable=0' /storage/.config/melonDS/melonDS.ini
else
if [ "$DBOOT" = "0" ]; then
sed -i '/^DirectBoot=/c\DirectBoot=0' /storage/.config/melonDS/melonDS.ini
sed -i '/^ExternalBIOSEnable=/c\ExternalBIOSEnable=1' /storage/.config/melonDS/melonDS.ini
else
sed -i '/^DirectBoot=/c\DirectBoot=1' /storage/.config/melonDS/melonDS.ini
sed -i '/^ExternalBIOSEnable=/c\ExternalBIOSEnable=0' /storage/.config/melonDS/melonDS.ini
fi
fi

View File

@@ -7,6 +7,10 @@ if [ ! -d "/storage/.config/drastic" ]; then
mkdir -p "/storage/.config/drastic"
cp -r "/usr/config/drastic" "/storage/.config/"
fi
if [ -f "/storage/.config/drastic/config/drastic.cfg.rgds" ]; then
cp /storage/.config/drastic/config/drastic.cfg.rgds /storage/.config/drastic/config/drastic.cfg
if [ -f "/storage/.config/drastic/config/drastic.cfg.rgds" ] && [ -f "/storage/.config/drastic/config/drastic.cfg" ]; then
sed -i '/^controls_b/d' /storage/.config/drastic/config/drastic.cfg
grep '^controls_b' /storage/.config/drastic/config/drastic.cfg.rgds >> /storage/.config/drastic/config/drastic.cfg
elif [ -f "/storage/.config/drastic/config/drastic.cfg.rgds" ]; then
# Fall back to full copy if user drastic.cfg doesn't exist for some reason
cp /storage/.config/drastic/config/drastic.cfg.rgds /storage/.config/drastic/config/drastic.cfg
fi

View File

@@ -1624,6 +1624,12 @@
<choice name="on" value="1" />
<choice name="off" value= "0" />
</feature>
<feature name="microphone sensitivity">
<choice name="off" value="0" />
<choice name="high" value="0.03" />
<choice name="medium" value="0.15" />
<choice name="low" value="0.3" />
</feature>
</features>
</core>
</cores>