diff options
| author | Lywx <kiritodev01@gmail.com> | 2026-07-02 20:42:38 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-02 20:42:38 -0600 |
| commit | 58527d7bf8778bc67179454a19ad8c84a74aff13 (patch) | |
| tree | 37df9df3eb558977b84b5a33b8143f3859c5d057 /src/ui/audio/SequenceDriver.cpp | |
| parent | b42a4fa16f7a75e2a96a7c107e6359c80b685863 (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.cpp | 121 |
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 |
