summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortortugaveloz <danielcampora@outlook.es>2025-05-20 22:58:35 +0200
committerGitHub <noreply@github.com>2025-05-20 17:58:35 -0300
commitca1a5dd510e3ef570e69a25251015a3ad8ab66c7 (patch)
tree21ce114aa5548dfc48925386c6b95982a99d20de
parent1e65843326ba2bd42858ac66ef4c849745b68979 (diff)
Additional 5ch options (#196)
* Added angle selector for surround channels, subwoofer threshold and rear music volume slider. * Change comment. * Integrate options in game. * Added reset button for positions. * Added option to show / hide menu for 5.1. * Added mfpu=neon option to aarch64. --------- Co-authored-by: Sonic Dreamcaster <alejandro.asenjo88@gmail.com>
-rw-r--r--CMakeLists.txt2
m---------libultraship0
-rw-r--r--src/audio/audio_playback.c20
-rw-r--r--src/audio/audio_synthesis.c9
-rw-r--r--src/audio/mixer.c6
-rw-r--r--src/audio/mixer.h7
-rw-r--r--src/port/ui/ImguiUI.cpp128
7 files changed, 151 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 171e542b..35f8d01e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -574,6 +574,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
else()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(CPU_OPTION -msse2 -mfpmath=sse)
+ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
+ set(CPU_OPTION -mfpu=neon)
endif()
target_compile_options(${PROJECT_NAME} PRIVATE
diff --git a/libultraship b/libultraship
-Subproject a8cddd2f8991c9d50783db4de855c98a392f4ac
+Subproject dda07c8ac6033ff98d37a05bc28406440c7769e
diff --git a/src/audio/audio_playback.c b/src/audio/audio_playback.c
index be9dfbcd..976d9f81 100644
--- a/src/audio/audio_playback.c
+++ b/src/audio/audio_playback.c
@@ -144,17 +144,13 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
panVolumeCenter = 1.0f;
}
else if (stereo.s.is_sfx) { // SFX
- float pan_angle = (float)(pan + 64) / 128 * 2 * M_PI;
+ float pan_angle = ((float) pan) / 128 * 2 * M_PI;
// Speaker angles in radians
- const float front_left = -0.5236;
- const float front_right = 0.5236;
- const float rear_left = -1.92;
- const float rear_right = 1.92;
-
- // Normalize pan_angle to [0, 2π]
- pan_angle = fmodf(pan_angle, 2 * M_PI);
- if (pan_angle < 0) pan_angle += 2 * M_PI;
+ const float front_left = (CVarGetInteger("gPositionFrontLeft", 240) - 90) * (M_PI / 180.0f);
+ const float front_right = (CVarGetInteger("gPositionFrontRight", 300) - 90) * (M_PI / 180.0f);
+ const float rear_left = (CVarGetInteger("gPositionRearLeft", 160) - 90) * (M_PI / 180.0f);
+ const float rear_right = (CVarGetInteger("gPositionRearRight", 20) - 90) * (M_PI / 180.0f);
// Calculate volumes using cosine panning law
panVolumeLeft = fmaxf(0, cosf(pan_angle - front_left)); // Front Left
@@ -164,8 +160,10 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
} else { // MUSIC
panVolumeLeft = gStereoPanVolume[pan];
panVolumeRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan];
- panVolumeRearLeft = gStereoPanVolume[pan];
- panVolumeRearRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan];
+
+ f32 rearMusicVolume = CVarGetFloat("gVolumeRearMusic", 1.0f);
+ panVolumeRearLeft = gStereoPanVolume[pan] * rearMusicVolume;
+ panVolumeRearRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan] * rearMusicVolume;
}
}
diff --git a/src/audio/audio_synthesis.c b/src/audio/audio_synthesis.c
index 84d894c5..17917d53 100644
--- a/src/audio/audio_synthesis.c
+++ b/src/audio/audio_synthesis.c
@@ -1368,6 +1368,7 @@ Acmd* AudioSynth_ProcessEnvelope(Acmd* aList, NoteSubEu* noteSub, NoteSynthesisS
synthState->curVolLfe = curVolLfe + (rampLfe * aiBufLenSmall);
synthState->curVolRLeft = curVolRLeft + (rampRLeft * aiBufLenSmall);
synthState->curVolRRight = curVolRRight + (rampRRight * aiBufLenSmall);
+ uint32_t cutoffFreqLfe = CVarGetInteger("gSubwooferThreshold", 80);
if (noteSub->bitField0.usesHeadsetPanEffects) {
int32_t num_audio_channels = 2;
@@ -1378,24 +1379,24 @@ Acmd* AudioSynth_ProcessEnvelope(Acmd* aList, NoteSubEu* noteSub, NoteSynthesisS
switch (delaySide) {
case HAAS_EFFECT_DELAY_LEFT:
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
- noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP << 16, num_audio_channels);
+ noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP << 16, num_audio_channels, cutoffFreqLfe);
break;
case HAAS_EFFECT_DELAY_RIGHT:
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
- noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP, num_audio_channels);
+ noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP, num_audio_channels, cutoffFreqLfe);
break;
default: // HAAS_EFFECT_DELAY_NONE
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
- noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, num_audio_channels);
+ noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, num_audio_channels, cutoffFreqLfe);
break;
}
} else {
aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight);
aEnvSetup2(aList++, curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight);
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
- noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, GetNumAudioChannels());
+ noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, GetNumAudioChannels(), cutoffFreqLfe);
}
return aList;
diff --git a/src/audio/mixer.c b/src/audio/mixer.c
index c2f9f3ea..d3c5a4d7 100644
--- a/src/audio/mixer.c
+++ b/src/audio/mixer.c
@@ -77,7 +77,6 @@ static __m128i m256i_clamp_to_m128i(m256i a) {
#define BUF_S16(a) (int16_t*) BUF_U8(a)
#define SAMPLE_RATE 32000 // Adjusted to match the actual sample rate of 32 kHz
-#define CUTOFF_FREQ_LFE 80 // Cutoff frequency of 80 Hz
static struct {
uint16_t in;
@@ -645,7 +644,8 @@ void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right, int16
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb,
bool neg_left, bool neg_right,
- uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels)
+ uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels,
+ uint32_t cutoff_freq_lfe)
{
// Note: max number of samples is 192 (192 * 2 = 384 bytes = 0x180)
int max_num_samples = 192;
@@ -675,7 +675,7 @@ void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb,
if (num_channels == 6) {
// Calculate the filter coefficient
- float RC = 1.f / (2 * M_PI * CUTOFF_FREQ_LFE);
+ float RC = 1.f / (2 * M_PI * cutoff_freq_lfe);
float dt = 1.f / SAMPLE_RATE;
float alpha = dt / (RC + dt);
diff --git a/src/audio/mixer.h b/src/audio/mixer.h
index 64c2221c..52cafd98 100644
--- a/src/audio/mixer.h
+++ b/src/audio/mixer.h
@@ -47,7 +47,8 @@ void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_le
void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right, int16_t initial_vol_center,
int16_t initial_vol_lfe, int16_t initial_vol_rear_left, int16_t initial_vol_rear_right);
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, bool neg_left,
- bool neg_right, uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels);
+ bool neg_right, uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels,
+ uint32_t cutoff_freq_lfe);
void aMixImpl(uint16_t count, int16_t gain, uint16_t in_addr, uint16_t out_addr);
void aS8DecImpl(uint8_t flags, ADPCM_STATE state);
void aAddMixerImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr);
@@ -76,8 +77,8 @@ void aUnkCmd19Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_add
aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight)
#define aEnvSetup2(pkt, initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight) \
aEnvSetup2Impl(initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight)
-#define aEnvMixer(pkt, inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels) \
- aEnvMixerImpl(inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels)
+#define aEnvMixer(pkt, inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels, cutoffFreqLfe) \
+ aEnvMixerImpl(inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels, cutoffFreqLfe)
#define aMix(pkt, c, g, i, o) aMixImpl(c, g, i, o)
#define aS8Dec(pkt, f, s) aS8DecImpl(f, s)
#define aAddMixer(pkt, s, d, c) aAddMixerImpl(s, d, c)
diff --git a/src/port/ui/ImguiUI.cpp b/src/port/ui/ImguiUI.cpp
index 615a07d0..1987efd7 100644
--- a/src/port/ui/ImguiUI.cpp
+++ b/src/port/ui/ImguiUI.cpp
@@ -116,6 +116,117 @@ static const char* voiceLangs[] = {
"Original", /*"Japanese",*/ "Lylat"
};
+void DrawSpeakerPositionEditor() {
+ static ImVec2 lastCanvasPos;
+ ImGui::Text("Speaker Position Editor");
+ ImVec2 canvasSize = ImVec2(200, 200); // Static canvas size
+ ImVec2 canvasPos = ImGui::GetCursorScreenPos();
+ ImVec2 center = ImVec2(canvasPos.x + canvasSize.x / 2, canvasPos.y + canvasSize.y / 2);
+
+ // Speaker positions
+ static ImVec2 speakerPositions[4];
+ static bool initialized = false;
+ static float radius = 80.0f;
+
+ // Reset positions if canvas position changed (window resized/moved)
+ if (!initialized || (lastCanvasPos.x != canvasPos.x || lastCanvasPos.y != canvasPos.y)) {
+ const char* cvarNames[4] = { "gPositionFrontLeft", "gPositionFrontRight", "gPositionRearLeft", "gPositionRearRight" };
+ float angles[4] = { 240.f, 300.f, 160.f, 20.f }; // Default angles
+
+ for (int i = 0; i < 4; i++) {
+ int savedAngle = CVarGetInteger(cvarNames[i], -1);
+ if (savedAngle != -1) {
+ angles[i] = static_cast<float>(savedAngle);
+ }
+
+ float rad = angles[i] * (M_PI / 180.0f);
+ speakerPositions[i] = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
+ }
+ initialized = true;
+ lastCanvasPos = canvasPos;
+ }
+
+ // Draw canvas
+ ImDrawList* drawList = ImGui::GetWindowDrawList();
+ drawList->AddRectFilled(canvasPos, ImVec2(canvasPos.x + canvasSize.x, canvasPos.y + canvasSize.y), IM_COL32(26, 26, 26, 255));
+ drawList->AddCircleFilled(center, 5.0f, IM_COL32(255, 255, 255, 255)); // Central person
+
+ // Draw circle line for speaker positions
+ drawList->AddCircle(center, radius, IM_COL32(163, 163, 163, 255), 100);
+
+ // Add markers at 0, 22.5, 45, etc.
+ for (float angle = 0; angle < 360; angle += 22.5f) {
+ float rad = angle * (M_PI / 180.0f);
+ ImVec2 markerStart = ImVec2(center.x + (radius - 5) * cosf(rad), center.y + (radius - 5) * sinf(rad));
+ ImVec2 markerEnd = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
+ drawList->AddLine(markerStart, markerEnd, IM_COL32(163, 163, 163, 255));
+ }
+
+ const char* speakerLabels[4] = { "L", "R", "RL", "RR" };
+ const char* cvarNames[4] = { "gPositionFrontLeft", "gPositionFrontRight", "gPositionRearLeft", "gPositionRearRight" };
+
+ const float snapThreshold = 2.5f; // Degrees within which snapping occurs
+
+ for (int i = 0; i < 4; i++) {
+ // Draw speaker as a darker blue circle
+ drawList->AddCircleFilled(speakerPositions[i], 10.0f, IM_COL32(34, 52, 78, 255)); // Dark blue color
+ drawList->AddText(ImVec2(speakerPositions[i].x - 6, speakerPositions[i].y - 6), IM_COL32(255, 255, 255, 255), speakerLabels[i]);
+
+ // Handle dragging
+ ImGui::SetCursorScreenPos(ImVec2(speakerPositions[i].x - 10, speakerPositions[i].y - 10));
+ ImGui::InvisibleButton(speakerLabels[i], ImVec2(20, 20));
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
+ ImVec2 mouseDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left);
+ ImVec2 newPos = ImVec2(speakerPositions[i].x + mouseDelta.x, speakerPositions[i].y + mouseDelta.y);
+
+ // Constrain position to the circle
+ ImVec2 direction = ImVec2(newPos.x - center.x, newPos.y - center.y);
+ float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
+ ImVec2 constrainedPos = ImVec2(center.x + (direction.x / length) * radius, center.y + (direction.y / length) * radius);
+
+ // Calculate angle of the constrained position
+ float angle = atan2f(constrainedPos.y - center.y, constrainedPos.x - center.x) * (180.0f / M_PI);
+ if (angle < 0) angle += 360.0f;
+
+ // Snap to the nearest 22.5-degree marker if within the snap threshold
+ float snappedAngle = roundf(angle / 22.5f) * 22.5f;
+ if (fabsf(snappedAngle - angle) <= snapThreshold) {
+ float rad = snappedAngle * (M_PI / 180.0f);
+ constrainedPos = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
+ }
+
+ speakerPositions[i] = constrainedPos;
+ ImGui::ResetMouseDragDelta();
+
+ // Save the updated angle to CVar after dragging
+ float updatedAngle = atan2f(speakerPositions[i].y - center.y, speakerPositions[i].x - center.x) * (180.0f / M_PI);
+ if (updatedAngle < 0) updatedAngle += 360.0f;
+ CVarSetInteger(cvarNames[i], static_cast<int>(updatedAngle));
+ Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); // Mark for saving
+ }
+
+ // Calculate angle and save to CVar
+ float angle = atan2f(speakerPositions[i].y - center.y, speakerPositions[i].x - center.x) * (180.0f / M_PI);
+ if (angle < 0) angle += 360.0f;
+ CVarSetInteger(cvarNames[i], static_cast<int>(angle));
+ }
+
+ // Reset cursor position for button placement
+ ImGui::SetCursorScreenPos(ImVec2(canvasPos.x, canvasPos.y + canvasSize.y + 10));
+ if (ImGui::Button("Reset Positions")) {
+ float defaultAngles[4] = { 240.f, 300.f, 160.f, 20.f };
+ for (int i = 0; i < 4; i++) {
+ float rad = defaultAngles[i] * (M_PI / 180.0f);
+ speakerPositions[i] = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
+ CVarSetInteger(cvarNames[i], static_cast<int>(defaultAngles[i]));
+ }
+ Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
+ }
+
+ // Reset cursor position to ensure canvas size remains static
+ ImGui::SetCursorScreenPos(ImVec2(canvasPos.x, canvasPos.y + canvasSize.y + 10));
+}
+
void DrawSettingsMenu(){
if(UIWidgets::BeginMenu("Settings")){
if (UIWidgets::BeginMenu("Audio")) {
@@ -173,6 +284,23 @@ void DrawSettingsMenu(){
}
UIWidgets::PaddedEnhancementCheckbox("Surround 5.1 (Needs reload)", "gAudioChannelsSetting", 1, 0);
+
+ if (CVarGetInteger("gAudioChannelsSetting", 0) == 1) {
+ // Subwoofer threshold
+ UIWidgets::CVarSliderInt("Subwoofer threshold (Hz)", "gSubwooferThreshold", 10u, 1000u, 80u, {
+ .tooltip = "The threshold for the subwoofer to be activated. Any sound under this frequency will be played on the subwoofer.",
+ .format = "%d",
+ });
+
+ // Rear music volume slider
+ UIWidgets::CVarSliderFloat("Rear music volume", "gVolumeRearMusic", 0.0f, 1.0f, 1.0f, {
+ .format = "%.0f%%",
+ .isPercentage = true,
+ });
+
+ // Configurable positioning of speakers
+ DrawSpeakerPositionEditor();
+ }
ImGui::EndMenu();
}