Add time-varying pitch modulation and built-in procedural glitch effects

This commit is contained in:
rardog
2026-09-24 13:29:51 +03:00
parent 2b9c376bbc
commit cd3672a274
6 changed files with 460 additions and 2 deletions
@@ -3,6 +3,7 @@ package com.example.soundmod.client.screen;
import com.example.soundmod.client.audio.AudioPlaybackService;
import com.example.soundmod.common.ActivationMode;
import com.example.soundmod.common.AttenuationMode;
import com.example.soundmod.common.PitchModulationMode;
import com.example.soundmod.common.RedstoneMode;
import com.example.soundmod.common.ScenarioQueue;
import com.example.soundmod.common.SoundManifestEntry;
@@ -111,6 +112,9 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
private Button crossfadeButton;
private TextFieldWidget fadeInField;
private TextFieldWidget fadeOutField;
private Button pitchModeButton;
private TextFieldWidget pitchModSpeedField;
private TextFieldWidget pitchModDepthField;
// Controls - Trigger
private Button activationModeButton;
@@ -349,6 +353,14 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
this.fadeInField = this.createField(x + 88, y + 147, 44, Integer.toString(this.localSettings.fadeInTicks));
this.fadeOutField = this.createField(x + 226, y + 147, 44, Integer.toString(this.localSettings.fadeOutTicks));
this.pitchModeButton = this.addButtonTracked(new Button(x, y + 172, 114, 20, new StringTextComponent("Pitch: " + this.localSettings.pitchMode.getDisplayName()), b -> {
this.localSettings.pitchMode = this.cyclePitchMode(this.localSettings.pitchMode);
b.setMessage(new StringTextComponent("Pitch: " + this.localSettings.pitchMode.getDisplayName()));
this.sendLiveApply("pitchMode");
}), this.effectsButtons);
this.pitchModSpeedField = this.createField(x + 152, y + 173, 34, this.floatText(this.localSettings.pitchModSpeed, 1));
this.pitchModDepthField = this.createField(x + 228, y + 173, 34, this.floatText(this.localSettings.pitchModDepth, 2));
// --- TAB 3: TRIGGER ---
this.activationModeButton = this.addButtonTracked(new Button(x, y + 42, 128, 20, new StringTextComponent("Режим: " + this.modeRu(this.localSettings.activationMode)), b -> {
this.localSettings.activationMode = this.cycleMode(this.localSettings.activationMode);
@@ -478,6 +490,8 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
this.addFieldTracked(this.reverbStrengthField, this.effectsFields);
this.addFieldTracked(this.fadeInField, this.effectsFields);
this.addFieldTracked(this.fadeOutField, this.effectsFields);
this.addFieldTracked(this.pitchModSpeedField, this.effectsFields);
this.addFieldTracked(this.pitchModDepthField, this.effectsFields);
this.addFieldTracked(this.startDelayField, this.triggerFields);
this.addFieldTracked(this.repeatDelayField, this.triggerFields);
@@ -741,6 +755,8 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
if (this.reverbStrengthField != null) this.localSettings.reverbStrength = this.clampFloat(this.parseFloat(this.reverbStrengthField.getValue(), 0.35f), 0.0f, 1.0f);
if (this.fadeInField != null) this.localSettings.fadeInTicks = this.clampInt(this.parseInt(this.fadeInField.getValue(), 0), 0, 72000);
if (this.fadeOutField != null) this.localSettings.fadeOutTicks = this.clampInt(this.parseInt(this.fadeOutField.getValue(), 0), 0, 72000);
if (this.pitchModSpeedField != null) this.localSettings.pitchModSpeed = this.clampFloat(this.parseFloat(this.pitchModSpeedField.getValue(), 1.0f), 0.05f, 20.0f);
if (this.pitchModDepthField != null) this.localSettings.pitchModDepth = this.clampFloat(this.parseFloat(this.pitchModDepthField.getValue(), 0.25f), 0.0f, 2.0f);
if (this.startDelayField != null) this.localSettings.startDelayTicks = this.clampInt(this.parseInt(this.startDelayField.getValue(), 0), 0, 72000);
if (this.repeatDelayField != null) this.localSettings.repeatDelayTicks = this.clampInt(this.parseInt(this.repeatDelayField.getValue(), 0), 0, 72000);
@@ -792,6 +808,15 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
this.zoneStopOnExitButton.setMessage(this.boolLabel("Стоп-выход", this.zoneStopOnExit));
this.zoneUseCustomCoordsButton.setMessage(this.boolLabel("Смещение центра", this.zoneUseCustomCoords));
this.scenarioLoopButton.setMessage(this.boolLabel("Цикл", this.activeScenarioLoop()));
if (this.pitchModSpeedField != null) {
this.pitchModSpeedField.setValue(this.floatText(this.localSettings.pitchModSpeed, 1));
}
if (this.pitchModDepthField != null) {
this.pitchModDepthField.setValue(this.floatText(this.localSettings.pitchModDepth, 2));
}
if (this.pitchModeButton != null) {
this.pitchModeButton.setMessage(new StringTextComponent("Pitch: " + this.localSettings.pitchMode.getDisplayName()));
}
if (this.scenarioShuffleButton != null) {
this.scenarioShuffleButton.setMessage(this.boolLabel("Случайно", this.activeScenarioShuffle()));
}
@@ -801,6 +826,12 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
this.updateTabVisibility();
}
private PitchModulationMode cyclePitchMode(PitchModulationMode current) {
PitchModulationMode[] values = PitchModulationMode.values();
int next = (current.ordinal() + 1) % values.length;
return values[next];
}
private void addScenarioStep() {
String sid = this.scenarioSoundField.getValue().trim();
if (sid.isEmpty()) {
@@ -1113,7 +1144,9 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
this.font.draw(matrixStack, new StringTextComponent("Сила:"), (float) (x + 176), (float) (y + 99), COLOR_TEXT_LABEL);
this.font.draw(matrixStack, new StringTextComponent("Fade In:"), (float) x, (float) (y + 151), COLOR_TEXT_LABEL);
this.font.draw(matrixStack, new StringTextComponent("Fade Out:"), (float) (x + 148), (float) (y + 151), COLOR_TEXT_LABEL);
this.font.draw(matrixStack, new StringTextComponent("⚡ Изменения применяются мгновенно во время звука"), (float) x, (float) (y + 176), COLOR_TEXT_MUTED);
this.font.draw(matrixStack, new StringTextComponent("Скор:"), (float) (x + 118), (float) (y + 177), COLOR_TEXT_LABEL);
this.font.draw(matrixStack, new StringTextComponent("Глуб:"), (float) (x + 192), (float) (y + 177), COLOR_TEXT_LABEL);
this.font.draw(matrixStack, new StringTextComponent("⚡ Изменения применяются на лету во время звука"), (float) x, (float) (y + 195), COLOR_TEXT_MUTED);
break;
case TRIGGER:
@@ -1321,6 +1354,7 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
lines.add("• ▶ Тест: мгновенное предпрослушивание выбранного звука.");
lines.add("• 2x клик по списку: запускает предпрослушивание трека.");
lines.add("• Эффекты: 3D звук, приглушение за стенами (окклюзия), эхо.");
lines.add("• Модуляция Pitch: вибрато, глитч, tape stop (замедление), разгон.");
lines.add("• Триггер: настройка запуска редстоуном, кликом или таймером.");
lines.add("• Зона: музыка включается при входе в зону и гаснет при выходе.");
lines.add("• Вид зоны: отображает 3D границы зоны прямо в игровом мире.");
@@ -1467,6 +1501,8 @@ public class SoundPlayerScreen extends ContainerScreen<SoundPlayerContainer> {
this.renderTooltip(matrixStack, new StringTextComponent("Вставить текущие координаты игрока как центр звука"), mouseX, mouseY);
} else if (this.zoneVisualizerButton != null && this.zoneVisualizerButton.isHovered()) {
this.renderTooltip(matrixStack, new StringTextComponent("Отображать 3D границы зоны в игровом мире"), mouseX, mouseY);
} else if (this.pitchModeButton != null && this.pitchModeButton.isHovered()) {
this.renderTooltip(matrixStack, new StringTextComponent("Динамическое изменение тона звука во времени (Вибрато, Глитч, Tape Stop и др.)"), mouseX, mouseY);
} else if (this.copyConfigButton != null && this.copyConfigButton.isHovered()) {
this.renderTooltip(matrixStack, new StringTextComponent("Скопировать все настройки блока в буфер обмена"), mouseX, mouseY);
} else if (this.pasteConfigButton != null && this.pasteConfigButton.isHovered()) {