diff --git a/src-ui/app/edit-screen/components/GroupZoneTreeControl.h b/src-ui/app/edit-screen/components/GroupZoneTreeControl.h index 51d3c3e4..8c686c3b 100644 --- a/src-ui/app/edit-screen/components/GroupZoneTreeControl.h +++ b/src-ui/app/edit-screen/components/GroupZoneTreeControl.h @@ -33,6 +33,7 @@ #include "sst/jucegui/components/GlyphButton.h" #include "sst/jucegui/components/Label.h" +#include "engine/feature_enums.h" namespace scxt::ui::app::edit_screen { @@ -53,7 +54,7 @@ template struct GroupZoneListBoxModel : juce:: auto &pgz = sidebar->partGroupSidebar->pgzStructure; thisGroup.clear(); for (const auto &el : pgz) - if (el.first.part == sidebar->editor->selectedPart && el.first.group >= 0) + if (el.address.part == sidebar->editor->selectedPart && el.address.group >= 0) thisGroup.push_back(el); } int getNumRows() override { return thisGroup.size() + 1; /* for the plus */ } @@ -74,7 +75,7 @@ template struct GroupZoneListBoxModel : juce:: auto &tgl = thisGroup; if (rowNumber < 0 || rowNumber >= tgl.size()) return {}; - auto &sad = tgl[rowNumber].first; + auto &sad = tgl[rowNumber].address; return sad; } @@ -112,7 +113,7 @@ template struct GroupZoneListBoxModel : juce:: const auto &sg = tgl[rowNumber]; - bool isLeadZone = isZone() && gsb->isLeadZone(sg.first); + bool isLeadZone = isZone() && gsb->isLeadZone(sg.address); auto editor = gsb->partGroupSidebar->editor; @@ -159,7 +160,7 @@ template struct GroupZoneListBoxModel : juce:: } } - if (sg.first.zone < 0) + if (sg.address.zone < 0) { g.setColour(fillColor); g.fillRect(getLocalBounds()); @@ -170,10 +171,10 @@ template struct GroupZoneListBoxModel : juce:: auto bx = getLocalBounds().withWidth(grouplabelPad); auto nb = getLocalBounds().withTrimmedLeft(grouplabelPad); g.setColour(lowTextColor); - g.drawText(std::to_string(sg.first.group + 1), bx, + g.drawText(std::to_string(sg.address.group + 1), bx, juce::Justification::centredLeft); g.setColour(textColor); - g.drawText(sg.second, nb, juce::Justification::centredLeft); + g.drawText(sg.name, nb, juce::Justification::centredLeft); if (dragOverState == DRAG_OVER) { @@ -201,7 +202,12 @@ template struct GroupZoneListBoxModel : juce:: g.drawLine(zonePad, getHeight(), getWidth(), getHeight()); g.setColour(textColor); - g.drawText(sg.second, getLocalBounds().translated(zonePad + 2, 0), + if (sg.features & engine::ZoneFeatures::MISSING_SAMPLE) + { + g.setColour(editor->themeColor(theme::ColorMap::warning_1a)); + } + + g.drawText(sg.name, getLocalBounds().translated(zonePad + 2, 0), juce::Justification::centredLeft); if (isLeadZone) @@ -266,7 +272,7 @@ template struct GroupZoneListBoxModel : juce:: const auto &sg = tgl[rowNumber]; - p.addSectionHeader(sg.second); + p.addSectionHeader(sg.name); p.addSeparator(); p.addItem("Rename", [w = juce::Component::SafePointer(this)]() { if (!w) @@ -368,12 +374,12 @@ template struct GroupZoneListBoxModel : juce:: const auto &tgl = lbm->thisGroup; const auto &sg = tgl[rowNumber]; - assert(sg.first.zone < 0); + assert(sg.address.zone < 0); auto st = gsb->partGroupSidebar->style(); auto groupFont = gsb->editor->themeApplier.interRegularFor(11); renameEditor->setFont(groupFont); renameEditor->applyFontToAllText(groupFont); - renameEditor->setText(sg.second); + renameEditor->setText(sg.name); renameEditor->setSelectAllWhenFocused(true); renameEditor->setIndents(2, 1); renameEditor->setVisible(true); @@ -389,7 +395,7 @@ template struct GroupZoneListBoxModel : juce:: auto zoneFont = gsb->editor->themeApplier.interLightFor(11); renameEditor->setFont(zoneFont); renameEditor->applyFontToAllText(zoneFont); - renameEditor->setText(sg.second); + renameEditor->setText(sg.name); renameEditor->setSelectAllWhenFocused(true); renameEditor->setIndents(2, 1); renameEditor->setVisible(true); diff --git a/src-ui/app/edit-screen/components/PartGroupSidebar.cpp b/src-ui/app/edit-screen/components/PartGroupSidebar.cpp index d0b7d90a..f5a80035 100644 --- a/src-ui/app/edit-screen/components/PartGroupSidebar.cpp +++ b/src-ui/app/edit-screen/components/PartGroupSidebar.cpp @@ -153,7 +153,7 @@ struct GroupZoneSidebarBase : juce::Component, HasEditor, juce::DragAndDropConta int selR = -1; for (const auto &[i, r] : sst::cpputils::enumerate(listBoxModel->thisGroup)) { - if (r.first == a) + if (r.address == a) { rows.addRange({(int)i, (int)(i + 1)}); } diff --git a/src-ui/app/edit-screen/components/mapping-pane/MappingDisplay.cpp b/src-ui/app/edit-screen/components/mapping-pane/MappingDisplay.cpp index 0ef7a014..19e0d768 100644 --- a/src-ui/app/edit-screen/components/mapping-pane/MappingDisplay.cpp +++ b/src-ui/app/edit-screen/components/mapping-pane/MappingDisplay.cpp @@ -313,11 +313,11 @@ void MappingDisplay::setLeadSelection(const selection::SelectionManager::ZoneAdd bool foundZone{false}; for (const auto &s : summary) { - if (s.first == za) + if (s.address == za) { foundZone = true; if (mappingZones) - mappingZones->setLeadZoneBounds(s.second); + mappingZones->setLeadZoneBounds(s); } } diff --git a/src-ui/app/edit-screen/components/mapping-pane/ZoneLayoutDisplay.cpp b/src-ui/app/edit-screen/components/mapping-pane/ZoneLayoutDisplay.cpp index c616c9dc..0ce46ef5 100644 --- a/src-ui/app/edit-screen/components/mapping-pane/ZoneLayoutDisplay.cpp +++ b/src-ui/app/edit-screen/components/mapping-pane/ZoneLayoutDisplay.cpp @@ -28,6 +28,7 @@ #include "ZoneLayoutDisplay.h" #include "ZoneLayoutKeyboard.h" #include "MappingDisplay.h" +#include "engine/feature_enums.h" namespace scxt::ui::app::edit_screen { @@ -47,16 +48,16 @@ void ZoneLayoutDisplay::mouseDown(const juce::MouseEvent &e) for (auto &z : display->summary) { - auto r = rectangleForZone(z.second); + auto r = rectangleForZone(z); if (r.contains(e.position)) { - gotOne = display->editor->isSelected(z.first); + gotOne = display->editor->isSelected(z.address); if (!gotOne) { gotOne = true; - display->editor->doSelectionAction(z.first, true, false, true); + display->editor->doSelectionAction(z.address, true, false, true); } - za = z.first; + za = z.address; } } if (gotOne) @@ -121,10 +122,10 @@ void ZoneLayoutDisplay::mouseDown(const juce::MouseEvent &e) std::vector potentialZones; for (auto &z : display->summary) { - auto r = rectangleForZone(z.second); - if (r.contains(e.position) && display->editor->isAnyZoneFromGroupSelected(z.first.group)) + auto r = rectangleForZone(z); + if (r.contains(e.position) && display->editor->isAnyZoneFromGroupSelected(z.address.group)) { - potentialZones.push_back(z.first); + potentialZones.push_back(z.address); } } selection::SelectionManager::ZoneAddress nextZone; @@ -461,9 +462,9 @@ void ZoneLayoutDisplay::mouseUp(const juce::MouseEvent &e) const auto &sel = *(display->editor->currentLeadZoneSelection); for (const auto &z : display->summary) { - if (!(z.first == sel)) + if (!(z.address == sel)) continue; - if (rz.intersects(rectangleForZone(z.second))) + if (rz.intersects(rectangleForZone(z))) selectedLead = true; } } @@ -472,14 +473,14 @@ void ZoneLayoutDisplay::mouseUp(const juce::MouseEvent &e) bool first = true; for (const auto &z : display->summary) { - if (rz.intersects(rectangleForZone(z.second))) + if (rz.intersects(rectangleForZone(z))) { - display->editor->doSelectionAction(z.first, true, false, first && firstAsLead); + display->editor->doSelectionAction(z.address, true, false, first && firstAsLead); first = false; } else if (!additiveSelect) { - display->editor->doSelectionAction(z.first, false, false, false); + display->editor->doSelectionAction(z.address, false, false, false); } } } @@ -521,7 +522,8 @@ void ZoneLayoutDisplay::mouseUp(const juce::MouseEvent &e) juce::Rectangle ZoneLayoutDisplay::rectangleForZone(const engine::Part::zoneMappingItem_t &sum) { - const auto &[kb, vel, name] = sum; + const auto &kb = sum.kr; + const auto &vel = sum.vr; return rectangleForRange(kb.keyStart, kb.keyEnd, vel.velStart, vel.velEnd + 1); } @@ -641,26 +643,39 @@ void ZoneLayoutDisplay::paint(juce::Graphics &g) { for (const auto &z : display->summary) { - if (!display->editor->isAnyZoneFromGroupSelected(z.first.group)) + if (!display->editor->isAnyZoneFromGroupSelected(z.address.group)) continue; - if (display->editor->isSelected(z.first) != drawSelected) + if (display->editor->isSelected(z.address) != drawSelected) continue; - if (z.first == display->editor->currentLeadZoneSelection) + if (z.address == display->editor->currentLeadZoneSelection) continue; - auto r = rectangleForZone(z.second); + auto r = rectangleForZone(z); auto borderColor = editor->themeColor(theme::ColorMap::accent_2a); auto fillColor = editor->themeColor(theme::ColorMap::accent_2b).withAlpha(0.32f); auto textColor = editor->themeColor(theme::ColorMap::accent_2a); + if (z.features & engine::ZoneFeatures::MISSING_SAMPLE) + { + borderColor = editor->themeColor(theme::ColorMap::warning_1b); + fillColor = editor->themeColor(theme::ColorMap::warning_1b).withAlpha(0.32f); + textColor = editor->themeColor(theme::ColorMap::warning_1a); + } if (drawSelected) { borderColor = editor->themeColor(theme::ColorMap::accent_1b); fillColor = borderColor.withAlpha(0.32f); textColor = editor->themeColor(theme::ColorMap::accent_1a); + + if (z.features & engine::ZoneFeatures::MISSING_SAMPLE) + { + borderColor = editor->themeColor(theme::ColorMap::warning_1a); + fillColor = editor->themeColor(theme::ColorMap::warning_1a).withAlpha(0.32f); + textColor = editor->themeColor(theme::ColorMap::warning_1a); + } } g.setColour(fillColor); @@ -668,9 +683,9 @@ void ZoneLayoutDisplay::paint(juce::Graphics &g) g.setColour(borderColor); g.drawRect(r, 1.f); - labelZoneRectangle(g, r, std::get<2>(z.second), textColor); + labelZoneRectangle(g, r, z.name, textColor); - auto ct = display->voiceCountFor(z.first); + auto ct = display->voiceCountFor(z.address); drawVoiceMarkers(r, ct); } } @@ -681,12 +696,20 @@ void ZoneLayoutDisplay::paint(juce::Graphics &g) for (const auto &z : display->summary) { - if (!(z.first == sel)) + if (!(z.address == sel)) continue; - const auto &[kb, vel, name] = z.second; + const auto &kb = z.kr; + const auto &vel = z.vr; + const auto &name = z.name; auto selZoneColor = editor->themeColor(theme::ColorMap::accent_1a); + auto borderColor = selZoneColor; + if (z.features & engine::ZoneFeatures::MISSING_SAMPLE) + { + selZoneColor = editor->themeColor(theme::ColorMap::warning_1a); + borderColor = editor->themeColor(theme::ColorMap::accent_1a); + } auto c1{selZoneColor.withAlpha(0.f)}; auto c2{selZoneColor.withAlpha(0.5f)}; @@ -899,14 +922,13 @@ void ZoneLayoutDisplay::paint(juce::Graphics &g) } } } - auto r = rectangleForZone(z.second); - g.setColour(selZoneColor); + auto r = rectangleForZone(z); + g.setColour(borderColor); g.drawRect(r, 3.f); - labelZoneRectangle(g, r, std::get<2>(z.second), - editor->themeColor(theme::ColorMap::accent_1a)); + labelZoneRectangle(g, r, z.name, editor->themeColor(theme::ColorMap::accent_1a)); - auto ct = display->voiceCountFor(z.first); + auto ct = display->voiceCountFor(z.address); drawVoiceMarkers(r, ct); } } @@ -929,7 +951,7 @@ void ZoneLayoutDisplay::paint(juce::Graphics &g) for (const auto &z : display->summary) { - auto rz = rectangleForZone(z.second); + auto rz = rectangleForZone(z); if (rz.intersects(r)) { g.setColour(editor->themeColor(theme::ColorMap::generic_content_high)); diff --git a/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp b/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp index 5dc99ae9..c5e91f79 100644 --- a/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp +++ b/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp @@ -226,9 +226,9 @@ void SCXTEditor::onGroupZoneMappingSummary(const scxt::engine::Part::zoneMapping if constexpr (scxt::log::uiStructure) { SCLOG("Updated zone mapping summary"); - for (const auto &[addr, item] : d) + for (const auto &z : d) { - SCLOG(" " << addr << " " << std::get<2>(item)); + SCLOG(" " << z.address << " " << z.name); } } } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a9f8e72e..90dda697 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -54,6 +54,7 @@ #include #include #include "messaging/client/client_serial.h" +#include "feature_enums.h" namespace scxt::engine { @@ -452,7 +453,13 @@ Engine::pgzStructure_t Engine::getPartGroupZoneStructure() const int32_t zoneidx{0}; for (const auto &zone : *group) { - res.push_back({{partidx, groupidx, zoneidx}, zone->getName()}); + int32_t zoneFeatures{0}; + if (zone->missingSampleCount() > 0) + { + zoneFeatures |= ZoneFeatures::MISSING_SAMPLE; + } + + res.push_back({{partidx, groupidx, zoneidx}, zone->getName(), zoneFeatures}); zoneidx++; } @@ -466,7 +473,7 @@ Engine::pgzStructure_t Engine::getPartGroupZoneStructure() const SCLOG("Returning partgroup structure size " << res.size()); for (const auto &pg : res) - SCLOG(" " << pg.first << " @ " << pg.second); + SCLOG(" " << pg.address << " @ " << pg.name); } return res; diff --git a/src/engine/engine.h b/src/engine/engine.h index 25d6db38..933c0f1d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -448,8 +448,13 @@ struct Engine : MoveableOnly, SampleRateSupport const std::optional getProcessorStorage(const processorAddress_t &addr) const; - typedef std::vector> - pgzStructure_t; + struct PGZStructureBundle + { + selection::SelectionManager::ZoneAddress address; + std::string name; + int32_t features{0}; + }; + typedef std::vector pgzStructure_t; /** * Get the Part/Group/Zone structure as a set o fzone addreses. A part with * no groups will be (p,-1,-1); a group with no zones will be (p,g,-1). diff --git a/src/engine/feature_enums.h b/src/engine/feature_enums.h new file mode 100644 index 00000000..c2704c49 --- /dev/null +++ b/src/engine/feature_enums.h @@ -0,0 +1,16 @@ +// +// Created by Paul Walker on 9/24/24. +// + +#ifndef FEATURE_ENUMS_H +#define FEATURE_ENUMS_H + +namespace scxt::engine +{ +enum ZoneFeatures +{ + MISSING_SAMPLE = 1 << 0, +}; +} + +#endif // FEATURE_ENUMS_H diff --git a/src/engine/part.cpp b/src/engine/part.cpp index 02233508..d4a191ed 100644 --- a/src/engine/part.cpp +++ b/src/engine/part.cpp @@ -29,6 +29,7 @@ #include "bus.h" #include "patch.h" #include "engine.h" +#include "feature_enums.h" #include "selection/selection_manager.h" @@ -73,11 +74,16 @@ Part::zoneMappingSummary_t Part::getZoneMappingSummary() for (const auto &z : *g) { // get address for zone - auto data = - zoneMappingItem_t{z->mapping.keyboardRange, z->mapping.velocityRange, z->getName()}; auto addr = selection::SelectionManager::ZoneAddress(pidx, gidx, zidx); + int32_t features{0}; + if (z->missingSampleCount() > 0) + { + features |= ZoneFeatures::MISSING_SAMPLE; + } + auto data = zoneMappingItem_t{addr, z->mapping.keyboardRange, z->mapping.velocityRange, + z->getName(), features}; // res[addr] = data; - res.emplace_back(addr, data); + res.emplace_back(data); zidx++; } gidx++; diff --git a/src/engine/part.h b/src/engine/part.h index 6cb91ce8..b9b9f0f8 100644 --- a/src/engine/part.h +++ b/src/engine/part.h @@ -128,9 +128,16 @@ struct Part : MoveableOnly, SampleRateSupport /** * Utility data structures to allow rapid draws and displays of the structure in clients */ - typedef std::tuple zoneMappingItem_t; - typedef std::vector> - zoneMappingSummary_t; + struct ZoneMappingItem + { + selection::SelectionManager::ZoneAddress address; + KeyboardRange kr; + VelocityRange vr; + std::string name; + int32_t features{0}; + }; + using zoneMappingItem_t = ZoneMappingItem; // legacy name from when we were a tuple + typedef std::vector zoneMappingSummary_t; zoneMappingSummary_t getZoneMappingSummary(); // TODO GroupID -> index diff --git a/src/engine/zone.cpp b/src/engine/zone.cpp index 8f2f745a..46f6fb65 100644 --- a/src/engine/zone.cpp +++ b/src/engine/zone.cpp @@ -433,5 +433,24 @@ void Zone::onRoutingChanged() // << SCD(lfosActive[3]) << SCD(egsActive[0]) << SCD(egsActive[1])) } +int16_t Zone::missingSampleCount() const +{ + int idx{0}; + int ct{0}; + for (auto &sv : variantData.variants) + { + if (sv.active) + { + auto smp = samplePointers[idx]; + if (smp && smp->isMissingPlaceholder) + { + ct++; + } + } + idx++; + } + return ct; +} + template struct HasGroupZoneProcessors; } // namespace scxt::engine diff --git a/src/engine/zone.h b/src/engine/zone.h index 698a7885..91527e14 100644 --- a/src/engine/zone.h +++ b/src/engine/zone.h @@ -205,6 +205,8 @@ struct Zone : MoveableOnly, HasGroupZoneProcessors, SampleRateSuppor void setNormalizedSampleLevel(bool usePeak = false, int associatedSampleID = -1); void clearNormalizedSampleLevel(int associatedSampleID = -1); + int16_t missingSampleCount() const; + struct ZoneMappingData { int16_t rootKey{60}; diff --git a/src/json/engine_traits.h b/src/json/engine_traits.h index 783488eb..2361b538 100644 --- a/src/json/engine_traits.h +++ b/src/json/engine_traits.h @@ -56,7 +56,6 @@ namespace scxt::json { - SC_STREAMDEF(scxt::engine::Engine, SC_FROM({ if (SC_STREAMING_FOR_IN_PROCESS) { @@ -121,6 +120,15 @@ SC_STREAMDEF(scxt::engine::Patch, SC_FROM({ findIf(v, "busses", patch.busses); })) +SC_STREAMDEF(scxt::engine::Engine::PGZStructureBundle, SC_FROM({ + v = {{"address", t.address}, {"name", t.name}, {"features", t.features}}; + }), + SC_TO({ + findIf(v, "address", to.address); + findIf(v, "name", to.name); + findOrSet(v, "features", 0, to.features); + })); + SC_STREAMDEF(scxt::engine::Macro, SC_FROM({ v = {{"p", t.part}, {"i", t.index}, {"v", t.value}}; @@ -150,6 +158,20 @@ SC_STREAMDEF( findOrSet(v, "s", false, to.solo); })); +SC_STREAMDEF(scxt::engine::Part::ZoneMappingItem, + SC_FROM(v = {{"a", from.address}, + {"kr", from.kr}, + {"vr", from.vr}, + {"name", from.name}, + {"features", from.features}};), + SC_TO({ + findIf(v, "a", to.address); + findIf(v, "kr", to.kr); + findIf(v, "vr", to.vr); + findIf(v, "name", to.name); + findIf(v, "features", to.features); + })) + SC_STREAMDEF( scxt::engine::Part, SC_FROM({ // TODO: Do a non-empty part stream with the If variant diff --git a/src/sample/sample.cpp b/src/sample/sample.cpp index a1e053c3..5756fa87 100644 --- a/src/sample/sample.cpp +++ b/src/sample/sample.cpp @@ -464,4 +464,19 @@ void Sample::dumpInformationToLog() } } +std::shared_ptr Sample::createMissingPlaceholder(const Sample::SampleFileAddress &a) +{ + auto res = std::make_shared(); + res->mFileName = a.path; + res->md5Sum = a.md5sum; + res->type = a.type; + res->preset = a.preset; + res->instrument = a.instrument; + res->region = a.region; + res->isMissingPlaceholder = true; + res->displayName = fmt::format("Missing {}", a.path.filename().u8string()); + + return res; +} + } // namespace scxt::sample \ No newline at end of file diff --git a/src/sample/sample.h b/src/sample/sample.h index ede8b1c3..2f2af06e 100644 --- a/src/sample/sample.h +++ b/src/sample/sample.h @@ -76,6 +76,9 @@ struct alignas(16) Sample : MoveableOnly int region{-1}; }; + bool isMissingPlaceholder{false}; + static std::shared_ptr createMissingPlaceholder(const SampleFileAddress &a); + SampleFileAddress getSampleFileAddress() const { #if BUILD_IS_DEBUG diff --git a/src/sample/sample_manager.cpp b/src/sample/sample_manager.cpp index c919d615..f482b165 100644 --- a/src/sample/sample_manager.cpp +++ b/src/sample/sample_manager.cpp @@ -39,6 +39,7 @@ void SampleManager::restoreFromSampleAddressesAndIDs(const sampleAddressesAndIds if (!fs::exists(addr.path)) { missingList.push_back(addr.path); + addSampleAsMissing(id, addr); } else { @@ -265,4 +266,18 @@ SampleManager::getSampleAddressesFor(const std::vector &sids) const return res; } +void SampleManager::addSampleAsMissing(const SampleID &id, const Sample::SampleFileAddress &f) +{ + if (samples.find(id) == samples.end()) + { + auto ms = Sample::createMissingPlaceholder(f); + ms->id = id; + + SCLOG("Missing : " << f.path.u8string()); + SCLOG(" : " << id.to_string()); + + samples[id] = ms; + } +} + } // namespace scxt::sample diff --git a/src/sample/sample_manager.h b/src/sample/sample_manager.h index b4e7fddc..3c3d0747 100644 --- a/src/sample/sample_manager.h +++ b/src/sample/sample_manager.h @@ -76,6 +76,7 @@ struct SampleManager : MoveableOnly SampleManager(const ThreadingChecker &t) : threadingChecker(t) {} ~SampleManager(); + void addSampleAsMissing(const SampleID &id, const Sample::SampleFileAddress &f); std::optional loadSampleByFileAddress(const Sample::SampleFileAddress &); std::optional loadSampleByFileAddressToID(const Sample::SampleFileAddress &, const SampleID &); @@ -144,6 +145,7 @@ struct SampleManager : MoveableOnly auto ap = idAliases.find(a); if (ap == idAliases.end()) return a; + assert(ap->second != a); return resolveAlias(ap->second); } diff --git a/src/utils.h b/src/utils.h index f02ecf5e..7e46f3c3 100644 --- a/src/utils.h +++ b/src/utils.h @@ -96,16 +96,19 @@ struct SampleID { static constexpr size_t md5len{32}; char md5[md5len + 1]{}; - std::array multiAddress{-1, -1, -1}; + + static constexpr int unusedAddress{-1}; + std::array multiAddress{unusedAddress, unusedAddress, unusedAddress}; SampleID() { setAsInvalid(); } std::string to_string() const { - if (multiAddress[0] == -1) - return fmt::format("SampleID:{}", std::string(md5)); + std::string hd{"SampleID"}; + if (multiAddress[0] == unusedAddress) + return fmt::format("{}:{}", hd, std::string(md5)); else - return fmt::format("SampleID:{}@{}/{}/{}", std::string(md5), multiAddress[0], + return fmt::format("{}:{}@{}/{}/{}", hd, std::string(md5), multiAddress[0], multiAddress[1], multiAddress[2]); } @@ -121,13 +124,13 @@ struct SampleID void setAsInvalid() { memset(md5, 0, sizeof(md5)); - std::fill(multiAddress.begin(), multiAddress.end(), -1); + std::fill(multiAddress.begin(), multiAddress.end(), unusedAddress); md5[0] = '!'; } void setAsMD5(const std::string &m) { strncpy(md5, m.c_str(), md5len + 1); - std::fill(multiAddress.begin(), multiAddress.end(), -1); + std::fill(multiAddress.begin(), multiAddress.end(), unusedAddress); } void setAsLegacy(int oldId) { diff --git a/src/voice/voice.cpp b/src/voice/voice.cpp index 04b9d164..6a41348c 100644 --- a/src/voice/voice.cpp +++ b/src/voice/voice.cpp @@ -591,9 +591,6 @@ void Voice::initializeGenerator() { if (sampleIndex < 0) { - // For now we just null out the generator and deal but an alternate - // approach in the future would be to have a family of built in generators - // which we can run which just do DSP. Because honestly its a sampler. Generator = nullptr; GD.isFinished = false; monoGenerator = true; @@ -602,8 +599,16 @@ void Voice::initializeGenerator() // but the default of course is to use the sample index auto &s = zone->samplePointers[sampleIndex]; - auto &variantData = zone->variantData.variants[sampleIndex]; assert(s); + if (s->isMissingPlaceholder) + { + Generator = nullptr; + GD.isFinished = false; + monoGenerator = true; + return; + } + + auto &variantData = zone->variantData.variants[sampleIndex]; GDIO.outputL = output[0]; GDIO.outputR = output[1];