sfizz/sources/Synth.cpp

170 lines
5.2 KiB
C++
Raw Normal View History

2019-07-29 02:13:03 +02:00
#include "Synth.h"
#include "Helpers.h"
2019-08-25 14:01:03 +02:00
#include <algorithm>
2019-07-29 02:13:03 +02:00
#include <iostream>
2019-08-04 01:38:31 +02:00
#include <utility>
2019-07-29 02:13:03 +02:00
void sfz::Synth::callback(std::string_view header, const std::vector<Opcode>& members)
2019-07-29 02:13:03 +02:00
{
2019-08-25 14:01:03 +02:00
switch (hash(header)) {
2019-07-29 02:13:03 +02:00
case hash("global"):
// We shouldn't have multiple global headers in file
ASSERT(!hasGlobal);
globalOpcodes = members;
2019-07-29 02:13:03 +02:00
hasGlobal = true;
break;
case hash("control"):
// We shouldn't have multiple control headers in file
ASSERT(!hasControl)
hasControl = true;
break;
case hash("master"):
masterOpcodes = members;
2019-07-29 02:13:03 +02:00
numMasters++;
break;
2019-08-25 14:01:03 +02:00
case hash("group"):
groupOpcodes = members;
2019-07-29 02:13:03 +02:00
numGroups++;
break;
case hash("region"):
buildRegion(members);
break;
case hash("curve"):
// TODO: implement curves
numCurves++;
break;
case hash("effect"):
2019-08-25 14:01:03 +02:00
// TODO: implement effects
2019-07-29 02:13:03 +02:00
break;
default:
std::cerr << "Unknown header: " << header << '\n';
}
}
void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
{
2019-08-23 01:27:39 +02:00
auto lastRegion = std::make_unique<Region>();
2019-08-25 14:01:03 +02:00
auto parseOpcodes = [&](const auto& opcodes) {
2019-08-25 14:01:03 +02:00
for (auto& opcode : opcodes)
2019-08-04 01:38:31 +02:00
if (!lastRegion->parseOpcode(opcode))
unknownOpcodes.insert(opcode.opcode);
};
parseOpcodes(globalOpcodes);
parseOpcodes(masterOpcodes);
parseOpcodes(groupOpcodes);
parseOpcodes(regionOpcodes);
2019-08-04 01:38:31 +02:00
2019-08-23 01:27:39 +02:00
regions.push_back(std::move(lastRegion));
2019-07-29 02:13:03 +02:00
}
void sfz::Synth::clear()
{
hasGlobal = false;
hasControl = false;
numGroups = 0;
numMasters = 0;
numCurves = 0;
defaultSwitch = std::nullopt;
2019-08-25 14:01:03 +02:00
for (auto& state : ccState)
2019-07-29 02:13:03 +02:00
state = 0;
ccNames.clear();
globalOpcodes.clear();
masterOpcodes.clear();
groupOpcodes.clear();
regions.clear();
}
void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members)
{
2019-08-25 14:01:03 +02:00
for (auto& member : members) {
2019-07-29 02:13:03 +02:00
if (member.opcode == "sw_default")
setValueFromOpcode(member, defaultSwitch, Default::keyRange);
}
}
void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
{
2019-08-25 14:01:03 +02:00
for (auto& member : members) {
switch (hash(member.opcode)) {
2019-07-29 02:13:03 +02:00
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;
2019-08-04 01:38:31 +02:00
break;
2019-07-29 02:13:03 +02:00
default:
// Unsupported control opcode
ASSERTFALSE;
}
}
}
bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
{
clear();
2019-08-04 01:38:31 +02:00
auto parserReturned = sfz::Parser::loadSfzFile(filename);
if (!parserReturned)
return false;
2019-08-25 14:01:03 +02:00
2019-08-04 01:38:31 +02:00
if (regions.empty())
return false;
filePool.setRootDirectory(this->rootDirectory);
2019-08-04 01:38:31 +02:00
auto lastRegion = regions.end() - 1;
auto currentRegion = regions.begin();
2019-08-25 14:01:03 +02:00
while (currentRegion <= lastRegion) {
2019-08-23 14:16:33 +02:00
auto region = currentRegion->get();
2019-08-25 14:01:03 +02:00
if (region->isGenerator()) {
currentRegion++;
continue;
2019-08-04 01:38:31 +02:00
}
auto fileInformation = filePool.getFileInformation(region->sample);
2019-08-25 14:01:03 +02:00
if (!fileInformation) {
DBG("Removing the region with sample " << region->sample);
std::iter_swap(currentRegion, lastRegion);
lastRegion--;
continue;
2019-08-04 01:38:31 +02:00
}
region->sampleEnd = std::min(region->sampleEnd, fileInformation->end);
region->loopRange.shrinkIfSmaller(fileInformation->loopBegin, fileInformation->loopEnd);
region->preloadedData = fileInformation->preloadedData;
region->sampleRate = fileInformation->sampleRate;
for (auto note = region->keyRange.getStart(); note <= region->keyRange.getEnd(); note++)
2019-08-23 01:27:39 +02:00
noteActivationLists[note].push_back(region);
for (auto cc = region->keyRange.getStart(); cc <= region->keyRange.getEnd(); cc++)
2019-08-23 01:27:39 +02:00
ccActivationLists[cc].push_back(region);
2019-08-25 14:01:03 +02:00
2019-08-11 13:31:16 +02:00
// Defaults
2019-08-11 11:10:36 +02:00
for (int ccIndex = 1; ccIndex < 128; ccIndex++)
region->registerCC(region->channelRange.getStart(), ccIndex, ccState[ccIndex]);
2019-08-25 14:01:03 +02:00
if (defaultSwitch) {
2019-08-11 11:10:36 +02:00
region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0);
region->registerNoteOff(region->channelRange.getStart(), *defaultSwitch, 0, 1.0);
}
2019-08-11 13:31:16 +02:00
region->registerPitchWheel(region->channelRange.getStart(), 0);
region->registerAftertouch(region->channelRange.getStart(), 0);
region->registerTempo(2.0f);
2019-08-25 14:01:03 +02:00
currentRegion++;
2019-08-04 01:38:31 +02:00
}
2019-08-04 01:38:31 +02:00
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;
}