| 1 |
#include "ImGuiSoundChip.hh" |
| 2 |
|
| 3 |
#include "ImGuiCpp.hh" |
| 4 |
#include "ImGuiManager.hh" |
| 5 |
#include "ImGuiUtils.hh" |
| 6 |
|
| 7 |
#include "MSXMixer.hh" |
| 8 |
#include "MSXMotherBoard.hh" |
| 9 |
#include "SoundDevice.hh" |
| 10 |
#include "StringSetting.hh" |
| 11 |
|
| 12 |
#include <imgui.h> |
| 13 |
|
| 14 |
#include <algorithm> |
| 15 |
|
| 16 |
using namespace std::literals; |
| 17 |
|
| 18 |
|
| 19 |
namespace openmsx { |
| 20 |
|
| 21 |
void ImGuiSoundChip::save(ImGuiTextBuffer& buf) |
| 22 |
{ |
| 23 |
savePersistent(buf, *this, persistentElements); |
| 24 |
for (const auto& [name, enabled] : channels) { |
| 25 |
buf.appendf("showChannels.%s=%d\n", name.c_str(), enabled); |
| 26 |
} |
| 27 |
} |
| 28 |
|
| 29 |
void ImGuiSoundChip::loadLine(std::string_view name, zstring_view value) |
| 30 |
{ |
| 31 |
if (loadOnePersistent(name, value, *this, persistentElements)) { |
| 32 |
|
| 33 |
} else if (name.starts_with("showChannels.")) { |
| 34 |
channels[std::string(name.substr(13))] = StringOp::stringToBool(value); |
| 35 |
} |
| 36 |
} |
| 37 |
|
| 38 |
void ImGuiSoundChip::paint(MSXMotherBoard* motherBoard) |
| 39 |
{ |
| 40 |
if (!motherBoard) return; |
| 41 |
if (showSoundChipSettings) { |
| 42 |
showChipSettings(*motherBoard); |
| 43 |
} |
| 44 |
|
| 45 |
|
| 46 |
const auto& msxMixer = motherBoard->getMSXMixer(); |
| 47 |
const auto& infos = msxMixer.getDeviceInfos(); |
| 48 |
for (const auto& info : infos) { |
| 49 |
const auto& name = info.device->getName(); |
| 50 |
auto [it, inserted] = channels.try_emplace(name, false); |
| 51 |
auto& enabled = it->second; |
| 52 |
if (enabled) { |
| 53 |
showChannelSettings(*motherBoard, name, &enabled); |
| 54 |
} |
| 55 |
} |
| 56 |
} |
| 57 |
|
| 58 |
[[nodiscard]] static bool anySpecialChannelSettings(const MSXMixer::SoundDeviceInfo& info) |
| 59 |
{ |
| 60 |
return std::ranges::any_of(info.channelSettings, [&](const auto& channel) { |
| 61 |
return channel.mute->getBoolean() || !channel.record->getString().empty(); |
| 62 |
}); |
| 63 |
} |
| 64 |
|
| 65 |
void ImGuiSoundChip::showChipSettings(MSXMotherBoard& motherBoard) |
| 66 |
{ |
| 67 |
auto restoreDefaultPopup = [](const char* label, Setting& setting) { |
| 68 |
im::PopupContextItem([&]{ |
| 69 |
if (ImGui::Button(label)) { |
| 70 |
setting.setValue(setting.getDefaultValue()); |
| 71 |
ImGui::CloseCurrentPopup(); |
| 72 |
} |
| 73 |
}); |
| 74 |
}; |
| 75 |
|
| 76 |
im::Window("Sound chip settings", &showSoundChipSettings, [&]{ |
| 77 |
int tableFlags = ImGuiTableFlags_RowBg | |
| 78 |
ImGuiTableFlags_BordersV | |
| 79 |
ImGuiTableFlags_BordersOuter | |
| 80 |
ImGuiTableFlags_Reorderable | |
| 81 |
ImGuiTableFlags_Hideable | |
| 82 |
ImGuiTableFlags_NoHostExtendX; |
| 83 |
im::Table("table", 4, tableFlags, [&]{ |
| 84 |
ImGui::TableSetupColumn("Device", ImGuiTableColumnFlags_WidthStretch); |
| 85 |
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 10.0f * ImGui::GetFontSize()); |
| 86 |
ImGui::TableSetupColumn("Balance", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 6.0f * ImGui::GetFontSize()); |
| 87 |
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 5.0f * ImGui::GetFontSize()); |
| 88 |
ImGui::TableHeadersRow(); |
| 89 |
|
| 90 |
const auto& msxMixer = motherBoard.getMSXMixer(); |
| 91 |
const auto& infos = msxMixer.getDeviceInfos(); |
| 92 |
im::ID_for_range(infos.size(), [&](int i) { |
| 93 |
const auto& info = infos[i]; |
| 94 |
if (ImGui::TableNextColumn()) { |
| 95 |
const auto& device = *info.device; |
| 96 |
ImGui::TextUnformatted(device.getName()); |
| 97 |
simpleToolTip(device.getDescription()); |
| 98 |
} |
| 99 |
if (ImGui::TableNextColumn()) { |
| 100 |
auto& volumeSetting = *info.volumeSetting; |
| 101 |
int volume = volumeSetting.getInt(); |
| 102 |
int min = volumeSetting.getMinValue(); |
| 103 |
int max = volumeSetting.getMaxValue(); |
| 104 |
ImGui::SetNextItemWidth(-FLT_MIN); |
| 105 |
if (ImGui::SliderInt("##volume", &volume, min, max)) { |
| 106 |
volumeSetting.setInt(volume); |
| 107 |
} |
| 108 |
restoreDefaultPopup("Set default", volumeSetting); |
| 109 |
} |
| 110 |
if (ImGui::TableNextColumn()) { |
| 111 |
auto& balanceSetting = *info.balanceSetting; |
| 112 |
int balance = balanceSetting.getInt(); |
| 113 |
int min = balanceSetting.getMinValue(); |
| 114 |
int max = balanceSetting.getMaxValue(); |
| 115 |
ImGui::SetNextItemWidth(-FLT_MIN); |
| 116 |
if (ImGui::SliderInt("##balance", &balance, min, max)) { |
| 117 |
balanceSetting.setInt(balance); |
| 118 |
} |
| 119 |
restoreDefaultPopup("Set center", balanceSetting); |
| 120 |
} |
| 121 |
if (ImGui::TableNextColumn()) { |
| 122 |
bool special = anySpecialChannelSettings(info); |
| 123 |
if (special) { |
| 124 |
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, getColor(imColor::YELLOW)); |
| 125 |
} |
| 126 |
const auto& name = info.device->getName(); |
| 127 |
auto [it, inserted] = channels.try_emplace(name, false); |
| 128 |
auto& enabled = it->second; |
| 129 |
im::Indent([&]{ |
| 130 |
ImGui::Checkbox("##channels", &enabled); |
| 131 |
}); |
| 132 |
} |
| 133 |
}); |
| 134 |
}); |
| 135 |
}); |
| 136 |
} |
| 137 |
|
| 138 |
void ImGuiSoundChip::showChannelSettings(MSXMotherBoard& motherBoard, const std::string& name, bool* enabled) |
| 139 |
{ |
| 140 |
const auto& msxMixer = motherBoard.getMSXMixer(); |
| 141 |
const auto* info = msxMixer.findDeviceInfo(name); |
| 142 |
if (!info) return; |
| 143 |
|
| 144 |
auto label = tmpStrCat(name, " channel settings"); |
| 145 |
ImGui::SetNextWindowSize(gl::vec2{40, 0} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver); |
| 146 |
im::Window(label.c_str(), enabled, [&]{ |
| 147 |
const auto& hotKey = manager.getReactor().getHotKey(); |
| 148 |
int flags = ImGuiTableFlags_ScrollY | |
| 149 |
ImGuiTableFlags_RowBg | |
| 150 |
ImGuiTableFlags_SizingStretchProp; |
| 151 |
im::Table("table", 3, flags, [&]{ |
| 152 |
ImGui::TableSetupScrollFreeze(0, 1); |
| 153 |
ImGui::TableSetupColumn("ch.", ImGuiTableColumnFlags_WidthFixed); |
| 154 |
ImGui::TableSetupColumn("mute", ImGuiTableColumnFlags_WidthFixed); |
| 155 |
ImGui::TableSetupColumn("file to record to", ImGuiTableColumnFlags_WidthStretch); |
| 156 |
ImGui::TableHeadersRow(); |
| 157 |
im::ID_for_range(info->channelSettings.size(), [&](int i) { |
| 158 |
const auto& channel = info->channelSettings[i]; |
| 159 |
if (ImGui::TableNextColumn()) { |
| 160 |
ImGui::StrCat(i + 1); |
| 161 |
} |
| 162 |
if (ImGui::TableNextColumn()) { |
| 163 |
Checkbox(hotKey, "##mute", *channel.mute); |
| 164 |
} |
| 165 |
if (ImGui::TableNextColumn()) { |
| 166 |
|
| 167 |
ImGui::SetNextItemWidth(-FLT_MIN); |
| 168 |
InputText("##rec", *channel.record); |
| 169 |
} |
| 170 |
}); |
| 171 |
}); |
| 172 |
}); |
| 173 |
} |
| 174 |
|
| 175 |
} |