77 strncpy(name_buf, instrument.
name.c_str(),
sizeof(name_buf));
78 if (ImGui::InputText(
"Name", name_buf,
sizeof(name_buf))) {
79 instrument.
name = name_buf;
88 if (ImGui::IsItemHovered()) {
90 "Play a C4 note with this instrument (requires ROM loaded)");
93 ImGui::BeginDisabled();
96 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
97 ImGui::SetTooltip(
"Preview not available - load a ROM first");
102 if (ImGui::BeginCombo(
103 "Sample", absl::StrFormat(
"%02X", instrument.
sample_index).c_str())) {
107 std::string label = absl::StrFormat(
108 "%02X: %s", i, sample ? sample->name.c_str() :
"Unknown");
109 if (ImGui::Selectable(label.c_str(), is_selected)) {
114 ImGui::SetItemDefaultFocus();
119 HelpMarker(
"The BRR sample used by this instrument.");
123 if (ImGui::InputInt(
"Pitch Multiplier", &pitch, 1, 16,
124 ImGuiInputTextFlags_CharsHexadecimal)) {
125 instrument.
pitch_mult =
static_cast<uint16_t
>(std::clamp(pitch, 0, 0xFFFF));
130 "Base pitch adjustment. $1000 = 1.0x (Standard C). Lower values lower "
134 ImGui::Text(
"Envelope (ADSR)");
136 HelpMarker(
"Attack, Decay, Sustain, Release envelope controls.");
140 int attack = instrument.
attack;
141 if (ImGui::SliderInt(
"Attack Rate", &attack, 0, 15)) {
142 instrument.
attack =
static_cast<uint8_t
>(attack);
145 if (ImGui::IsItemHovered())
147 "How fast the volume reaches peak. 15 = Fastest (Instant), 0 = "
151 int decay = instrument.
decay;
152 if (ImGui::SliderInt(
"Decay Rate", &decay, 0, 7)) {
153 instrument.
decay =
static_cast<uint8_t
>(decay);
156 if (ImGui::IsItemHovered())
158 "How fast volume drops from peak to Sustain Level. 7 = Fastest, 0 = "
163 if (ImGui::SliderInt(
"Sustain Level", &sustain_level, 0, 7)) {
164 instrument.
sustain_level =
static_cast<uint8_t
>(sustain_level);
167 if (ImGui::IsItemHovered())
169 "The volume level (1/8ths) to sustain at. 7 = Max Volume, 0 = "
174 if (ImGui::SliderInt(
"Sustain Rate", &sustain_rate, 0, 31)) {
175 instrument.
sustain_rate =
static_cast<uint8_t
>(sustain_rate);
178 if (ImGui::IsItemHovered())
180 "How fast volume decays WHILE holding the key (after reaching Sustain "
181 "Level). 0 = Infinite sustain, 31 = Fast fade out.");
214 float attack_time = 1.0f / (instrument.
attack + 1.0f);
215 if (instrument.
attack == 15)
222 plot_x_.push_back(attack_time);
233 for (
int i = 0; i < 20; ++i) {
237 float alpha = (float)i / 20.0f;
238 float curve = alpha * alpha;
239 float vol = 1.0f - (1.0f - s_level) * curve;
247 float sustain_time = 1.0f;
248 float sustain_drop_per_sec = instrument.
sustain_rate / 31.0f;
250 plot_x_.push_back(t + sustain_time);
251 plot_y_.push_back(std::max(0.0f, s_level - sustain_drop_per_sec));
253 if (ImPlot::BeginPlot(
"ADSR Envelope", ImVec2(-1, 200))) {
254 ImPlot::SetupAxes(
"Time",
"Volume");
255 ImPlot::SetupAxesLimits(0, 2.0, 0, 1.1);
257 static_cast<int>(
plot_x_.size()));
260 ImPlot::TagX(attack_time, ImVec4(1, 1, 0, 0.5),
"Decay Start");