OXYGENHUB
◆ main openmsx / src / imgui / ImGuiSoundChip.cc
5.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 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
		// already handled
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
	// Show sound chip channel settings
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(); // TODO sort on name
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); // Make top row always visible
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
					// TODO: use a file browser (in "create mode")
167
					ImGui::SetNextItemWidth(-FLT_MIN);
168
					InputText("##rec", *channel.record);
169
				}
170
			});
171
		});
172
	});
173
}
174
175
} // namespace openmsx