DSi power button and volume switch support (#1630)

* Add proper BPTWL interrupts

* Added DSi power button and volume switch hotkeys

* Added hardware reset workaround

* Adjusted syntax to follow guidelines

* Added DSi output volume synchronization

* Fix trivial member function error
This commit is contained in:
Ed_IT 2023-04-04 12:31:58 +02:00 committed by GitHub
parent 350292fb3c
commit 613280d3b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 534 additions and 22 deletions

5
.gitignore vendored
View File

@ -16,3 +16,8 @@ compile_commands.json
*.exe *.exe
.DS_Store .DS_Store
.vs
.vscode
CMakeFiles
CMakeCache.txt

View File

@ -18,6 +18,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <math.h>
#include "DSi.h" #include "DSi.h"
#include "DSi_I2C.h" #include "DSi_I2C.h"
#include "DSi_Camera.h" #include "DSi_Camera.h"
@ -31,6 +32,38 @@ using Platform::LogLevel;
namespace DSi_BPTWL namespace DSi_BPTWL
{ {
// TODO: These are purely approximations
const double PowerButtonShutdownTime = 0.5;
const double PowerButtonForcedShutdownTime = 5.0;
const double VolumeSwitchRepeatStart = 0.5;
const double VolumeSwitchRepeatRate = 1.0 / 6;
// Could not find a pattern or a decent formula for these,
// regardless, they're only 64 bytes in size
const u8 VolumeDownTable[32] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03,
0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
};
const u8 VolumeUpTable[32] =
{
0x02, 0x03, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
};
double PowerButtonTime = 0.0;
bool PowerButtonDownFlag = false;
bool PowerButtonShutdownFlag = false;
double VolumeSwitchTime = 0.0;
double VolumeSwitchRepeatTime = 0.0;
bool VolumeSwitchDownFlag = false;
u32 VolumeSwitchKeysDown = 0;
u8 Registers[0x100]; u8 Registers[0x100];
u32 CurPos; u32 CurPos;
@ -51,9 +84,9 @@ void Reset()
Registers[0x00] = 0x33; // TODO: support others?? Registers[0x00] = 0x33; // TODO: support others??
Registers[0x01] = 0x00; Registers[0x01] = 0x00;
Registers[0x02] = 0x50; Registers[0x02] = 0x50;
Registers[0x10] = 0x00; // power btn Registers[0x10] = 0x00; // irq flag
Registers[0x11] = 0x00; // reset Registers[0x11] = 0x00; // reset
Registers[0x12] = 0x00; // power btn tap Registers[0x12] = 0x00; // irq mode
Registers[0x20] = 0x8F; // battery Registers[0x20] = 0x8F; // battery
Registers[0x21] = 0x07; Registers[0x21] = 0x07;
Registers[0x30] = 0x13; Registers[0x30] = 0x13;
@ -74,6 +107,15 @@ void Reset()
Registers[0x77] = 0x00; Registers[0x77] = 0x00;
Registers[0x80] = 0x10; Registers[0x80] = 0x10;
Registers[0x81] = 0x64; Registers[0x81] = 0x64;
// Ideally these should be replaced by a proper BPTWL core emulator
PowerButtonTime = 0.0;
PowerButtonDownFlag = false;
PowerButtonShutdownFlag = false;
VolumeSwitchTime = 0.0;
VolumeSwitchRepeatTime = 0.0;
VolumeSwitchKeysDown = 0;
} }
void DoSavestate(Savestate* file) void DoSavestate(Savestate* file)
@ -84,6 +126,12 @@ void DoSavestate(Savestate* file)
file->Var32(&CurPos); file->Var32(&CurPos);
} }
// TODO: Needs more investigation on the other bits
inline bool GetIRQMode()
{
return Registers[0x12] & 0x01;
}
u8 GetBootFlag() { return Registers[0x70]; } u8 GetBootFlag() { return Registers[0x70]; }
bool GetBatteryCharging() { return Registers[0x20] >> 7; } bool GetBatteryCharging() { return Registers[0x20] >> 7; }
@ -97,6 +145,242 @@ void SetBatteryLevel(u8 batteryLevel)
{ {
Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F));
SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false);
if (batteryLevel <= 1)
{
SetIRQ(batteryLevel ? IRQ_BatteryLow : IRQ_BatteryEmpty);
}
}
u8 GetVolumeLevel() { return Registers[0x40]; }
void SetVolumeLevel(u8 volume)
{
Registers[0x40] = volume & 0x1F;
}
u8 GetBacklightLevel() { return Registers[0x41]; }
void SetBacklightLevel(u8 backlight)
{
Registers[0x41] = backlight > 4 ? 4 : backlight;
}
void ResetButtonState()
{
PowerButtonTime = 0.0;
PowerButtonDownFlag = false;
PowerButtonShutdownFlag = false;
VolumeSwitchKeysDown = 0;
VolumeSwitchDownFlag = false;
VolumeSwitchTime = 0.0;
VolumeSwitchRepeatTime = 0.0;
}
void DoHardwareReset(bool direct)
{
ResetButtonState();
Log(LogLevel::Debug, "BPTWL: soft-reset\n");
if (direct)
{
// TODO: This doesn't seem to stop the SPU
DSi::SoftReset();
return;
}
// TODO: soft-reset might need to be scheduled later!
// TODO: this has been moved for the JIT to work, nothing is confirmed here
NDS::ARM7->Halt(4);
}
void DoShutdown()
{
ResetButtonState();
NDS::Stop();
}
void SetPowerButtonHeld(double time)
{
if (!PowerButtonDownFlag)
{
PowerButtonDownFlag = true;
PowerButtonTime = time;
DoPowerButtonPress();
return;
}
double elapsed = time - PowerButtonTime;
if (elapsed < 0)
return;
if (elapsed >= PowerButtonForcedShutdownTime)
{
Log(LogLevel::Debug, "Force power off via DSi power button\n");
DoPowerButtonForceShutdown();
return;
}
if (elapsed >= PowerButtonShutdownTime)
{
DoPowerButtonShutdown();
}
}
void SetPowerButtonReleased(double time)
{
double elapsed = time - PowerButtonTime;
if (elapsed >= 0 && elapsed < PowerButtonShutdownTime)
{
DoPowerButtonReset();
}
PowerButtonTime = 0.0;
PowerButtonDownFlag = false;
PowerButtonShutdownFlag = false;
}
void SetVolumeSwitchHeld(u32 key)
{
VolumeSwitchKeysDown |= (1 << key);
}
void SetVolumeSwitchReleased(u32 key)
{
VolumeSwitchKeysDown &= ~(1 << key);
VolumeSwitchDownFlag = false;
VolumeSwitchTime = 0.0;
VolumeSwitchRepeatTime = 0.0;
}
inline bool CheckVolumeSwitchKeysValid()
{
bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up);
bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down);
return up != down;
}
s32 ProcessVolumeSwitchInput(double time)
{
if (!CheckVolumeSwitchKeysValid())
return -1;
s32 key = VolumeSwitchKeysDown & (1 << volumeKey_Up) ? volumeKey_Up : volumeKey_Down;
// Always fire an IRQ when first pressed
if (!VolumeSwitchDownFlag)
{
VolumeSwitchDownFlag = true;
VolumeSwitchTime = time;
DoVolumeSwitchPress(key);
return key;
}
// Handle key repetition mechanic
if (VolumeSwitchRepeatTime == 0)
{
double elapsed = time - VolumeSwitchTime;
if (elapsed < VolumeSwitchRepeatStart)
return -1;
VolumeSwitchRepeatTime = time;
DoVolumeSwitchPress(key);
return key;
}
double elapsed = time - VolumeSwitchRepeatTime;
if (elapsed < VolumeSwitchRepeatRate)
return -1;
double rem = fmod(elapsed, VolumeSwitchRepeatRate);
VolumeSwitchRepeatTime = time - rem;
DoVolumeSwitchPress(key);
return key;
}
void DoPowerButtonPress()
{
// Set button pressed IRQ
SetIRQ(IRQ_PowerButtonPressed);
// There is no default hardware behavior for pressing the power button
}
void DoPowerButtonReset()
{
// Reset via IRQ, handled by software
SetIRQ(IRQ_PowerButtonReset);
// Reset automatically via hardware
if (!GetIRQMode())
{
// Assumes this isn't called during normal CPU execution
DoHardwareReset(true);
}
}
void DoPowerButtonShutdown()
{
// Shutdown via IRQ, handled by software
if (!PowerButtonShutdownFlag)
{
SetIRQ(IRQ_PowerButtonShutdown);
}
PowerButtonShutdownFlag = true;
// Shutdown automatically via hardware
if (!GetIRQMode())
{
DoShutdown();
}
// The IRQ is only fired once (hence the need for an if guard),
// but the hardware shutdown is continuously triggered.
// That way when switching the IRQ mode while holding
// down the power button, the DSi will still shut down
}
void DoPowerButtonForceShutdown()
{
DoShutdown();
}
void DoVolumeSwitchPress(u32 key)
{
u8 volume = Registers[0x40];
switch (key)
{
case volumeKey_Up:
volume = VolumeUpTable[volume];
break;
case volumeKey_Down:
volume = VolumeDownTable[volume];
break;
}
Registers[0x40] = volume;
SetIRQ(IRQ_VolumeSwitchPressed);
}
void SetIRQ(u8 irqFlag)
{
Registers[0x10] |= irqFlag & IRQ_ValidMask;
if (GetIRQMode())
{
NDS::SetIRQ2(NDS::IRQ2_DSi_BPTWL);
}
} }
void Start() void Start()
@ -107,7 +391,15 @@ void Start()
u8 Read(bool last) u8 Read(bool last)
{ {
//printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1)); //printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1));
u8 ret = Registers[CurPos++]; u8 ret = Registers[CurPos];
// IRQ flags are automatically cleared upon read
if (CurPos == 0x10)
{
Registers[0x10] = 0;
}
CurPos++;
if (last) if (last)
{ {
@ -134,19 +426,29 @@ void Write(u8 val, bool last)
if (CurPos == 0x11 && val == 0x01) if (CurPos == 0x11 && val == 0x01)
{ {
Log(LogLevel::Debug, "BPTWL: soft-reset\n"); // Assumes this is called during normal CPU execution
DoHardwareReset(false);
val = 0; // checkme val = 0; // checkme
// TODO: soft-reset might need to be scheduled later!
// TODO: this has been moved for the JIT to work, nothing is confirmed here
NDS::ARM7->Halt(4);
CurPos = -1; CurPos = -1;
return; return;
} }
// Mask volume level
if (CurPos == 0x40)
{
val &= 0x1F;
}
// Clamp backlight level
if (CurPos == 0x41)
{
val = val > 4 ? 4 : val;
}
if (CurPos == 0x11 || CurPos == 0x12 || if (CurPos == 0x11 || CurPos == 0x12 ||
CurPos == 0x21 || CurPos == 0x21 ||
CurPos == 0x30 || CurPos == 0x31 || CurPos == 0x30 || CurPos == 0x31 ||
CurPos == 0x40 || CurPos == 0x31 || CurPos == 0x40 || CurPos == 0x41 ||
CurPos == 0x60 || CurPos == 0x63 || CurPos == 0x60 || CurPos == 0x63 ||
(CurPos >= 0x70 && CurPos <= 0x77) || (CurPos >= 0x70 && CurPos <= 0x77) ||
CurPos == 0x80 || CurPos == 0x81) CurPos == 0x80 || CurPos == 0x81)

View File

@ -42,6 +42,56 @@ enum
u8 GetBatteryLevel(); u8 GetBatteryLevel();
void SetBatteryLevel(u8 batteryLevel); void SetBatteryLevel(u8 batteryLevel);
// 0-31
u8 GetVolumeLevel();
void SetVolumeLevel(u8 volume);
// 0-4
u8 GetBacklightLevel();
void SetBacklightLevel(u8 backlight);
void DoHardwareReset(bool direct);
void DoShutdown();
enum
{
volumeKey_Up,
volumeKey_Down,
};
// Used by hotkeys
void SetPowerButtonHeld(double time);
void SetPowerButtonReleased(double time);
void SetVolumeSwitchHeld(u32 key);
void SetVolumeSwitchReleased(u32 key);
s32 ProcessVolumeSwitchInput(double time);
void DoPowerButtonPress();
void DoPowerButtonReset();
void DoPowerButtonShutdown();
void DoPowerButtonForceShutdown();
void DoVolumeSwitchPress(u32 key);
enum
{
IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly
IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second
IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button
IRQ_BatteryEmpty = 0x10, //
IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1
IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down
/*
Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off)
It is unknown whether it is set as the console powers off immediately.
Bit 7 (0x80) is unused?
Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?).
*/
IRQ_ValidMask = 0x7B,
};
void SetIRQ(u8 irqFlag);
} }
namespace DSi_I2C namespace DSi_I2C

View File

@ -110,7 +110,7 @@ enum
IRQ2_DSi_Unused3, IRQ2_DSi_Unused3,
IRQ2_DSi_GPIO33_0, IRQ2_DSi_GPIO33_0,
IRQ2_DSi_Headphone, IRQ2_DSi_Headphone,
IRQ2_DSi_PowerButton, IRQ2_DSi_BPTWL,
IRQ2_DSi_GPIO33_3, // "sound enable input" IRQ2_DSi_GPIO33_3, // "sound enable input"
IRQ2_DSi_SDMMC, IRQ2_DSi_SDMMC,
IRQ2_DSi_SD_Data1, IRQ2_DSi_SD_Data1,

View File

@ -22,6 +22,8 @@
#include "types.h" #include "types.h"
#include "Platform.h" #include "Platform.h"
#include "Config.h" #include "Config.h"
#include "NDS.h"
#include "DSi_I2C.h"
#include "AudioSettingsDialog.h" #include "AudioSettingsDialog.h"
#include "ui_AudioSettingsDialog.h" #include "ui_AudioSettingsDialog.h"
@ -32,7 +34,7 @@ AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr;
extern std::string EmuDirectory; extern std::string EmuDirectory;
AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AudioSettingsDialog) AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDialog(parent), ui(new Ui::AudioSettingsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
@ -40,6 +42,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(
oldInterp = Config::AudioInterp; oldInterp = Config::AudioInterp;
oldBitrate = Config::AudioBitrate; oldBitrate = Config::AudioBitrate;
oldVolume = Config::AudioVolume; oldVolume = Config::AudioVolume;
oldDSiSync = Config::DSiVolumeSync;
ui->cbInterpolation->addItem("None"); ui->cbInterpolation->addItem("None");
ui->cbInterpolation->addItem("Linear"); ui->cbInterpolation->addItem("Linear");
@ -52,7 +55,21 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(
ui->cbBitrate->addItem("16-bit"); ui->cbBitrate->addItem("16-bit");
ui->cbBitrate->setCurrentIndex(Config::AudioBitrate); ui->cbBitrate->setCurrentIndex(Config::AudioBitrate);
bool state = ui->slVolume->blockSignals(true);
ui->slVolume->setValue(Config::AudioVolume); ui->slVolume->setValue(Config::AudioVolume);
ui->slVolume->blockSignals(state);
ui->chkSyncDSiVolume->setChecked(Config::DSiVolumeSync);
// Setup volume slider accordingly
if (emuActive && NDS::ConsoleType == 1)
{
on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync);
}
else
{
ui->chkSyncDSiVolume->setEnabled(false);
}
grpMicMode = new QButtonGroup(this); grpMicMode = new QButtonGroup(this);
grpMicMode->addButton(ui->rbMicNone, 0); grpMicMode->addButton(ui->rbMicNone, 0);
@ -88,6 +105,22 @@ AudioSettingsDialog::~AudioSettingsDialog()
delete ui; delete ui;
} }
void AudioSettingsDialog::onSyncVolumeLevel()
{
if (Config::DSiVolumeSync && NDS::ConsoleType == 1)
{
bool state = ui->slVolume->blockSignals(true);
ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel());
ui->slVolume->blockSignals(state);
}
}
void AudioSettingsDialog::onConsoleReset()
{
on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync);
ui->chkSyncDSiVolume->setEnabled(NDS::ConsoleType == 1);
}
void AudioSettingsDialog::on_AudioSettingsDialog_accepted() void AudioSettingsDialog::on_AudioSettingsDialog_accepted()
{ {
Config::MicInputType = grpMicMode->checkedId(); Config::MicInputType = grpMicMode->checkedId();
@ -102,6 +135,7 @@ void AudioSettingsDialog::on_AudioSettingsDialog_rejected()
Config::AudioInterp = oldInterp; Config::AudioInterp = oldInterp;
Config::AudioBitrate = oldBitrate; Config::AudioBitrate = oldBitrate;
Config::AudioVolume = oldVolume; Config::AudioVolume = oldVolume;
Config::DSiVolumeSync = oldDSiSync;
closeDlg(); closeDlg();
} }
@ -128,9 +162,38 @@ void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx)
void AudioSettingsDialog::on_slVolume_valueChanged(int val) void AudioSettingsDialog::on_slVolume_valueChanged(int val)
{ {
if (Config::DSiVolumeSync && NDS::ConsoleType == 1)
{
DSi_BPTWL::SetVolumeLevel(val);
return;
}
Config::AudioVolume = val; Config::AudioVolume = val;
} }
void AudioSettingsDialog::on_chkSyncDSiVolume_clicked(bool checked)
{
Config::DSiVolumeSync = checked;
bool state = ui->slVolume->blockSignals(true);
if (Config::DSiVolumeSync && NDS::ConsoleType == 1)
{
ui->slVolume->setMaximum(31);
ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel());
ui->slVolume->setPageStep(4);
ui->slVolume->setTickPosition(QSlider::TicksBelow);
}
else
{
Config::AudioVolume = oldVolume;
ui->slVolume->setMaximum(256);
ui->slVolume->setValue(Config::AudioVolume);
ui->slVolume->setPageStep(16);
ui->slVolume->setTickPosition(QSlider::NoTicks);
}
ui->slVolume->blockSignals(state);
}
void AudioSettingsDialog::onChangeMicMode(int mode) void AudioSettingsDialog::onChangeMicMode(int mode)
{ {
bool iswav = (mode == 3); bool iswav = (mode == 3);

View File

@ -30,11 +30,11 @@ class AudioSettingsDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit AudioSettingsDialog(QWidget* parent); explicit AudioSettingsDialog(QWidget* parent, bool emuActive);
~AudioSettingsDialog(); ~AudioSettingsDialog();
static AudioSettingsDialog* currentDlg; static AudioSettingsDialog* currentDlg;
static AudioSettingsDialog* openDlg(QWidget* parent) static AudioSettingsDialog* openDlg(QWidget* parent, bool emuActive)
{ {
if (currentDlg) if (currentDlg)
{ {
@ -42,7 +42,7 @@ public:
return currentDlg; return currentDlg;
} }
currentDlg = new AudioSettingsDialog(parent); currentDlg = new AudioSettingsDialog(parent, emuActive);
currentDlg->show(); currentDlg->show();
return currentDlg; return currentDlg;
} }
@ -51,6 +51,9 @@ public:
currentDlg = nullptr; currentDlg = nullptr;
} }
void onSyncVolumeLevel();
void onConsoleReset();
signals: signals:
void updateAudioSettings(); void updateAudioSettings();
@ -61,6 +64,7 @@ private slots:
void on_cbInterpolation_currentIndexChanged(int idx); void on_cbInterpolation_currentIndexChanged(int idx);
void on_cbBitrate_currentIndexChanged(int idx); void on_cbBitrate_currentIndexChanged(int idx);
void on_slVolume_valueChanged(int val); void on_slVolume_valueChanged(int val);
void on_chkSyncDSiVolume_clicked(bool checked);
void onChangeMicMode(int mode); void onChangeMicMode(int mode);
void on_btnMicWavBrowse_clicked(); void on_btnMicWavBrowse_clicked();
@ -70,6 +74,7 @@ private:
int oldInterp; int oldInterp;
int oldBitrate; int oldBitrate;
int oldVolume; int oldVolume;
bool oldDSiSync;
QButtonGroup* grpMicMode; QButtonGroup* grpMicMode;
}; };

View File

@ -59,6 +59,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1">
<widget class="QCheckBox" name="chkSyncDSiVolume">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Synchronizes the output volume with the DSi hardware volume.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Sync with DSi volume</string>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">

View File

@ -118,6 +118,7 @@ bool SavestateRelocSRAM;
int AudioInterp; int AudioInterp;
int AudioBitrate; int AudioBitrate;
int AudioVolume; int AudioVolume;
bool DSiVolumeSync;
int MicInputType; int MicInputType;
std::string MicWavPath; std::string MicWavPath;
@ -187,6 +188,9 @@ ConfigEntry ConfigFile[] =
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, true}, {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, true},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, true}, {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, true},
{"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, true}, {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, true},
{"HKKey_PowerButton", 0, &HKKeyMapping[HK_PowerButton], -1, true},
{"HKKey_VolumeUp", 0, &HKKeyMapping[HK_VolumeUp], -1, true},
{"HKKey_VolumeDown", 0, &HKKeyMapping[HK_VolumeDown], -1, true},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, true}, {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, true},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, true}, {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, true},
@ -200,6 +204,9 @@ ConfigEntry ConfigFile[] =
{"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, true}, {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, true},
{"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, true}, {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, true},
{"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, true}, {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, true},
{"HKJoy_PowerButton", 0, &HKJoyMapping[HK_PowerButton], -1, true},
{"HKJoy_VolumeUp", 0, &HKJoyMapping[HK_VolumeUp], -1, true},
{"HKJoy_VolumeDown", 0, &HKJoyMapping[HK_VolumeDown], -1, true},
{"JoystickID", 0, &JoystickID, 0, true}, {"JoystickID", 0, &JoystickID, 0, true},
@ -291,6 +298,7 @@ ConfigEntry ConfigFile[] =
{"AudioInterp", 0, &AudioInterp, 0, false}, {"AudioInterp", 0, &AudioInterp, 0, false},
{"AudioBitrate", 0, &AudioBitrate, 0, false}, {"AudioBitrate", 0, &AudioBitrate, 0, false},
{"AudioVolume", 0, &AudioVolume, 256, true}, {"AudioVolume", 0, &AudioVolume, 256, true},
{"DSiVolumeSync", 0, &DSiVolumeSync, 0, true},
{"MicInputType", 0, &MicInputType, 1, false}, {"MicInputType", 0, &MicInputType, 1, false},
{"MicWavPath", 2, &MicWavPath, (std::string)"", false}, {"MicWavPath", 2, &MicWavPath, (std::string)"", false},

View File

@ -36,6 +36,9 @@ enum
HK_SolarSensorDecrease, HK_SolarSensorDecrease,
HK_SolarSensorIncrease, HK_SolarSensorIncrease,
HK_FrameStep, HK_FrameStep,
HK_PowerButton,
HK_VolumeUp,
HK_VolumeDown,
HK_MAX HK_MAX
}; };
@ -163,6 +166,7 @@ extern bool SavestateRelocSRAM;
extern int AudioInterp; extern int AudioInterp;
extern int AudioBitrate; extern int AudioBitrate;
extern int AudioVolume; extern int AudioVolume;
extern bool DSiVolumeSync;
extern int MicInputType; extern int MicInputType;
extern std::string MicWavPath; extern std::string MicWavPath;

View File

@ -52,7 +52,10 @@ static constexpr std::initializer_list<int> hk_general =
HK_Lid, HK_Lid,
HK_Mic, HK_Mic,
HK_SwapScreens, HK_SwapScreens,
HK_SwapScreenEmphasis HK_SwapScreenEmphasis,
HK_PowerButton,
HK_VolumeUp,
HK_VolumeDown
}; };
static constexpr std::initializer_list<const char*> hk_general_labels = static constexpr std::initializer_list<const char*> hk_general_labels =
@ -66,7 +69,10 @@ static constexpr std::initializer_list<const char*> hk_general_labels =
"Close/open lid", "Close/open lid",
"Microphone", "Microphone",
"Swap screens", "Swap screens",
"Swap screen emphasis" "Swap screen emphasis",
"DSi Power button",
"DSi Volume up",
"DSi Volume down"
}; };
static_assert(hk_general.size() == hk_general_labels.size()); static_assert(hk_general.size() == hk_general_labels.size());

View File

@ -88,6 +88,7 @@
#include "Platform.h" #include "Platform.h"
#include "LocalMP.h" #include "LocalMP.h"
#include "Config.h" #include "Config.h"
#include "DSi_I2C.h"
#include "Savestate.h" #include "Savestate.h"
@ -581,6 +582,7 @@ void EmuThread::run()
double lastMeasureTime = lastTime; double lastMeasureTime = lastTime;
u32 winUpdateCount = 0, winUpdateFreq = 1; u32 winUpdateCount = 0, winUpdateFreq = 1;
u8 dsiVolumeLevel = 0x1F;
char melontitle[100]; char melontitle[100];
@ -620,6 +622,42 @@ void EmuThread::run()
} }
} }
if (NDS::ConsoleType == 1)
{
double currentTime = SDL_GetPerformanceCounter() * perfCountsSec;
// Handle power button
if (Input::HotkeyDown(HK_PowerButton))
{
DSi_BPTWL::SetPowerButtonHeld(currentTime);
}
else if (Input::HotkeyReleased(HK_PowerButton))
{
DSi_BPTWL::SetPowerButtonReleased(currentTime);
}
// Handle volume buttons
if (Input::HotkeyDown(HK_VolumeUp))
{
DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up);
}
else if (Input::HotkeyReleased(HK_VolumeUp))
{
DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up);
}
if (Input::HotkeyDown(HK_VolumeDown))
{
DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down);
}
else if (Input::HotkeyReleased(HK_VolumeDown))
{
DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down);
}
DSi_BPTWL::ProcessVolumeSwitchInput(currentTime);
}
if (EmuRunning == 1 || EmuRunning == 3) if (EmuRunning == 1 || EmuRunning == 3)
{ {
EmuStatus = 1; EmuStatus = 1;
@ -739,6 +777,18 @@ void EmuThread::run()
oglContext->SetSwapInterval(0); oglContext->SetSwapInterval(0);
} }
if (Config::DSiVolumeSync && NDS::ConsoleType == 1)
{
u8 volumeLevel = DSi_BPTWL::GetVolumeLevel();
if (volumeLevel != dsiVolumeLevel)
{
dsiVolumeLevel = volumeLevel;
emit syncVolumeLevel();
}
Config::AudioVolume = volumeLevel * (256.0 / 31.0);
}
if (Config::AudioSync && !fastforward && audioDevice) if (Config::AudioSync && !fastforward && audioDevice)
{ {
SDL_LockMutex(audioSyncLock); SDL_LockMutex(audioSyncLock);
@ -3027,7 +3077,9 @@ void MainWindow::onCameraSettingsFinished(int res)
void MainWindow::onOpenAudioSettings() void MainWindow::onOpenAudioSettings()
{ {
AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this); AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive());
connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel);
connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset);
connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings);
connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished);
} }
@ -3270,7 +3322,8 @@ void MainWindow::onTitleUpdate(QString title)
setWindowTitle(title); setWindowTitle(title);
} }
void ToggleFullscreen(MainWindow* mainWindow) { void ToggleFullscreen(MainWindow* mainWindow)
{
if (!mainWindow->isFullScreen()) if (!mainWindow->isFullScreen())
{ {
mainWindow->showFullScreen(); mainWindow->showFullScreen();
@ -3289,11 +3342,15 @@ void MainWindow::onFullscreenToggled()
ToggleFullscreen(this); ToggleFullscreen(this);
} }
void MainWindow::onScreenEmphasisToggled() { void MainWindow::onScreenEmphasisToggled()
{
int currentSizing = Config::ScreenSizing; int currentSizing = Config::ScreenSizing;
if (currentSizing == screenSizing_EmphTop) { if (currentSizing == screenSizing_EmphTop)
{
Config::ScreenSizing = screenSizing_EmphBot; Config::ScreenSizing = screenSizing_EmphBot;
} else if (currentSizing == screenSizing_EmphBot) { }
else if (currentSizing == screenSizing_EmphBot)
{
Config::ScreenSizing = screenSizing_EmphTop; Config::ScreenSizing = screenSizing_EmphTop;
} }

View File

@ -87,6 +87,8 @@ signals:
void swapScreensToggle(); void swapScreensToggle();
void screenEmphasisToggle(); void screenEmphasisToggle();
void syncVolumeLevel();
private: private:
void drawScreenGL(); void drawScreenGL();
void initOpenGL(); void initOpenGL();