78 strncpy(name_buf, instrument.
name.c_str(),
sizeof(name_buf));
79 if (ImGui::InputText(tr(
"Name"), name_buf,
sizeof(name_buf))) {
80 instrument.
name = name_buf;
89 if (ImGui::IsItemHovered()) {
91 tr(
"Play a C4 note with this instrument (requires ROM loaded)"));
94 ImGui::BeginDisabled();
97 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
98 ImGui::SetTooltip(tr(
"Preview not available - load a ROM first"));
103 if (ImGui::BeginCombo(
105 absl::StrFormat(
"%02X", instrument.
sample_index).c_str())) {
109 std::string label = absl::StrFormat(
110 "%02X: %s", i, sample ? sample->name.c_str() :
"Unknown");
111 if (ImGui::Selectable(label.c_str(), is_selected)) {
116 ImGui::SetItemDefaultFocus();
121 HelpMarker(
"The BRR sample used by this instrument.");
125 if (ImGui::InputInt(tr(
"Pitch Multiplier"), &pitch, 1, 16,
126 ImGuiInputTextFlags_CharsHexadecimal)) {
127 instrument.
pitch_mult =
static_cast<uint16_t
>(std::clamp(pitch, 0, 0xFFFF));
132 "Base pitch adjustment. $1000 = 1.0x (Standard C). Lower values lower "
136 ImGui::Text(tr(
"Envelope (ADSR)"));
138 HelpMarker(
"Attack, Decay, Sustain, Release envelope controls.");
142 int attack = instrument.
attack;
143 if (ImGui::SliderInt(tr(
"Attack Rate"), &attack, 0, 15)) {
144 instrument.
attack =
static_cast<uint8_t
>(attack);
147 if (ImGui::IsItemHovered())
149 tr(
"How fast the volume reaches peak. 15 = Fastest (Instant), 0 = "
153 int decay = instrument.
decay;
154 if (ImGui::SliderInt(tr(
"Decay Rate"), &decay, 0, 7)) {
155 instrument.
decay =
static_cast<uint8_t
>(decay);
158 if (ImGui::IsItemHovered())
160 tr(
"How fast volume drops from peak to Sustain Level. 7 = Fastest, 0 = "
165 if (ImGui::SliderInt(tr(
"Sustain Level"), &sustain_level, 0, 7)) {
166 instrument.
sustain_level =
static_cast<uint8_t
>(sustain_level);
169 if (ImGui::IsItemHovered())
171 tr(
"The volume level (1/8ths) to sustain at. 7 = Max Volume, 0 = "
176 if (ImGui::SliderInt(tr(
"Sustain Rate"), &sustain_rate, 0, 31)) {
177 instrument.
sustain_rate =
static_cast<uint8_t
>(sustain_rate);
180 if (ImGui::IsItemHovered())
181 ImGui::SetTooltip(tr(
182 "How fast volume decays WHILE holding the key (after reaching Sustain "
183 "Level). 0 = Infinite sustain, 31 = Fast fade out."));
216 float attack_time = 1.0f / (instrument.
attack + 1.0f);
217 if (instrument.
attack == 15)
224 plot_x_.push_back(attack_time);
235 for (
int i = 0; i < 20; ++i) {
239 float alpha = (float)i / 20.0f;
240 float curve = alpha * alpha;
241 float vol = 1.0f - (1.0f - s_level) * curve;
249 float sustain_time = 1.0f;
250 float sustain_drop_per_sec = instrument.
sustain_rate / 31.0f;
252 plot_x_.push_back(t + sustain_time);
253 plot_y_.push_back(std::max(0.0f, s_level - sustain_drop_per_sec));
255 if (ImPlot::BeginPlot(
"ADSR Envelope", ImVec2(-1, 200))) {
256 ImPlot::SetupAxes(
"Time",
"Volume");
257 ImPlot::SetupAxesLimits(0, 2.0, 0, 1.1);
259 static_cast<int>(
plot_x_.size()));
262 ImPlot::TagX(attack_time, ImVec4(1, 1, 0, 0.5),
"Decay Start");