-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
2,086 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
cmake_minimum_required(VERSION 3.20) | ||
|
||
set(UHHYOU_USE_FFTW True) | ||
include(../common/cmake/non_simd.cmake) | ||
|
||
if(TEST_PLUGIN) | ||
build_test("") | ||
else() | ||
# VST 3 source files. | ||
set(plug_sources | ||
source/dsp/spectralfilter.cpp | ||
source/parameter.cpp | ||
source/gui/splashdraw.cpp | ||
source/plugprocessor.cpp | ||
source/editor.cpp | ||
source/plugfactory.cpp) | ||
|
||
build_vst3("${plug_sources}") | ||
endif() |
Binary file added
BIN
+26.2 KB
SpectralPhaser/resource/7F624680B9F84549B086166F2C3061CA_snapshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>NSHumanReadableCopyright</key> | ||
<string>2018 Steinberg Media Technologies</string> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>English</string> | ||
<key>CFBundleExecutable</key> | ||
<string>SpectralPhaser</string> | ||
<key>CFBundleIconFile</key> | ||
<string></string> | ||
<key>CFBundleIdentifier</key> | ||
<string>com.steinberg.vst3.SpectralPhaser</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundlePackageType</key> | ||
<string>BNDL</string> | ||
<key>CFBundleSignature</key> | ||
<string>????</string> | ||
<key>CFBundleVersion</key> | ||
<string>1.0</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CSResourcesFileMapped</key> | ||
<true /> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#include <windows.h> | ||
#include "../source/version.hpp" | ||
|
||
#define APSTUDIO_READONLY_SYMBOLS | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
// Version | ||
///////////////////////////////////////////////////////////////////////////// | ||
VS_VERSION_INFO VERSIONINFO | ||
FILEVERSION MAJOR_VERSION_INT,SUB_VERSION_INT,RELEASE_NUMBER_INT,BUILD_NUMBER_INT | ||
PRODUCTVERSION MAJOR_VERSION_INT,SUB_VERSION_INT,RELEASE_NUMBER_INT,BUILD_NUMBER_INT | ||
FILEFLAGSMASK 0x3fL | ||
#ifdef _DEBUG | ||
FILEFLAGS 0x1L | ||
#else | ||
FILEFLAGS 0x0L | ||
#endif | ||
FILEOS 0x40004L | ||
FILETYPE 0x1L | ||
FILESUBTYPE 0x0L | ||
BEGIN | ||
BLOCK "StringFileInfo" | ||
BEGIN | ||
BLOCK "040004e4" | ||
BEGIN | ||
VALUE "FileVersion", FULL_VERSION_STR"\0" | ||
VALUE "ProductVersion", FULL_VERSION_STR"\0" | ||
VALUE "OriginalFilename", stringOriginalFilename"\0" | ||
VALUE "FileDescription", stringFileDescription"\0" | ||
VALUE "InternalName", stringFileDescription"\0" | ||
VALUE "ProductName", stringFileDescription"\0" | ||
VALUE "CompanyName", stringCompanyName"\0" | ||
VALUE "LegalCopyright", stringLegalCopyright"\0" | ||
VALUE "LegalTrademarks", stringLegalTrademarks"\0" | ||
//VALUE "PrivateBuild", " \0" | ||
//VALUE "SpecialBuild", " \0" | ||
//VALUE "Comments", " \0" | ||
END | ||
END | ||
BLOCK "VarFileInfo" | ||
BEGIN | ||
VALUE "Translation", 0x400, 1252 | ||
END | ||
END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
// Copyright Takamitsu Endo ([email protected]). | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
|
||
#include "../../../lib/juce_ScopedNoDenormal.hpp" | ||
|
||
#include "dspcore.hpp" | ||
|
||
#include <algorithm> | ||
#include <limits> | ||
#include <numeric> | ||
|
||
using Sample = DSPCore::Sample; | ||
|
||
void DSPCore::setup(double sampleRate) | ||
{ | ||
this->sampleRate = Sample(sampleRate); | ||
|
||
SmootherCommon<Sample>::setSampleRate(sampleRate); | ||
|
||
lfoSyncRate = EMAFilter<double>::secondToP(sampleRate, Sample(0.013)); | ||
|
||
reset(); | ||
startup(); | ||
} | ||
|
||
size_t DSPCore::getLatency() { return spcParam.reportLatency ? spcParam.frmSize : 0; } | ||
|
||
#define ASSIGN_PARAMETER(METHOD) \ | ||
using ID = ParameterID::ID; \ | ||
const auto &pv = param.value; \ | ||
\ | ||
SmootherCommon<Sample>::setTime(pv[ID::parameterSmoothingSecond]->getFloat()); \ | ||
\ | ||
spcParam.sideChain = pv[ID::sideChainSwitch]->getInt(); \ | ||
spcParam.reportLatency = pv[ID::reportLatency]->getInt(); \ | ||
spcParam.frameSizeLog2 = pv[ID::frameSize]->getInt() + maxFrameSizeStart; \ | ||
spcParam.frmSize = int(1) << spcParam.frameSizeLog2; \ | ||
spcParam.transform = static_cast<TransformType>(pv[ID::transform]->getInt()); \ | ||
spcParam.maskWaveform = static_cast<MaskWaveform>(pv[ID::maskWaveform]->getInt()); \ | ||
\ | ||
lfoWaveform = static_cast<LfoWaveform>(pv[ID::lfoWaveform]->getInt()); \ | ||
lfoWaveMod.METHOD(pv[ID::lfoWaveMod]->getFloat()); \ | ||
lfoRate.METHOD(pv[ID::lfoRate]->getFloat() / sampleRate); \ | ||
lfoStereoPhaseOffset.METHOD(pv[ID::lfoStereoPhaseOffset]->getFloat()); \ | ||
lfoInitialPhase.METHOD(pv[ID::lfoInitialPhase]->getFloat()); \ | ||
\ | ||
feedback.METHOD(pv[ID::feedback]->getFloat()); \ | ||
const auto spcShift = Sample(0.5) * pv[ID::spectralShift]->getFloat(); \ | ||
spectralShift.METHOD(spcShift - std::floor(spcShift)); \ | ||
octaveDown.METHOD(pv[ID::octaveDown]->getFloat()); \ | ||
\ | ||
constexpr Sample twopi = Sample(2) * std::numbers::pi_v<Sample>; \ | ||
maskMix.METHOD(pv[ID::maskMix]->getFloat()); \ | ||
maskPhase.METHOD(pv[ID::maskPhase]->getFloat()); \ | ||
maskFreq.METHOD(pv[ID::maskFreq]->getFloat() / spcParam.frmSize); \ | ||
maskThreshold.METHOD(pv[ID::maskThreshold]->getFloat()); \ | ||
maskRotation.METHOD(pv[ID::maskRotation]->getFloat() * twopi); \ | ||
lfoToMaskMix.METHOD(pv[ID::lfoToMaskMix]->getFloat()); \ | ||
lfoToMaskPhase.METHOD(pv[ID::lfoToMaskPhase]->getFloat()); \ | ||
lfoToMaskFreq.METHOD(pv[ID::lfoToMaskFreq]->getFloat()); \ | ||
lfoToMaskThreshold.METHOD(pv[ID::lfoToMaskThreshold]->getFloat()); \ | ||
lfoToMaskRotation.METHOD(pv[ID::lfoToMaskRotation]->getFloat() * twopi); \ | ||
lfoToSpectralShift.METHOD(pv[ID::lfoToSpectralShift]->getFloat() * Sample(2)); \ | ||
\ | ||
outputGain.METHOD(pv[ID::outputGain]->getFloat()); \ | ||
dryWetMix.METHOD(pv[ID::dryWetMix]->getFloat()); | ||
|
||
void DSPCore::reset() | ||
{ | ||
ASSIGN_PARAMETER(reset); | ||
|
||
lfoTargetFreq = getTempoSyncFrequency(); | ||
lfo.reset(lfoInitialPhase.getValue(), lfoTargetFreq); | ||
for (auto &x : spc) x.reset(spcParam.frameSizeLog2); | ||
|
||
startup(); | ||
} | ||
|
||
void DSPCore::startup() {} | ||
|
||
void DSPCore::setParameters() { ASSIGN_PARAMETER(push); } | ||
|
||
// Output range is in [0, 1]. | ||
inline Sample phaseToWave(Sample phase, Sample mod, LfoWaveform waveform) | ||
{ | ||
switch (waveform) { | ||
case LfoWaveform::triSaw: { | ||
constexpr Sample eps = std::numeric_limits<Sample>::epsilon(); | ||
const auto pw = mod * Sample(0.5) + Sample(0.5); | ||
if (pw <= eps) return Sample(1) - phase; | ||
if (pw >= Sample(1) - eps) return phase; | ||
return phase < pw ? phase / pw : (Sample(1) - phase) / (Sample(1) - pw); | ||
} break; | ||
} | ||
// LfoWaveform::sine | ||
constexpr Sample twopi = Sample(2) * std::numbers::pi_v<Sample>; | ||
return std::sin(twopi * (phase + mod * std::sin(twopi * phase))); | ||
} | ||
|
||
std::array<Sample, 2> | ||
DSPCore::processFrame(int frameIndex, Sample in0, Sample in1, Sample side0, Sample side1) | ||
{ | ||
lfoInitialPhase.process(); | ||
|
||
const auto processLfoSync = [&]() { | ||
auto samplesElapsed = sampleRate * beatsElapsed * 60 / tempo; | ||
auto targetPhase = Sample((samplesElapsed + frameIndex) * lfoTargetFreq); | ||
targetPhase -= std::floor(targetPhase); | ||
return lfo.processSync( | ||
targetPhase + lfoInitialPhase.getValue(), lfoTargetFreq, lfoSyncRate); | ||
}; | ||
|
||
auto modPhase0 = isPlaying ? processLfoSync() : lfo.process(lfoTargetFreq); | ||
auto modPhase1 = modPhase0 + lfoStereoPhaseOffset.process(); | ||
modPhase1 -= std::floor(modPhase1); | ||
|
||
lfoWaveMod.process(); | ||
auto mod0 = phaseToWave(modPhase0, lfoWaveMod.getValue(), lfoWaveform); | ||
auto mod1 = phaseToWave(modPhase1, lfoWaveMod.getValue(), lfoWaveform); | ||
|
||
spcParam.dryWetMix = dryWetMix.process(); | ||
spcParam.feedback = feedback.process(); | ||
spectralShift.process(); | ||
octaveDown.process(); | ||
maskMix.process(); | ||
maskPhase.process(); | ||
maskFreq.process(); | ||
maskThreshold.process(); | ||
maskRotation.process(); | ||
lfoToMaskMix.process(); | ||
lfoToMaskPhase.process(); | ||
lfoToMaskFreq.process(); | ||
lfoToMaskThreshold.process(); | ||
lfoToMaskRotation.process(); | ||
lfoToSpectralShift.process(); | ||
lfoToOctaveDown.process(); | ||
|
||
const auto modulateParam = [&](Sample unipoler) { | ||
const auto bipoler = unipoler - Sample(0.5); | ||
|
||
spcParam.spectralShift | ||
= spectralShift.getValue() + lfoToSpectralShift.getValue() * bipoler; | ||
spcParam.spectralShift = spcParam.spectralShift - std::floor(spcParam.spectralShift); | ||
spcParam.octaveDown = octaveDown.getValue() + lfoToOctaveDown.getValue() * unipoler; | ||
spcParam.octaveDown -= std::floor(spcParam.octaveDown); | ||
|
||
spcParam.maskMix = std::clamp( | ||
maskMix.getValue() + lfoToMaskMix.getValue() * bipoler, Sample(0), Sample(1)); | ||
spcParam.maskPhase = maskPhase.getValue() + lfoToMaskPhase.getValue() * unipoler; | ||
spcParam.maskFreq | ||
= maskFreq.getValue() * std::exp2(Sample(6) * lfoToMaskFreq.getValue() * unipoler); | ||
spcParam.maskThreshold = std::clamp( | ||
maskThreshold.getValue() + lfoToMaskThreshold.getValue() * unipoler, // | ||
Sample(0), Sample(1)); | ||
spcParam.maskRotation | ||
= maskRotation.getValue() + lfoToMaskRotation.getValue() * unipoler; | ||
}; | ||
|
||
modulateParam(mod0); | ||
auto sig0 = spc[0].process(in0, side0, spcParam); | ||
|
||
modulateParam(mod1); | ||
auto sig1 = spc[1].process(in1, side1, spcParam); | ||
|
||
outputGain.process(); | ||
return { | ||
outputGain.getValue() * sig0, | ||
outputGain.getValue() * sig1, | ||
}; | ||
} | ||
|
||
void DSPCore::process( | ||
const size_t length, | ||
const float *in0, | ||
const float *in1, | ||
const float *side0, | ||
const float *side1, | ||
float *out0, | ||
float *out1) | ||
{ | ||
ScopedNoDenormals scopedDenormals; | ||
|
||
using ID = ParameterID::ID; | ||
const auto &pv = param.value; | ||
|
||
SmootherCommon<Sample>::setBufferSize(Sample(length)); | ||
lfoTargetFreq = getTempoSyncFrequency(); | ||
|
||
for (int i = 0; i < length; ++i) { | ||
auto frame = processFrame( | ||
i, Sample(in0[i]), Sample(in1[i]), Sample(side0[i]), Sample(side1[i])); | ||
out0[i] = float(frame[0]); | ||
out1[i] = float(frame[1]); | ||
} | ||
} | ||
|
||
Sample DSPCore::getTempoSyncFrequency() | ||
{ | ||
using ID = ParameterID::ID; | ||
const auto &pv = param.value; | ||
|
||
auto lfoRate = pv[ID::lfoRate]->getDouble(); | ||
if (lfoRate > Scales::lfoRate.getMax()) return 0; | ||
|
||
auto upper = (pv[ID::lfoTempoUpper]->getDouble() + double(1)) * timeSigLower; | ||
auto lower = pv[ID::lfoTempoLower]->getDouble() + double(1); | ||
|
||
// | ||
// Simplified version of following expressions. | ||
// | ||
// ``` | ||
// syncBeat = upper / (lower * lfoRate); | ||
// secondsPerBeat = 60 / tempoInBpm; | ||
// lfoFreq = 1 / (syncBeat * secondPerBeat * sampleRate); | ||
// ``` | ||
return Sample((tempo * lower * lfoRate) / (60 * upper * sampleRate)); | ||
} |
Oops, something went wrong.