Skip to content

Commit

Permalink
MIDI Program Change Support (#7171)
Browse files Browse the repository at this point in the history
Addresses #576

I might even go as so far as to say we are done with #576 with this
commit but I'm sure there will be comments and changes still. So here's
how it works

1. We add a new directory %USER%/Patches/MIDI Programs
2. We add a new data structure `patchIdToMidiBankAndProgram[128][128]` which
   for a bank and program is used for a patch id
3. We populate it as follows

   a. If `MIDI Programs` has top level patches those become bank 0
   b. If `MIDI Programs` has directories, those fill in order. So a
      just-directories MIDI programs has bank 0 as the first directory
      and a mix and match has bank 0 as the raw top level and bank 1
      as the first directory
   c. If there's still banks to be filled we fill them one at a time
      from factor. So if you have 6 directories in MIDI Programs,
      bank 7 will become 'Basses' (the first factory)

By 'fill' we mean traverse the category in alphabetical order and
set the patches as programs, up to 128

Banks with < 128 patches don't respond to PCH for the empty slots
Categories with > 128 patches only map the first 128
  • Loading branch information
baconpaul authored Aug 11, 2023
1 parent 7444983 commit 3f72558
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 2 deletions.
72 changes: 72 additions & 0 deletions src/common/SurgeStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ SurgeStorage::SurgeStorage(const SurgeStorage::SurgeStorageConfig &config) : oth

// append separator if not present
userPatchesPath = userDataPath / "Patches";
userPatchesMidiProgramChangePath = userPatchesPath / midiProgramChangePatchesSubdir;
userWavetablesPath = userDataPath / "Wavetables";
userWavetablesExportPath = userWavetablesPath / "Exported";
userFXPath = userDataPath / "FX Presets";
Expand Down Expand Up @@ -557,6 +558,19 @@ void SurgeStorage::createUserDirectory()
reportError(e.what(), "Unable to set up User Directory");
}
}

// Special case - MIDI Program Changes came later
if (!fs::exists(userPatchesMidiProgramChangePath))
{
try
{
fs::create_directories(userPatchesMidiProgramChangePath);
}
catch (const fs::filesystem_error &e)
{
reportError(e.what(), "Unable to set up User Directory");
}
}
}

void SurgeStorage::initializePatchDb(bool force)
Expand Down Expand Up @@ -729,6 +743,64 @@ void SurgeStorage::refresh_patchlist()
else
p.isFavorite = false;
}

/*
* Update midi program change here
*/
int loadFactoryBanksInAt{0};

auto loadCategoryIntoBank = [this](int catid, int bk) {
int currProg = 0;
for (const auto &pd : patchOrdering)
{
auto &p = patch_list[pd];
if (p.category == catid)
{
patchIdToMidiBankAndProgram[bk][currProg] = pd;
currProg++;
if (currProg >= 128)
break;
}
}
};
int currBank = 0;
// TODO: Initialize the data structure with init patch everywhere
for (auto &a : patchIdToMidiBankAndProgram)
for (auto &p : a)
p = -1;

for (const auto &c : patch_category)
{
if (c.name == midiProgramChangePatchesSubdir)
{
if (c.numberOfPatchesInCatgory > 0)
{
loadCategoryIntoBank(c.internalid, currBank);
currBank++;
}

// And luckily .children are sorted
for (const auto &k : c.children)
{
loadCategoryIntoBank(k.internalid, currBank);
currBank++;
if (currBank >= 128)
break;
}
}
}

if (currBank < 128)
{
for (auto c : patchCategoryOrdering)
{
loadCategoryIntoBank(c, currBank);

currBank++;
if (currBank >= 128)
break;
}
}
}

void SurgeStorage::refreshPatchlistAddDir(bool userDir, string subdir)
Expand Down
4 changes: 4 additions & 0 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,7 @@ class alignas(16) SurgeStorage
int firstUserCategory;
std::vector<int> patchOrdering;
std::vector<int> patchCategoryOrdering;
std::array<std::array<int, 128>, 128> patchIdToMidiBankAndProgram;

// The in-memory wavetable database
std::vector<Patch> wt_list;
Expand All @@ -1372,6 +1373,7 @@ class alignas(16) SurgeStorage
fs::path userDefaultFilePath;
fs::path userDataPath;
fs::path userPatchesPath;
fs::path userPatchesMidiProgramChangePath;
fs::path userWavetablesPath;
fs::path userModulatorSettingsPath;
fs::path userFXPath;
Expand All @@ -1380,6 +1382,8 @@ class alignas(16) SurgeStorage
fs::path userMidiMappingsPath;
fs::path extraThirdPartyWavetablesPath; // used by rack

std::string midiProgramChangePatchesSubdir{"MIDI Programs"};

std::map<std::string, TiXmlDocument> userMidiMappingsXMLByName;
void rescanUserMidiMappings();
void loadMidiMappingByName(std::string name);
Expand Down
6 changes: 4 additions & 2 deletions src/common/SurgeSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1929,8 +1929,10 @@ void SurgeSynthesizer::programChange(char channel, int value)
return;

PCH = value;
// load_patch((CC0<<7) + PCH);
patchid_queue = (CC0 << 7) + PCH;

auto pid = storage.patchIdToMidiBankAndProgram[CC0][PCH];
if (pid >= 0)
patchid_queue = pid;
}

void SurgeSynthesizer::updateDisplay()
Expand Down

0 comments on commit 3f72558

Please sign in to comment.