72 strncpy(name_buf, instrument.
name.c_str(),
sizeof(name_buf));
73 if (ImGui::InputText(
"Name", name_buf,
sizeof(name_buf))) {
74 instrument.
name = name_buf;
83 if (ImGui::IsItemHovered()) {
84 ImGui::SetTooltip(
"Play a C4 note with this instrument (requires ROM loaded)");
87 ImGui::BeginDisabled();
90 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
91 ImGui::SetTooltip(
"Preview not available - load a ROM first");
96 if (ImGui::BeginCombo(
"Sample", absl::StrFormat(
"%02X", instrument.
sample_index).c_str())) {
100 std::string label = absl::StrFormat(
"%02X: %s", i, sample ? sample->name.c_str() :
"Unknown");
101 if (ImGui::Selectable(label.c_str(), is_selected)) {
105 if (is_selected) ImGui::SetItemDefaultFocus();
110 HelpMarker(
"The BRR sample used by this instrument.");
114 if (ImGui::InputInt(
"Pitch Multiplier", &pitch, 1, 16, ImGuiInputTextFlags_CharsHexadecimal)) {
115 instrument.
pitch_mult =
static_cast<uint16_t
>(std::clamp(pitch, 0, 0xFFFF));
119 HelpMarker(
"Base pitch adjustment. $1000 = 1.0x (Standard C). Lower values lower the pitch.");
122 ImGui::Text(
"Envelope (ADSR)");
124 HelpMarker(
"Attack, Decay, Sustain, Release envelope controls.");
128 int attack = instrument.
attack;
129 if (ImGui::SliderInt(
"Attack Rate", &attack, 0, 15)) {
130 instrument.
attack =
static_cast<uint8_t
>(attack);
133 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"How fast the volume reaches peak. 15 = Fastest (Instant), 0 = Slowest.");
136 int decay = instrument.
decay;
137 if (ImGui::SliderInt(
"Decay Rate", &decay, 0, 7)) {
138 instrument.
decay =
static_cast<uint8_t
>(decay);
141 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"How fast volume drops from peak to Sustain Level. 7 = Fastest, 0 = Slowest.");
145 if (ImGui::SliderInt(
"Sustain Level", &sustain_level, 0, 7)) {
146 instrument.
sustain_level =
static_cast<uint8_t
>(sustain_level);
149 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"The volume level (1/8ths) to sustain at. 7 = Max Volume, 0 = Silence.");
153 if (ImGui::SliderInt(
"Sustain Rate", &sustain_rate, 0, 31)) {
154 instrument.
sustain_rate =
static_cast<uint8_t
>(sustain_rate);
157 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"How fast volume decays WHILE holding the key (after reaching Sustain Level). 0 = Infinite sustain, 31 = Fast fade out.");
190 float attack_time = 1.0f / (instrument.
attack + 1.0f);
191 if (instrument.
attack == 15) attack_time = 0.0f;
197 plot_x_.push_back(attack_time);
208 for (
int i = 0; i < 20; ++i) {
212 float alpha = (float)i / 20.0f;
213 float curve = alpha * alpha;
214 float vol = 1.0f - (1.0f - s_level) * curve;
222 float sustain_time = 1.0f;
223 float sustain_drop_per_sec = instrument.
sustain_rate / 31.0f;
225 plot_x_.push_back(t + sustain_time);
226 plot_y_.push_back(std::max(0.0f, s_level - sustain_drop_per_sec));
228 if (ImPlot::BeginPlot(
"ADSR Envelope", ImVec2(-1, 200))) {
229 ImPlot::SetupAxes(
"Time",
"Volume");
230 ImPlot::SetupAxesLimits(0, 2.0, 0, 1.1);
234 ImPlot::TagX(attack_time, ImVec4(1,1,0,0.5),
"Decay Start");