summaryrefslogtreecommitdiff
path: root/src/ui/audio/SequenceDriver.cpp
diff options
context:
space:
mode:
authorLywx <kiritodev01@gmail.com>2026-07-02 20:42:38 -0600
committerGitHub <noreply@github.com>2026-07-02 20:42:38 -0600
commit58527d7bf8778bc67179454a19ad8c84a74aff13 (patch)
tree37df9df3eb558977b84b5a33b8143f3859c5d057 /src/ui/audio/SequenceDriver.cpp
parentb42a4fa16f7a75e2a96a7c107e6359c80b685863 (diff)
Implemented UI Framework (#225)
* Implemented base renderer among main UI framework implementation * Implemented basic geo layout rendering * Implemented animation support among fixing lightning among other things * Implemented multiple ui factories for sm64 * Implemented first iteration of the custom sequence driver * Added export buttons on multiple factories * More sequence player fixes among f3dex2 working correctly * Implemented pm64 viewers * Implemented audio support for pm64 * Fully implemented pm64 factories * Implemented more sm64 factories * Added tons of missing sf64 factories * Implemented all assets among sf64 wrong padding * Run formatting * Fixed wrong spacing on gfx too * Fixed CI * Fixed bad pipeline
Diffstat (limited to 'src/ui/audio/SequenceDriver.cpp')
-rw-r--r--src/ui/audio/SequenceDriver.cpp121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/ui/audio/SequenceDriver.cpp b/src/ui/audio/SequenceDriver.cpp
new file mode 100644
index 0000000..af8a7ac
--- /dev/null
+++ b/src/ui/audio/SequenceDriver.cpp
@@ -0,0 +1,121 @@
+#ifdef BUILD_UI
+
+#include "SequenceDriver.h"
+
+#include <algorithm>
+#include <unordered_map>
+
+#include "ui/BaseBackend.h"
+#include "ui/ExportUtils.h"
+#include "ui/Widgets.h"
+
+namespace UI {
+namespace {
+
+RenderedAudio sRendered;
+std::string sRenderedName;
+int sRenderedOption = -1;
+std::string sPlayingName;
+std::unordered_map<std::string, int> sOptionChoice;
+
+bool RenderCached(SequenceDriver* driver, const ParseResultData& item, int option) {
+ if (sRenderedName == item.name && sRenderedOption == option) {
+ return !sRendered.pcm.empty();
+ }
+ sRendered = {};
+ sRenderedName = item.name;
+ sRenderedOption = option;
+ if (!driver->Render(item, option, sRendered)) {
+ sRendered = {};
+ return false;
+ }
+ sRendered.seconds = (double)sRendered.pcm.size() / 2.0 / kSynthRate;
+ return !sRendered.pcm.empty();
+}
+
+} // namespace
+
+float SequencePreviewUI::GetItemHeight(const ParseResultData&) {
+ return ImGui::GetTextLineHeightWithSpacing() * 3.0f + ImGui::GetFrameHeightWithSpacing() * 2.0f +
+ ImGui::GetStyle().ItemSpacing.y * 4.0f;
+}
+
+void SequencePreviewUI::DrawUI(const ParseResultData& item) {
+ UI::AssetHeader(item.name, item.type);
+ SequenceDriver* driver = GetSequenceDriver(item.type);
+ if (driver == nullptr || !item.data.has_value()) {
+ ImGui::TextDisabled(driver == nullptr ? "no sequence driver for this type" : "no data");
+ return;
+ }
+ ImGui::TextDisabled("sequence");
+
+ const std::vector<std::string> options = driver->Options(item);
+ auto [choiceIt, inserted] = sOptionChoice.try_emplace(item.name, 0);
+ if (inserted) {
+ choiceIt->second = driver->DefaultOption(item);
+ }
+ int& choice = choiceIt->second;
+ if (!options.empty()) {
+ choice = std::clamp(choice, 0, (int)options.size() - 1);
+ ImGui::SetNextItemWidth(std::min(ImGui::GetContentRegionAvail().x * 0.4f, 260.0f));
+ if (ImGui::BeginCombo("##seqopt", options[choice].c_str())) {
+ for (int i = 0; i < (int)options.size(); ++i) {
+ if (ImGui::Selectable(options[i].c_str(), i == choice)) {
+ choice = i;
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::SameLine();
+ }
+
+ const bool playingThis = sPlayingName == item.name && GetBackend()->AudioProgress() >= 0.0f;
+ if (ImGui::Button(playingThis ? "Stop##seq" : "Play##seq")) {
+ if (playingThis) {
+ GetBackend()->StopAudio();
+ sPlayingName.clear();
+ } else if (RenderCached(driver, item, choice)) {
+ GetBackend()->SetAudioSpeed(1.0f);
+ if (GetBackend()->PlaySamples(sRendered.pcm.data(), sRendered.pcm.size() / 2, kSynthRate, 2)) {
+ sPlayingName = item.name;
+ }
+ }
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("WAV##seqexp")) {
+ if (RenderCached(driver, item, choice)) {
+ const auto path = ExportFilePath(item.name, "wav");
+ NoteExport(item.name, WriteWavFile(path, sRendered.pcm.data(), sRendered.pcm.size() / 2, 2, kSynthRate)
+ ? path.string()
+ : "export failed");
+ } else {
+ NoteExport(item.name, "render failed");
+ }
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Export rendered sequence to torch-exports/");
+ }
+ DrawExportMarker(item.name);
+ ImGui::SameLine();
+ float volume = GetBackend()->GetAudioVolume();
+ ImGui::SetNextItemWidth(140.0f);
+ if (ImGui::SliderFloat("##seqvol", &volume, 0.0f, 1.0f, "vol %.2f")) {
+ GetBackend()->SetAudioVolume(volume);
+ }
+ ImGui::SameLine();
+ if (sRenderedName == item.name && !sRendered.pcm.empty()) {
+ ImGui::TextDisabled("%zu notes, %.1fs", sRendered.noteCount, sRendered.seconds);
+ } else {
+ ImGui::TextDisabled("press play to render");
+ }
+
+ float progress = playingThis ? std::max(GetBackend()->AudioProgress(), 0.0f) : 0.0f;
+ ImGui::SetNextItemWidth(std::min(ImGui::GetContentRegionAvail().x, 420.0f));
+ if (ImGui::SliderFloat("##seqseek", &progress, 0.0f, 1.0f, "") && playingThis) {
+ GetBackend()->SeekAudio(progress);
+ }
+}
+
+} // namespace UI
+
+#endif // BUILD_UI