using ImGuiNET; using T3.Core.Audio; using T3.Core.IO; using T3.Core.Settings; using T3.Editor.Gui.Audio; using T3.Editor.Gui.Input; using T3.Editor.Gui.Interaction; using T3.Editor.Gui.Styling; using T3.Editor.Gui.UiHelpers; using T3.Editor.UiModel.InputsAndTypes; namespace T3.Editor.Gui.Windows; internal sealed partial class SettingsWindow { private static float _smoothedGlobalLevel; private static float _smoothedOperatorLevel; private static float _smoothedSoundtrackLevel; private static float EdgePadding => 10 * T3Ui.UiScaleFactor; private static void DrawAudioPanel(ref bool changed) { FormInputs.AddVerticalSpace(); changed |= DrawMixerSection( "App Mixer", ref CoreSettings.Config.AppVolume, 0.0f, 1.0f, CoreSettings.Defaults.AppVolume, "Affects all audio output at the global mixer level.", AudioMixerManager.GetGlobalMixerLevel(), ref _smoothedGlobalLevel, ref CoreSettings.Config.AppMute, CoreSettings.Defaults.AppMute, "Mute all audio output at the global mixer level."); AudioEngine.SetGlobalMute(CoreSettings.Config.AppMute); var audio = CompositionSettings.Current.Audio; var audioDefaults = CompositionSettings.Defaults.Audio; changed |= DrawMixerSection( "Operator Mixer", ref audio.OperatorVolume, 0.0f, 1.0f, audioDefaults.OperatorVolume, "Affects all operator audio output at the operator mixer level.", AudioMixerManager.GetOperatorMixerLevel(), ref _smoothedOperatorLevel, ref audio.OperatorMute, audioDefaults.OperatorMute, "Mute all operator audio output at the operator mixer level."); AudioMixerManager.SetOperatorMixerVolume(audio.OperatorVolume); AudioEngine.SetOperatorMute(audio.OperatorMute); changed |= DrawMixerSection( "Soundtrack Mixer", ref audio.SoundtrackVolume, 0.0f, 10f, audioDefaults.SoundtrackVolume, "Limit the audio playback volume for the soundtrack", AudioMixerManager.GetSoundtrackMixerLevel(), ref _smoothedSoundtrackLevel, ref audio.SoundtrackMute, audioDefaults.SoundtrackMute, "Mute soundtrack audio only."); FormInputs.AddVerticalSpace(); FormInputs.AddSectionSubHeader("Default Input Device"); CustomComponents.HelpText("The WASAPI input device used when a project leaves its input set to \"" + AudioDeviceSelector.DefaultDeviceLabel + "\". Stored per machine, so shared projects stay portable."); FormInputs.DrawInputLabel("Device"); changed |= AudioDeviceSelector.DrawLocalDefaultDeviceCombo("##SettingsLocalAudioDevice"); } /// /// Draws a mixer section with volume slider, level meter, and mute controls. /// private static bool DrawMixerSection( string sectionLabel, ref float volume, float minVolume, float maxVolume, float defaultVolume, string volumeTooltip, float currentLevel, ref float smoothedLevel, ref bool mute, bool defaultMute, string muteTooltip) { var changed = false; ImGui.PushID(sectionLabel); DrawSectionHeader(sectionLabel); ImGui.SetCursorPosX(EdgePadding); ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Volume"); CustomComponents.TooltipForLastItem(volumeTooltip); ImGui.SameLine(0, 10 * T3Ui.UiScaleFactor); var resetButtonSize = ImGui.GetFrameHeight(); var muteTextWidth = ImGui.CalcTextSize("Mute").X + 30 * T3Ui.UiScaleFactor + resetButtonSize; var sliderWidth = 80 * T3Ui.UiScaleFactor; ImGui.SetNextItemWidth(sliderWidth); if (SingleValueEdit.Draw(ref volume, new Vector2(sliderWidth, ImGui.GetFrameHeight()), minVolume, maxVolume, true, true) != InputEditStateFlags.Nothing) { changed = true; } changed |= DrawResetButton(ref volume, defaultVolume, "Reset to default"); var contentMaxX = ImGui.GetWindowSize().X; ImGui.SameLine(contentMaxX - muteTextWidth - EdgePadding); if (ImGui.Checkbox("Mute", ref mute)) { changed = true; } CustomComponents.TooltipForLastItem(muteTooltip); ImGui.SameLine(); ImGui.PushID("muteReset"); changed |= DrawResetButton(ref mute, defaultMute, "Reset to default"); ImGui.PopID(); var windowPos = ImGui.GetWindowPos(); var meterLeftEdge = windowPos.X + EdgePadding; var meterRightEdge = windowPos.X + ImGui.GetWindowSize().X - EdgePadding; AudioLevelMeter.DrawAbsoluteWithinBounds("", currentLevel, ref smoothedLevel, 2f, meterLeftEdge, meterRightEdge); DrawSectionSeparator(); ImGui.PopID(); return changed; } private static void DrawSectionHeader(string label) { FormInputs.AddVerticalSpace(3); ImGui.PushFont(Fonts.FontSmall); ImGui.PushStyleColor(ImGuiCol.Text, UiColors.TextMuted.Rgba); ImGui.SetCursorPosX(EdgePadding); ImGui.TextUnformatted(label.ToUpperInvariant()); ImGui.PopStyleColor(); ImGui.PopFont(); FormInputs.AddVerticalSpace(2); } private static void DrawSectionSeparator() { FormInputs.AddVerticalSpace(6); var p = ImGui.GetCursorScreenPos(); var width = ImGui.GetContentRegionAvail().X - 2 * EdgePadding; ImGui.GetWindowDrawList().AddRectFilled( p + new Vector2(EdgePadding, 0), p + new Vector2(EdgePadding + width, 1), UiColors.ForegroundFull.Fade(0.05f)); FormInputs.AddVerticalSpace(6); } private static bool DrawResetButton(ref T value, T defaultValue, string tooltip) where T : IEquatable { ImGui.SameLine(); var isModified = !value.Equals(defaultValue); ImGui.PushStyleColor(ImGuiCol.Text, isModified ? UiColors.TextMuted.Rgba : UiColors.TextMuted.Fade(0.3f).Rgba); var clicked = CustomComponents.IconButton(Icon.Revert, new Vector2(ImGui.GetFrameHeight())); ImGui.PopStyleColor(); CustomComponents.TooltipForLastItem(tooltip); if (clicked) { value = defaultValue; return true; } return false; } }