sfizz/sources/Synth.cpp
2019-08-04 01:38:31 +02:00

147 lines
4.3 KiB
C++

#include "Synth.h"
#include "Helpers.h"
#include <iostream>
#include <utility>
#include <algorithm>
void sfz::Synth::callback(std::string_view header, std::vector<Opcode> members)
{
switch (hash(header))
{
case hash("global"):
// We shouldn't have multiple global headers in file
ASSERT(!hasGlobal);
globalOpcodes = std::move(members);
hasGlobal = true;
break;
case hash("control"):
// We shouldn't have multiple control headers in file
ASSERT(!hasControl)
hasControl = true;
break;
case hash("master"):
masterOpcodes = std::move(members);
numMasters++;
break;
case hash("group"):
groupOpcodes = std::move(members);
numGroups++;
break;
case hash("region"):
buildRegion(members);
break;
case hash("curve"):
// TODO: implement curves
numCurves++;
break;
case hash("effect"):
// TODO: implement curves
break;
default:
std::cerr << "Unknown header: " << header << '\n';
}
}
void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
{
auto lastRegion = std::make_shared<Region>(filePool);
auto parseOpcodes = [&](const auto& opcodes) {
for (auto& opcode: opcodes)
if (!lastRegion->parseOpcode(opcode))
unknownOpcodes.insert(opcode.opcode);
};
parseOpcodes(globalOpcodes);
parseOpcodes(masterOpcodes);
parseOpcodes(groupOpcodes);
parseOpcodes(regionOpcodes);
regions.push_back(lastRegion);
}
void sfz::Synth::clear()
{
hasGlobal = false;
hasControl = false;
numGroups = 0;
numMasters = 0;
numCurves = 0;
defaultSwitch = std::nullopt;
for (auto& state: ccState)
state = 0;
ccNames.clear();
globalOpcodes.clear();
masterOpcodes.clear();
groupOpcodes.clear();
regions.clear();
}
void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members)
{
for (auto& member: members)
{
if (member.opcode == "sw_default")
setValueFromOpcode(member, defaultSwitch, Default::keyRange);
}
}
void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
{
for (auto& member: members)
{
switch (hash(member.opcode))
{
case hash("set_cc"):
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
setValueFromOpcode(member, ccState[*member.parameter], Default::ccRange);
break;
case hash("label_cc"):
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
ccNames.emplace_back(*member.parameter, member.value);
break;
case hash("default_path"):
if (auto newPath = std::filesystem::path(member.value); std::filesystem::exists(newPath))
rootDirectory = newPath;
break;
default:
// Unsupported control opcode
ASSERTFALSE;
}
}
}
bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
{
clear();
auto parserReturned = sfz::Parser::loadSfzFile(filename);
if (!parserReturned)
return false;
if (regions.empty())
return false;
filePool.setRootDirectory(this->rootDirectory);
auto lastRegion = regions.end() - 1;
auto currentRegion = regions.begin();
while (currentRegion <= lastRegion)
{
if (! (*currentRegion)->prepare() )
{
DBG("Removing the region with sample " << (*currentRegion)->sample);
std::iter_swap(currentRegion, lastRegion);
lastRegion--;
}
else
{
for (auto note = (*currentRegion)->keyRange.getStart(); note <= (*currentRegion)->keyRange.getEnd(); note++)
noteActivationLists[note].push_back(*currentRegion);
for (auto cc = (*currentRegion)->keyRange.getStart(); cc <= (*currentRegion)->keyRange.getEnd(); cc++)
ccActivationLists[cc].push_back(*currentRegion);
currentRegion++;
}
}
DBG("Removed " << regions.size() - std::distance(regions.begin(), lastRegion) - 1 << " out of " << regions.size() << " regions.");
regions.resize(std::distance(regions.begin(), lastRegion) + 1);
return parserReturned;
}