Add SFZ support of generators
This commit is contained in:
parent
6e733e8c11
commit
5295a97981
9 changed files with 111 additions and 30 deletions
|
|
@ -18,6 +18,7 @@ set (SFIZZ_SOURCES
|
|||
sfizz/SfzFilter.cpp
|
||||
sfizz/Curve.cpp
|
||||
sfizz/Wavetables.cpp
|
||||
sfizz/Resources.cpp
|
||||
sfizz/Effects.cpp
|
||||
sfizz/effects/Nothing.cpp
|
||||
sfizz/effects/Filter.cpp
|
||||
|
|
|
|||
58
src/sfizz/Resources.cpp
Normal file
58
src/sfizz/Resources.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "Resources.h"
|
||||
#include "Wavetables.h"
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
|
||||
static constexpr unsigned kTableSize = 1024;
|
||||
|
||||
// amplitude values are matched to reference
|
||||
static constexpr double kAmplitudeSine = 0.625;
|
||||
static constexpr double kAmplitudeTriangle = 0.625;
|
||||
static constexpr double kAmplitudeSaw = 0.515;
|
||||
static constexpr double kAmplitudeSquare = 0.515;
|
||||
|
||||
static const WavetableMulti* getWaveSin()
|
||||
{
|
||||
static auto wave = WavetableMulti::createForHarmonicProfile(
|
||||
HarmonicProfile::getSine(), kAmplitudeSine, kTableSize);
|
||||
return &wave;
|
||||
}
|
||||
|
||||
static const WavetableMulti* getWaveTriangle()
|
||||
{
|
||||
static auto wave = WavetableMulti::createForHarmonicProfile(
|
||||
HarmonicProfile::getTriangle(), kAmplitudeTriangle, kTableSize);
|
||||
return &wave;
|
||||
}
|
||||
|
||||
static const WavetableMulti* getWaveSaw()
|
||||
{
|
||||
static auto wave = WavetableMulti::createForHarmonicProfile(
|
||||
HarmonicProfile::getSaw(), kAmplitudeSaw, kTableSize);
|
||||
return &wave;
|
||||
}
|
||||
|
||||
static const WavetableMulti* getWaveSquare()
|
||||
{
|
||||
static auto wave = WavetableMulti::createForHarmonicProfile(
|
||||
HarmonicProfile::getSquare(), kAmplitudeSquare, kTableSize);
|
||||
return &wave;
|
||||
}
|
||||
|
||||
Resources::Resources()
|
||||
: waveSin(getWaveSin()),
|
||||
waveTriangle(getWaveTriangle()),
|
||||
waveSaw(getWaveSaw()),
|
||||
waveSquare(getWaveSquare())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace sfz
|
||||
{
|
||||
class WavetableMulti;
|
||||
|
||||
struct Resources
|
||||
{
|
||||
MidiState midiState;
|
||||
|
|
@ -19,5 +21,12 @@ struct Resources
|
|||
FilePool filePool { logger };
|
||||
FilterPool filterPool { midiState };
|
||||
EQPool eqPool { midiState };
|
||||
|
||||
const WavetableMulti* waveSin { nullptr };
|
||||
const WavetableMulti* waveTriangle { nullptr };
|
||||
const WavetableMulti* waveSaw { nullptr };
|
||||
const WavetableMulti* waveSquare { nullptr };
|
||||
|
||||
Resources();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ sfz::Voice::Voice(sfz::Resources& resources)
|
|||
{
|
||||
filters.reserve(config::filtersPerVoice);
|
||||
equalizers.reserve(config::eqsPerVoice);
|
||||
|
||||
waveOscillator.init(sampleRate);
|
||||
}
|
||||
|
||||
void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept
|
||||
|
|
@ -35,7 +37,28 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value
|
|||
if (delay < 0)
|
||||
delay = 0;
|
||||
|
||||
if (!region->isGenerator()) {
|
||||
if (region->isGenerator()) {
|
||||
const WavetableMulti* wave = nullptr;
|
||||
switch (hash(region->sample)) {
|
||||
default:
|
||||
case hash("*silence"):
|
||||
break;
|
||||
case hash("*sine"):
|
||||
wave = resources.waveSin;
|
||||
break;
|
||||
case hash("*triangle"):
|
||||
case hash("*tri"):
|
||||
wave = resources.waveTriangle;
|
||||
break;
|
||||
case hash("*square"):
|
||||
wave = resources.waveSquare;
|
||||
break;
|
||||
case hash("*saw"):
|
||||
wave = resources.waveSaw;
|
||||
break;
|
||||
}
|
||||
waveOscillator.setWavetable(wave);
|
||||
} else {
|
||||
currentPromise = resources.filePool.getFilePromise(region->sample);
|
||||
if (currentPromise == nullptr) {
|
||||
reset();
|
||||
|
|
@ -226,6 +249,8 @@ void sfz::Voice::registerTempo(int delay, float secondsPerQuarter) noexcept
|
|||
void sfz::Voice::setSampleRate(float sampleRate) noexcept
|
||||
{
|
||||
this->sampleRate = sampleRate;
|
||||
|
||||
waveOscillator.init(sampleRate);
|
||||
}
|
||||
|
||||
void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||
|
|
@ -472,38 +497,26 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
|
|||
{
|
||||
const auto leftSpan = buffer.getSpan(0);
|
||||
const auto rightSpan = buffer.getSpan(1);
|
||||
|
||||
if (region->sample == "*noise") {
|
||||
absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); });
|
||||
absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); });
|
||||
}
|
||||
else if (region->sample == "*sine") {
|
||||
// TODO: wavetables for sine and other generators
|
||||
if (buffer.getNumFrames() == 0)
|
||||
return;
|
||||
|
||||
auto jumps = tempSpan1.first(buffer.getNumFrames());
|
||||
} else {
|
||||
// wavetables for sine and other generators
|
||||
auto frequencies = tempSpan1.first(buffer.getNumFrames());
|
||||
auto bends = tempSpan2.first(buffer.getNumFrames());
|
||||
auto phases = tempSpan2.first(buffer.getNumFrames());
|
||||
|
||||
const float step = baseFrequency * twoPi<float>() / sampleRate;
|
||||
fill<float>(jumps, step);
|
||||
fill<float>(frequencies, baseFrequency);
|
||||
|
||||
if (region->bendStep > 1)
|
||||
pitchBendEnvelope.getQuantizedBlock(bends, bendStepFactor);
|
||||
else
|
||||
pitchBendEnvelope.getBlock(bends);
|
||||
|
||||
applyGain<float>(bends, jumps);
|
||||
jumps[0] += phase;
|
||||
cumsum<float>(jumps, phases);
|
||||
phase = phases.back();
|
||||
applyGain<float>(bends, frequencies);
|
||||
|
||||
sin<float>(phases, leftSpan);
|
||||
waveOscillator.processModulated(frequencies.data(), leftSpan.data(), buffer.getNumFrames());
|
||||
copy<float>(leftSpan, rightSpan);
|
||||
|
||||
// Wrap the phase so we don't loose too much precision on longer notes
|
||||
const auto numTwoPiWraps = static_cast<int>(phase / twoPi<float>());
|
||||
phase -= twoPi<float>() * static_cast<float>(numTwoPiWraps);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "Region.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "MidiState.h"
|
||||
#include "Wavetables.h"
|
||||
#include "Resources.h"
|
||||
#include "AudioSpan.h"
|
||||
#include "LeakDetector.h"
|
||||
|
|
@ -307,6 +308,8 @@ private:
|
|||
MultiplicativeEnvelope<float> volumeEnvelope;
|
||||
float bendStepFactor { centsFactor(1) };
|
||||
|
||||
WavetableOscillator waveOscillator;
|
||||
|
||||
Duration dataDuration;
|
||||
Duration amplitudeDuration;
|
||||
Duration panningDuration;
|
||||
|
|
|
|||
|
|
@ -218,14 +218,11 @@ WavetableRange WavetableRange::getRangeForFrequency(float f)
|
|||
constexpr unsigned WavetableMulti::_tableExtra;
|
||||
|
||||
WavetableMulti WavetableMulti::createForHarmonicProfile(
|
||||
const HarmonicProfile& hp, unsigned tableSize, double refSampleRate)
|
||||
const HarmonicProfile& hp, double amplitude, unsigned tableSize, double refSampleRate)
|
||||
{
|
||||
WavetableMulti wm;
|
||||
constexpr unsigned multiSize = WavetableMulti::multiSize();
|
||||
|
||||
// amplitude to match ARIA's default generator output
|
||||
constexpr double amplitude = 0.25;
|
||||
|
||||
wm.allocateStorage(tableSize);
|
||||
|
||||
for (unsigned m = 0; m < multiSize; ++m) {
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public:
|
|||
// the reference sample rate is the minimum value accepted by the DSP
|
||||
// system (most defavorable wrt. aliasing)
|
||||
static WavetableMulti createForHarmonicProfile(
|
||||
const HarmonicProfile& hp, unsigned tableSize, double refSampleRate = 44100.0);
|
||||
const HarmonicProfile& hp, double amplitude, unsigned tableSize, double refSampleRate = 44100.0);
|
||||
|
||||
// create the tiniest wavetable with null content for use with oscillators
|
||||
static WavetableMulti createSilence();
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ bool DemoApp::initSound()
|
|||
fTmpFrequency.reset(new float[bufferSize]);
|
||||
|
||||
fMulti[0] = sfz::WavetableMulti::createForHarmonicProfile(
|
||||
sfz::HarmonicProfile::getSine(), 2048);
|
||||
sfz::HarmonicProfile::getSine(), 1.0, 2048);
|
||||
fMulti[1] = sfz::WavetableMulti::createForHarmonicProfile(
|
||||
sfz::HarmonicProfile::getTriangle(), 2048);
|
||||
sfz::HarmonicProfile::getTriangle(), 1.0, 2048);
|
||||
fMulti[2] = sfz::WavetableMulti::createForHarmonicProfile(
|
||||
sfz::HarmonicProfile::getSaw(), 2048);
|
||||
sfz::HarmonicProfile::getSaw(), 1.0, 2048);
|
||||
fMulti[3] = sfz::WavetableMulti::createForHarmonicProfile(
|
||||
sfz::HarmonicProfile::getSquare(), 2048);
|
||||
sfz::HarmonicProfile::getSquare(), 1.0, 2048);
|
||||
|
||||
fClient.reset(client);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ int main(int argc, char* argv[])
|
|||
std::cout << (i * (1.0 / tableSize)) << ' ' << table[i] << '\n';
|
||||
} else {
|
||||
sfz::WavetableMulti multi = sfz::WavetableMulti::createForHarmonicProfile(
|
||||
*hp, tableSize);
|
||||
*hp, 1.0, tableSize);
|
||||
unsigned multiSize = multi.multiSize();
|
||||
|
||||
if (true) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue