├── Wavetable Editor ├── MyScript.js ├── compilerconfig.json ├── Styles.min.css ├── Styles.css ├── Styles.less ├── Utils.js ├── compilerconfig.json.defaults ├── WtOsc.js ├── Wavetable Editor.sln.exclude ├── Wav.js └── Index.html ├── Perft.xlsx ├── Polyhedrus.png ├── wavetable Comutation.xlsx ├── Polyhedrus.Ui ├── Background.jpg ├── Components │ ├── Background.jpg │ ├── OscSection.xaml.cs │ ├── DelaySection.xaml.cs │ ├── MixerSection.xaml.cs │ ├── VoicesSection.xaml.cs │ ├── FilterHpSection.xaml.cs │ ├── CharacterSection.xaml.cs │ ├── FilterMainSection.xaml.cs │ ├── ArpeggiatorSection.xaml.cs │ ├── VoiceTuning.xaml.cs │ ├── DriveSection.xaml.cs │ ├── ChorusSection.xaml.cs │ ├── MacroControls.xaml.cs │ ├── LfoSection.xaml.cs │ ├── EnvelopeSection.xaml.cs │ ├── BaseControl.cs │ ├── SynthView.xaml.cs │ ├── MatrixSection.xaml.cs │ ├── SavePresetDialog.xaml.cs │ ├── VoiceIndicator.xaml │ ├── ContextMenus.xaml │ ├── MenuSelector.xaml │ ├── FlatToggleButton.xaml │ ├── Spinner.xaml │ ├── LightKnob.xaml │ ├── SavePresetDialog.xaml │ ├── VoiceIndicator.xaml.cs │ ├── FilterHpSection.xaml │ ├── ModKnob.xaml │ ├── MenuSelector.xaml.cs │ ├── DriveSection.xaml │ ├── CharacterSection.xaml │ ├── MacroControls.xaml │ ├── FlatToggleButton.xaml.cs │ ├── ChorusSection.xaml │ └── ArpeggiatorSection.xaml ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ └── Resources.Designer.cs ├── VoiceMode.cs ├── RoutingStage.cs ├── App.xaml.cs ├── App.xaml ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Messaging │ └── OscTranceiver.cs ├── OscAddress.cs ├── ModSourceDest.cs └── ControlManager.cs ├── Polyhedrus.Native ├── UnitTests │ ├── WaveTests.h │ ├── OscTests.h │ ├── UnitTests.cpp │ ├── WaveTests.cpp │ └── OscTests.cpp ├── AudioLib │ ├── Noise.cpp │ ├── Decimator.cpp │ ├── FastSin.h │ ├── FastSin.cpp │ ├── Noise.h │ ├── WhiteNoise.h │ ├── MathDefs.h │ ├── ValueTables.h │ ├── Utils.cpp │ ├── LcgRandom.h │ ├── OnePoleFilters.h │ ├── SlopGenerator.h │ ├── Sse.h │ ├── Decimator.h │ ├── WaveFile.h │ ├── Biquad.h │ ├── SvfZeroFilter.h │ ├── ValueTables.cpp │ └── SvfFilter.h ├── Fft │ ├── BitReverse.h │ ├── TwiddleFactors.h │ ├── BitReverse.cpp │ └── Complex.h ├── PlatformSpecific.h ├── SynthDefines.h ├── FilterInternalMode.h ├── Vca.h ├── Utils.h ├── CvFreq.h ├── Osc │ ├── UdpTranceiver.h │ ├── OscMessage.h │ └── UdpTranceiver.cpp ├── Vca.cpp ├── Default.h ├── NoiseSimple.h ├── CvFreq.cpp ├── FilterHp.h ├── VoiceAllocator.h ├── OscillatorWt.h ├── PlatformSpecific.cpp ├── PresetManager.h ├── FilterDualSvf.h ├── FilterMain.h ├── AllpassDiffuser.h ├── Character.h ├── Drive.h ├── ModMatrix.h ├── Arpeggiator.h ├── FilterCascade.h ├── FilterTrueZero.h ├── NoiseSimple.cpp ├── OscillatorBase.h ├── FilterCascadeZero.h ├── Modulator.h ├── Delay.h ├── Exports.cpp ├── Envelope.h ├── ModSourceDest.h ├── SynthParameters.cpp ├── Drive.cpp ├── MixerSettings.h └── WavetableManager.h ├── CreateWavetables ├── App.config ├── packages.config └── Properties │ └── AssemblyInfo.cs ├── Polyhedrus.Tests ├── packages.config ├── Properties │ └── AssemblyInfo.cs └── BasicTests.cs ├── Wavetables ├── Basic │ ├── Sine-Pure.txt │ ├── Saw-Pure.txt │ ├── PWM.txt │ └── PulseySaw.txt ├── FM │ ├── FM-Saw.txt │ ├── FM1-8.txt │ ├── FM-Overtones.txt │ ├── FM1-3.txt │ └── FM-Angry.txt ├── AM │ └── AM1.txt ├── Sync │ └── AcidSync.txt └── Additive │ ├── Additive-Line.txt │ ├── Additive-Line-Hard.txt │ ├── Additive-1.txt │ └── Additive-1-Hard.txt ├── .gitignore ├── Optimizations ├── 2-FilterHp.md ├── 3-Noise.md └── 1-Character.md ├── readme.md ├── Polyhedrus.Plugin ├── Properties │ └── AssemblyInfo.cs └── Polyhedrus.Plugin.csproj ├── Performance1.psess └── Todo List.txt /Wavetable Editor/MyScript.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Perft.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValdemarOrn/Polyhedrus/HEAD/Perft.xlsx -------------------------------------------------------------------------------- /Polyhedrus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValdemarOrn/Polyhedrus/HEAD/Polyhedrus.png -------------------------------------------------------------------------------- /wavetable Comutation.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValdemarOrn/Polyhedrus/HEAD/wavetable Comutation.xlsx -------------------------------------------------------------------------------- /Polyhedrus.Ui/Background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValdemarOrn/Polyhedrus/HEAD/Polyhedrus.Ui/Background.jpg -------------------------------------------------------------------------------- /Wavetable Editor/compilerconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFile": "Styles2.css", 4 | "inputFile": "Styles2.less" 5 | } 6 | ] -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/Background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValdemarOrn/Polyhedrus/HEAD/Polyhedrus.Ui/Components/Background.jpg -------------------------------------------------------------------------------- /Polyhedrus.Native/UnitTests/WaveTests.h: -------------------------------------------------------------------------------- 1 | #ifndef WAVE_TESTS 2 | #define WAVE_TESTS 3 | 4 | namespace Tests 5 | { 6 | namespace Wave 7 | { 8 | void TestWaveReader1(); 9 | } 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /CreateWavetables/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CreateWavetables/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Polyhedrus.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Noise.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Noise.h" 3 | #include 4 | 5 | namespace AudioLib 6 | { 7 | uint8_t Noise::noise[96000]; 8 | 9 | void Noise::Initialize() 10 | { 11 | for (size_t i = 0; i < NoiseSamples; i++) 12 | noise[i] = std::rand() & 0xFF; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /Polyhedrus.Native/UnitTests/OscTests.h: -------------------------------------------------------------------------------- 1 | #ifndef OSC_TESTS 2 | #define OSC_TESTS 3 | 4 | namespace Tests 5 | { 6 | namespace Osc 7 | { 8 | void TestOscParser1(); 9 | void TestOscParserFloatInt(); 10 | void TestOscParserStringBlobInt(); 11 | void TestBundleParse(); 12 | } 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Decimator.cpp: -------------------------------------------------------------------------------- 1 | #include "Decimator.h" 2 | 3 | namespace AudioLib 4 | { 5 | float Decimator::Coeff[19] = { -0.0246f, -0.0298f, 0.0065f, 0.0460f, 0.0231f, -0.0598f, -0.0799f, 0.0685f, 0.3092f, 0.4286f, 0.3092f, 0.0685f, -0.0799f, -0.0598f, 0.0231f, 0.0460f, 0.0065f, -0.0298f, -0.0246f }; 6 | } -------------------------------------------------------------------------------- /Polyhedrus.Ui/VoiceMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Polyhedrus.Ui 8 | { 9 | enum VoiceMode 10 | { 11 | MonoHighest = 0, 12 | MonoLowest = 1, 13 | MonoNewest = 2, 14 | PolyRoundRobin = 3, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Wavetable Editor/Styles.min.css: -------------------------------------------------------------------------------- 1 | body{font-family:Tahoma;}.codeEditor{width:800px;height:200px;border:1px solid #999;margin:5px;}.controls{border:1px solid #999;margin:5px;width:800px;}.controls table{width:49%;display:inline-block;}.controls a{font-size:14pt;text-decoration:none;font-weight:bold;color:#555;cursor:pointer;}.controls a:hover{color:#777;} -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/FastSin.h: -------------------------------------------------------------------------------- 1 | #ifndef FASTSIN 2 | #define FASTSIN 3 | 4 | namespace AudioLib 5 | { 6 | class FastSin 7 | { 8 | private: 9 | static const int DataSize = 32768; 10 | static double data[DataSize]; 11 | 12 | public: 13 | static void Init(); 14 | static double Get(double index); 15 | }; 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/RoutingStage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Polyhedrus.Ui 8 | { 9 | enum RoutingStage 10 | { 11 | Character = 0, 12 | HpFilter = 1, 13 | MainFilter = 2, 14 | Drive = 3, 15 | Direct = 4, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/OscSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for OscSection.xaml 7 | /// 8 | public partial class OscSection : BaseControl 9 | { 10 | public OscSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/DelaySection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for DelaySection.xaml 7 | /// 8 | public partial class DelaySection : UserControl 9 | { 10 | public DelaySection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/MixerSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for MixerSection.xaml 7 | /// 8 | public partial class MixerSection : UserControl 9 | { 10 | public MixerSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/VoicesSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for VoicesSection.xaml 7 | /// 8 | public partial class VoicesSection : UserControl 9 | { 10 | public VoicesSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Fft/BitReverse.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BITREVERSE 3 | #define BITREVERSE 4 | 5 | #include 6 | 7 | class BitReverse 8 | { 9 | public: 10 | static std::map Tables; 11 | static std::map Bitsize; 12 | 13 | static void Setup(); 14 | static void Generate(int size); 15 | static int Reverse(int input, int bits); 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/FilterHpSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for FilterHpSection.xaml 7 | /// 8 | public partial class FilterHpSection : UserControl 9 | { 10 | public FilterHpSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/CharacterSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for CharacterSection.xaml 7 | /// 8 | public partial class CharacterSection : UserControl 9 | { 10 | public CharacterSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Native/UnitTests/UnitTests.cpp: -------------------------------------------------------------------------------- 1 | #include "OscTests.h" 2 | #include "WaveTests.h" 3 | 4 | extern "C" 5 | { 6 | _declspec(dllexport) void RunTests() 7 | { 8 | Tests::Wave::TestWaveReader1(); 9 | 10 | Tests::Osc::TestOscParser1(); 11 | Tests::Osc::TestOscParserFloatInt(); 12 | Tests::Osc::TestOscParserStringBlobInt(); 13 | Tests::Osc::TestBundleParse(); 14 | } 15 | } -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/FilterMainSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for FilterMainSection.xaml 7 | /// 8 | public partial class FilterMainSection : UserControl 9 | { 10 | public FilterMainSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/ArpeggiatorSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Polyhedrus.Ui.Components 4 | { 5 | /// 6 | /// Interaction logic for ArpeggiatorSection.xaml 7 | /// 8 | public partial class ArpeggiatorSection : UserControl 9 | { 10 | public ArpeggiatorSection() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace Polyhedrus.Ui 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Polyhedrus.Native/PlatformSpecific.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_PLATFORM_SPECIFIC 2 | #define POLYHEDRUS_PLATFORM_SPECIFIC 3 | 4 | #include 5 | 6 | namespace Polyhedrus 7 | { 8 | class PlatformSpecific 9 | { 10 | public: 11 | static std::string GetDllDir(); 12 | static void StartEditor(int port); 13 | 14 | static long long PerformanceFrequency(); 15 | static long long PerformanceCounter(); 16 | }; 17 | } 18 | 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /Polyhedrus.Native/SynthDefines.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_SYNTH_DEFINES 2 | #define POLYHEDRUS_SYNTH_DEFINES 3 | 4 | namespace Polyhedrus 5 | { 6 | 7 | const int MaxVoiceCount = 32; 8 | const int ModulationUpdateRate = 8; 9 | const int BufferSize = 64; 10 | const int VoiceTuningCount = 20; 11 | 12 | enum class VoiceMode 13 | { 14 | MonoHighest = 0, 15 | MonoLowest = 1, 16 | MonoNewest = 2, 17 | PolyRoundRobin = 3, 18 | }; 19 | 20 | } 21 | 22 | #endif -------------------------------------------------------------------------------- /Wavetables/Basic/Sine-Pure.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "1"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "Sine-Pure.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | output = []; 10 | for (var i = 0; i < N; i++) { 11 | var value = Math.sin(i / N * 2 * Math.PI); 12 | output[i] = value; 13 | } 14 | 15 | return output; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterInternalMode.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_FILTER_INTERNAL_MODE 2 | #define POLYHEDRUS_FILTER_INTERNAL_MODE 3 | 4 | namespace Polyhedrus 5 | { 6 | enum class InternalFilterMode 7 | { 8 | Lp24 = 0, // default 9 | Lp18, 10 | Lp12, 11 | Lp6, 12 | 13 | Hp24, 14 | Hp18, 15 | Hp12, 16 | Hp6, 17 | 18 | Bp6_6, 19 | Bp6_12, 20 | Bp12_6, 21 | 22 | Bp12_12, 23 | Bp6_18, 24 | Bp18_6, 25 | 26 | Count 27 | }; 28 | } 29 | 30 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/FastSin.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "MathDefs.h" 3 | #include 4 | #include "FastSin.h" 5 | 6 | namespace AudioLib 7 | { 8 | double FastSin::data[FastSin::DataSize]; 9 | 10 | void FastSin::Init() 11 | { 12 | for (int i = 0; i < DataSize; i++) 13 | { 14 | data[i] = std::sin(2 * M_PI * i / (double)DataSize); 15 | } 16 | } 17 | 18 | double FastSin::Get(double index) 19 | { 20 | return data[(int)(index * 32767.99999)]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Noise.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_NOISE 2 | #define AUDIOLIB_NOISE 3 | 4 | #include 5 | 6 | namespace AudioLib 7 | { 8 | class Noise 9 | { 10 | public: 11 | static const int NoiseSamples = 96000; 12 | 13 | private: 14 | static uint8_t noise[NoiseSamples]; 15 | 16 | public: 17 | static void Initialize(); 18 | 19 | static inline uint8_t Random(int index) 20 | { 21 | return noise[index]; 22 | } 23 | }; 24 | } 25 | 26 | #endif -------------------------------------------------------------------------------- /Wavetables/Basic/Saw-Pure.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "1"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "Saw-Pure.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | output = []; 10 | for (var i = 0; i < N; i++) { 11 | var phase = i / N; 12 | var value = 1 - 2 * phase; 13 | output[i] = value; 14 | } 15 | 16 | return output; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Wavetables/Basic/PWM.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "PWM.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var limit = (1 + k) / (1 + K); 11 | 12 | for (var i = 0; i < N; i++) { 13 | 14 | var part = (i / N) < limit; 15 | output[i] = part ? 1 : -1; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetable Editor/Styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Tahoma; 3 | } 4 | .codeEditor { 5 | width: 800px; 6 | height: 200px; 7 | border: 1px solid #999; 8 | margin: 5px; 9 | } 10 | .controls { 11 | border: 1px solid #999; 12 | margin: 5px; 13 | width: 800px; 14 | } 15 | .controls table { 16 | width: 49%; 17 | display: inline-block; 18 | } 19 | .controls a { 20 | font-size: 14pt; 21 | text-decoration: none; 22 | font-weight: bold; 23 | color: #555; 24 | cursor: pointer; 25 | } 26 | .controls a:hover { 27 | color: #777; 28 | } -------------------------------------------------------------------------------- /Polyhedrus.Native/Vca.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_VCA 2 | #define POLYHEDRUS_VCA 3 | 4 | namespace Polyhedrus 5 | { 6 | class Vca 7 | { 8 | public: 9 | float ControlVoltage; 10 | private: 11 | float* buffer; 12 | int samplerate; 13 | int modulationUpdateRate; 14 | float currentCv; 15 | 16 | public: 17 | Vca(); 18 | ~Vca(); 19 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate); 20 | void Process(float* input, int len); 21 | float* GetOutput(); 22 | private: 23 | void Update(); 24 | }; 25 | } 26 | 27 | #endif 28 | 29 | -------------------------------------------------------------------------------- /Wavetables/FM/FM-Saw.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "FM-Saw.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var last = 0.0; 11 | for (var i = 0; i < N; i++) { 12 | var phase = i / N * 2 * Math.PI; 13 | var value = Math.sin(phase + k * 0.01 * last); 14 | last = value; 15 | output[i] = value; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetables/AM/AM1.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "AM1.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var last = 0.0; 11 | for (var i = 0; i < N; i++) { 12 | var phase = i / N * 2 * Math.PI; 13 | var value = Math.sin(phase) * Math.sin(phase * (1 + k * 0.2)); 14 | last = value; 15 | output[i] = value; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetables/FM/FM1-8.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "FM1-8.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var last = 0.0; 11 | for (var i = 0; i < N; i++) { 12 | var phase = i * (-1 / N) * 2 * Math.PI; 13 | var value = Math.sin(phase - Math.sin(8 * phase) * k * 0.05); 14 | last = value; 15 | output[i] = value; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetables/FM/FM-Overtones.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "32"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "FM-Overtones.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var last = 0.0; 11 | for (var i = 0; i < N; i++) { 12 | var phase = i * (-1 / N) * 2 * Math.PI; 13 | var value = Math.sin(phase + Math.sin(phase * k)); 14 | last = value; 15 | output[i] = value; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetables/FM/FM1-3.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "FM1-3.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var last = 0.0; 11 | for (var i = 0; i < N; i++) { 12 | var phase = i * (-1 / N) * 2 * Math.PI; 13 | var value = Math.sin(phase - Math.sin(3 * phase) * k * 0.075); 14 | last = value; 15 | output[i] = value; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetables/Sync/AcidSync.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "AcidSync.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | 11 | for (var i = 0; i < N; i++) { 12 | var phase = i / N * 2 * Math.PI; 13 | var value = Math.sin(phase*0.5) * Math.sin(phase * (1 + k * 0.0275)); 14 | 15 | output[i] = Math.tanh(20* value) - value; 16 | } 17 | 18 | return output; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Wavetable Editor/Styles.less: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: Tahoma; 4 | } 5 | 6 | .codeEditor { 7 | width:800px; 8 | height:200px; 9 | border: 1px solid #999; 10 | margin: 5px; 11 | } 12 | 13 | .controls { 14 | border: 1px solid #999; 15 | margin: 5px; 16 | width: 800px; 17 | } 18 | 19 | .controls table { 20 | width: 49%; 21 | display: inline-block; 22 | } 23 | 24 | .controls a { 25 | font-size: 14pt; 26 | text-decoration: none; 27 | font-weight: bold; 28 | color: #555; 29 | cursor: pointer; 30 | } 31 | 32 | .controls a:hover { 33 | color: #777; 34 | } 35 | -------------------------------------------------------------------------------- /Wavetables/FM/FM-Angry.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "FM-Angry.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | k *= 0.5; 10 | var output = new Array(N); 11 | var last = 0.0; 12 | for (var i = 0; i < N; i++) { 13 | var phase = i * (-1 / N) * 2 * Math.PI; 14 | var value = Math.sin(phase - Math.sin(3 * phase) * k * 0.1 + last * k * 0.01); 15 | last = value; 16 | output[i] = value; 17 | } 18 | 19 | return output; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | *.obj 3 | *.exe 4 | *.pdb 5 | *.user 6 | *.aps 7 | *.pch 8 | *.vspscc 9 | *_i.c 10 | *_p.c 11 | *.ncb 12 | *.suo 13 | *.sln.docstates 14 | *.tlb 15 | *.tlh 16 | *.bak 17 | *.cache 18 | *.ilk 19 | *.log 20 | *.sdf 21 | *.vsp 22 | *.opensdf 23 | *.o 24 | [Bb]in 25 | [Dd]ebug*/ 26 | test-results/ 27 | *.lib 28 | *.sbr 29 | *.ipch 30 | *.vspx 31 | obj/ 32 | intermediate*/ 33 | .vs*/ 34 | [Rr]elease*/ 35 | _ReSharper*/ 36 | packages*/ 37 | [Tt]est[Rr]esult* 38 | !SharpSoundDevice-Stable-1.1.2-2013-05-08/* 39 | !SharpSoundDevice-Dev-1.3.0-2015-01-06/* 40 | Build/* 41 | Wavetables/*.wav 42 | Wavetables/*/*.wav 43 | Wavetables.zip -------------------------------------------------------------------------------- /Wavetables/Basic/PulseySaw.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | wavetableCountTextBox.value = "256"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "PulseySaw.wav"; 6 | } 7 | 8 | wt.makeWave = function (k, N, K) { 9 | var output = new Array(N); 10 | var sk = 2 * k / K - 1; 11 | var pow = Math.pow(2, 2 * sk); 12 | console.log(pow); 13 | 14 | for (var i = 0; i < N; i++) { 15 | var phase = (i / N + 0.5) % 1; 16 | var value = -1 + 2 * phase; 17 | 18 | output[i] = Math.sign(value) * Math.pow(Math.abs(value), pow); 19 | } 20 | 21 | return output; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Wavetable Editor/Utils.js: -------------------------------------------------------------------------------- 1 | Utils = {}; 2 | 3 | Utils.normalize = function(data) { 4 | 5 | var max = 0.0; 6 | 7 | for (var i = 0; i < data.length; i++) { 8 | if (Math.abs(data[i]) > max) 9 | max = Math.abs(data[i]); 10 | } 11 | 12 | for (var i = 0; i < data.length; i++) { 13 | data[i] = data[i] / max; 14 | } 15 | } 16 | 17 | Utils.gain = function (data, gain) { 18 | 19 | for (var i = 0; i < data.length; i++) { 20 | data[i] = data[i] * gain; 21 | } 22 | } 23 | 24 | Utils.tanh = function (data) { 25 | 26 | for (var i = 0; i < data.length; i++) { 27 | data[i] = Math.tanh(data[i]); 28 | } 29 | } -------------------------------------------------------------------------------- /Polyhedrus.Native/UnitTests/WaveTests.cpp: -------------------------------------------------------------------------------- 1 | #include "WaveTests.h" 2 | #include "../AudioLib/WaveFile.h" 3 | #include 4 | #include 5 | 6 | namespace Tests 7 | { 8 | namespace Wave 9 | { 10 | void TestWaveReader1() 11 | { 12 | std::string filename("C:\\Src\\_Tree\\Audio\\Polyhedrus\\Wavetables\\PWM.wav"); 13 | auto data = AudioLib::WaveFile::ReadWaveFile(filename); 14 | 15 | assert(data.size() == 1); 16 | auto wave = data.at(0); 17 | 18 | assert(wave.size() == 256 * 2048); 19 | assert(std::abs(wave.at(0) - 0.8) < 0.01); 20 | assert(std::abs(wave.at(7) - 0.8) < 0.01); 21 | assert(std::abs(wave.at(8) + 0.8) < 0.01); 22 | 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Optimizations/2-FilterHp.md: -------------------------------------------------------------------------------- 1 | Base case: 2 | 3 | PerftFilterHp 4 | Seconds,TimeMillis 5 | 500,422 6 | 500,426 7 | 500,412 8 | 500,416 9 | 500,420 10 | 500,420 11 | 500,417 12 | 500,421 13 | 14 | Lower bound (only update, not processing loop): 15 | 16 | PerftFilterHp 17 | Seconds,TimeMillis 18 | 500,91 19 | 500,90 20 | 500,90 21 | 500,90 22 | 500,91 23 | 500,90 24 | 500,89 25 | 500,91 26 | 27 | After consolidating the loop and using local register variables: 28 | 29 | PerftFilterHp 30 | Seconds,TimeMillis 31 | 500,373 32 | 500,372 33 | 500,365 34 | 500,374 35 | 500,372 36 | 500,371 37 | 500,370 38 | 500,364 39 | 40 | Not a huge improvement, but something measurable. -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/WhiteNoise.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace AudioLib 4 | { 5 | class WhiteNoise 6 | { 7 | private: 8 | 9 | // http://musicdsp.org/archive.php?classid=1#216 10 | const float g_fScale = 2.0f / 0xffffffff; 11 | int g_x1 = 0x67452301; 12 | int g_x2 = 0xefcdab89; 13 | 14 | public: 15 | 16 | void Sample(float* buffer, unsigned int bufferSize) 17 | { 18 | while (bufferSize--) 19 | { 20 | g_x1 ^= g_x2; 21 | *buffer++ = g_x2 * g_fScale; 22 | g_x2 += g_x1; 23 | } 24 | } 25 | 26 | void Cycle(unsigned int count) 27 | { 28 | while (count--) 29 | { 30 | g_x1 ^= g_x2; 31 | g_x2 += g_x1; 32 | } 33 | } 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_UTILS 2 | #define POLYHEDRUS_UTILS 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Polyhedrus 9 | { 10 | inline std::vector SplitString(const std::string s, char delim) 11 | { 12 | std::vector elems; 13 | 14 | std::stringstream ss(s); 15 | std::string item; 16 | 17 | while (std::getline(ss, item, delim)) 18 | { 19 | elems.push_back(item); 20 | } 21 | return elems; 22 | } 23 | 24 | inline std::string SPrint(const char* formatter, double value) 25 | { 26 | char buffer[256]; 27 | sprintf(buffer, formatter, value); 28 | return std::string(buffer); 29 | } 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/VoiceTuning.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Polyhedrus.Ui.Components 17 | { 18 | /// 19 | /// Interaction logic for VoiceTuning.xaml 20 | /// 21 | public partial class VoiceTuning : UserControl 22 | { 23 | public VoiceTuning() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/DriveSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Polyhedrus.Ui.Components 17 | { 18 | /// 19 | /// Interaction logic for DriveSection.xaml 20 | /// 21 | public partial class DriveSection : UserControl 22 | { 23 | public DriveSection() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/ChorusSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Polyhedrus.Ui.Components 17 | { 18 | /// 19 | /// Interaction logic for ChorusSection.xaml 20 | /// 21 | public partial class ChorusSection : UserControl 22 | { 23 | public ChorusSection() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/MacroControls.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Polyhedrus.Ui.Components 17 | { 18 | /// 19 | /// Interaction logic for MacroControls.xaml 20 | /// 21 | public partial class MacroControls : UserControl 22 | { 23 | public MacroControls() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Polyhedrus.Native/CvFreq.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_CV_FREQ 2 | #define POLYHEDRUS_CV_FREQ 3 | 4 | #include "AudioLib/Utils.h" 5 | 6 | namespace Polyhedrus 7 | { 8 | class CvFreq 9 | { 10 | private: 11 | static double GetCvFreqHz(double cv); 12 | static double Warp(double freqHz, double samplerate); 13 | 14 | float CvToFreq[11000]; 15 | float CvToFreqWarped[11000]; 16 | 17 | public: 18 | void Initialize(float samplerate); 19 | float inline GetFreq(float cv) 20 | { 21 | cv = AudioLib::Utils::Limit(cv, 0.0f, 11.0f); 22 | return CvToFreq[(int)(cv * 999.99)]; 23 | } 24 | 25 | float inline GetFreqWarped(float cv) 26 | { 27 | cv = AudioLib::Utils::Limit(cv, 0.0f, 11.0f); 28 | return CvToFreqWarped[(int)(cv * 999.99)]; 29 | } 30 | 31 | }; 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/LfoSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace Polyhedrus.Ui.Components 5 | { 6 | /// 7 | /// Interaction logic for LfoSection.xaml 8 | /// 9 | public partial class LfoSection : BaseControl 10 | { 11 | public static readonly DependencyProperty ShowPage2Property = DependencyProperty.Register("ShowPage2", typeof(bool), typeof(LfoSection), 12 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 13 | 14 | public LfoSection() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public bool ShowPage2 20 | { 21 | get { return (bool)GetValue(ShowPage2Property); } 22 | set { SetValue(ShowPage2Property, value); } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/EnvelopeSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace Polyhedrus.Ui.Components 5 | { 6 | /// 7 | /// Interaction logic for EnvelopeSection.xaml 8 | /// 9 | public partial class EnvelopeSection : BaseControl 10 | { 11 | public static readonly DependencyProperty ShowPage2Property = DependencyProperty.Register("ShowPage2", typeof(bool), typeof(EnvelopeSection), 12 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 13 | 14 | public EnvelopeSection() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public bool ShowPage2 20 | { 21 | get { return (bool)GetValue(ShowPage2Property); } 22 | set { SetValue(ShowPage2Property, value); } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/MathDefs.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | // shitty _USE_MATH_DEFINES is impossible to track! 4 | 5 | #define M_E 2.71828182845904523536 // e 6 | #define M_LOG2E 1.44269504088896340736 // log2(e) 7 | #define M_LOG10E 0.434294481903251827651 // log10(e) 8 | #define M_LN2 0.693147180559945309417 // ln(2) 9 | #define M_LN10 2.30258509299404568402 // ln(10) 10 | #define M_PI 3.14159265358979323846 // pi 11 | #define M_PI_2 1.57079632679489661923 // pi/2 12 | #define M_PI_4 0.785398163397448309616 // pi/4 13 | #define M_1_PI 0.318309886183790671538 // 1/pi 14 | #define M_2_PI 0.636619772367581343076 // 2/pi 15 | #define M_2_SQRTPI 1.12837916709551257390 // 2/sqrt(pi) 16 | #define M_SQRT2 1.41421356237309504880 // sqrt(2) 17 | #define M_SQRT1_2 0.707106781186547524401 // 1/sqrt(2) 18 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/BaseControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Controls; 7 | 8 | namespace Polyhedrus.Ui.Components 9 | { 10 | public class BaseControl : UserControl 11 | { 12 | private string moduleAddress; 13 | 14 | public string ModuleAddress 15 | { 16 | get { return moduleAddress; } 17 | set { moduleAddress = value; SetModuleAddress(); } 18 | } 19 | 20 | private void SetModuleAddress() 21 | { 22 | var children = OscAddress.GetChildrenWithValue(this); 23 | foreach (var child in children) 24 | { 25 | var currentVal = OscAddress.GetAddress(child.Key); 26 | var newVal = currentVal.Replace("*", moduleAddress); 27 | OscAddress.SetAddress(child.Key, newVal); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Osc/UdpTranceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_UDP_H 2 | #define POLYHEDRUS_UDP_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | using boost::asio::ip::udp; 10 | 11 | namespace Polyhedrus 12 | { 13 | class UdpTranceiver 14 | { 15 | private: 16 | boost::asio::io_service ioServiceSend; 17 | udp::endpoint sendEndpoint; 18 | udp::socket* sendSocket; 19 | 20 | boost::asio::io_service ioServiceReceive; 21 | udp::socket* receiveSocket; 22 | boost::array receiveBuffer; 23 | bool sendPortEnabled; 24 | 25 | public: 26 | UdpTranceiver(int receivePort, int sendPort); 27 | UdpTranceiver(int receivePort, int sendPort, std::string sendIp); 28 | ~UdpTranceiver(); 29 | 30 | std::vector Receive(); 31 | void Send(std::vector message); 32 | 33 | }; 34 | } 35 | 36 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/Vca.cpp: -------------------------------------------------------------------------------- 1 | #include "Vca.h" 2 | 3 | namespace Polyhedrus 4 | { 5 | Vca::Vca() 6 | { 7 | ControlVoltage = 0.0f; 8 | currentCv = 0.0f; 9 | buffer = 0; 10 | } 11 | 12 | Vca::~Vca() 13 | { 14 | delete buffer; 15 | } 16 | 17 | void Vca::Initialize(int samplerate, int bufferSize, int modulationUpdateRate) 18 | { 19 | buffer = new float[bufferSize](); 20 | this->modulationUpdateRate = modulationUpdateRate; 21 | this->samplerate = samplerate; 22 | } 23 | 24 | void Vca::Process(float* input, int len) 25 | { 26 | // smooth linear interpolation of CV 27 | float diff = (ControlVoltage - currentCv) / modulationUpdateRate; 28 | 29 | for (int i = 0; i < len; i++) 30 | { 31 | if (i < modulationUpdateRate) // ripe for optimization 32 | currentCv = currentCv + diff; 33 | 34 | buffer[i] = input[i] * currentCv; 35 | } 36 | } 37 | 38 | float* Vca::GetOutput() 39 | { 40 | return buffer; 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/SynthView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace Polyhedrus.Ui.Components 5 | { 6 | /// 7 | /// Interaction logic for SynthView.xaml 8 | /// 9 | public partial class SynthView : UserControl 10 | { 11 | private SynthViewModel Vm; 12 | 13 | public SynthView() 14 | { 15 | InitializeComponent(); 16 | } 17 | 18 | private void Button_Click(object sender, RoutedEventArgs e) 19 | { 20 | var controls = OscAddress.GetChildrenWithValue(this); 21 | this.Vm = new SynthViewModel(controls); 22 | this.DataContext = Vm; 23 | Vm.Parent = this.Parent as Window; 24 | Vm.Initialize(); 25 | } 26 | 27 | private void ShowContextMenu(object sender, System.Windows.Input.MouseButtonEventArgs e) 28 | { 29 | (sender as FrameworkElement).ContextMenu.DataContext = this.DataContext; 30 | (sender as FrameworkElement).ContextMenu.IsOpen = true; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Default.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_DEFAULT 2 | #define POLYHEDRUS_DEFAULT 3 | 4 | #include 5 | 6 | // --------- compiler target --------- 7 | 8 | #ifdef _MSC_VER 9 | 10 | // ---------- Define #pragma WARNING ----------- 11 | #define STRINGIZE_HELPER(x) #x 12 | #define STRINGIZE(x) STRINGIZE_HELPER(x) 13 | #define WARNING(desc) message(__FILE__ "(" STRINGIZE(__LINE__) ") : Warning: " #desc) 14 | 15 | #include "inttypes.h" 16 | 17 | #define __dllexport __declspec(dllexport) 18 | 19 | #ifdef DEBUG 20 | #define __inline_always inline 21 | #else 22 | #define __inline_always __forceinline 23 | #endif 24 | 25 | #endif 26 | 27 | 28 | #ifdef __GNUC__ 29 | 30 | #include 31 | 32 | #define __dllexport 33 | #define __stdcall __attribute__((stdcall)) 34 | 35 | #ifdef DEBUG 36 | #define __inline_always inline 37 | #else 38 | #define __inline_always inline __attribute__((always_inline)) 39 | #endif 40 | 41 | #endif 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Polyhedrus.Native/NoiseSimple.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_NOISE_SIMPLE 2 | #define POLYHEDRUS_NOISE_SIMPLE 3 | 4 | #include "AudioLib/OnePoleFilters.h" 5 | #include "AudioLib/WhiteNoise.h" 6 | 7 | namespace Polyhedrus 8 | { 9 | enum class NoiseType 10 | { 11 | White = 0, 12 | Pink = 1, 13 | Brown = 2 14 | }; 15 | 16 | class NoiseSimple 17 | { 18 | 19 | public: 20 | float Type; 21 | //float TypeMod; 22 | private: 23 | AudioLib::WhiteNoise random; 24 | AudioLib::Lp1 lpL; 25 | AudioLib::Lp1 lpR; 26 | AudioLib::Hp1 hpL; 27 | AudioLib::Hp1 hpR; 28 | float* bufferL; 29 | float* bufferR; 30 | float* randBuf; 31 | float* output[2]; 32 | 33 | int samplerate; 34 | int bufferSize; 35 | float brownL; 36 | float brownR; 37 | float typeTotal; 38 | 39 | public: 40 | NoiseSimple(); 41 | ~NoiseSimple(); 42 | void Initialize(int samplerate, int bufferSize, int voiceIndex); 43 | void Process(int len); 44 | inline float** GetOutput() { return output; } 45 | 46 | }; 47 | } 48 | 49 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Ui/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Navigation; 16 | using System.Windows.Shapes; 17 | 18 | namespace Polyhedrus.Ui 19 | { 20 | /// 21 | /// Interaction logic for MainWindow.xaml 22 | /// 23 | public partial class MainWindow : Window 24 | { 25 | public MainWindow() 26 | { 27 | CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; 28 | Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; 29 | Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; 30 | InitializeComponent(); 31 | 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Polyhedrus.Native/CvFreq.cpp: -------------------------------------------------------------------------------- 1 | #include "AudioLib/MathDefs.h" 2 | #include 3 | 4 | #include "CvFreq.h" 5 | 6 | namespace Polyhedrus 7 | { 8 | double CvFreq::GetCvFreqHz(double cv) 9 | { 10 | // Voltage is 1V/OCt, 0V = C0 = 16.3516Hz 11 | // 10.3V = Max = 20614.33hz 12 | // goes up to 11V = 33488.07 Hz as some filters need it 13 | double freq = (double)(440.0 * std::pow(2, (cv * 12 - 69.0 + 12) / 12)); 14 | return freq; 15 | } 16 | 17 | double CvFreq::Warp(double freqHz, double samplerate) 18 | { 19 | double T = 1.0 / samplerate; 20 | double omegaCWarped = freqHz * 2 * M_PI; 21 | double omegaC = 2 / T * std::tan(omegaCWarped * T / 2); 22 | 23 | return omegaC / 2 / M_PI; 24 | } 25 | 26 | void CvFreq::Initialize(float samplerate) 27 | { 28 | for (int i = 0; i < 11000; i++) 29 | { 30 | double cv = i / 1000.0; 31 | auto cvFreq = GetCvFreqHz(cv); 32 | auto compensatedFreq = Warp(cvFreq, samplerate); 33 | CvToFreq[i] = (float)cvFreq; 34 | CvToFreqWarped[i] = (float)compensatedFreq; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /Wavetables/Additive/Additive-Line.txt: -------------------------------------------------------------------------------- 1 | wt.initialize = function(N, K) { 2 | 3 | wavetableCountTextBox.value = "128"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "Additive-Line.wav"; 6 | 7 | tones = []; 8 | 9 | for (var k = 0; k < K; k++) { 10 | tones.push([]); 11 | var divisor = 1 / (k + 1); 12 | 13 | for (var i = 0; i < K; i++) { 14 | var val = 1 - i * divisor; 15 | if (val < 0) val = 0; 16 | tones[k][i] = val; 17 | } 18 | } 19 | } 20 | 21 | wt.makeWave = function (k, N, K) { 22 | var output = new Array(N); 23 | var partials = tones[k]; 24 | for (var i = 0; i < N; i++) { 25 | output[i] = 0.0; 26 | } 27 | 28 | for (var p = 0; p < partials.length; p++) { 29 | var vol = partials[p]; 30 | 31 | for (var i = 0; i < N; i++) { 32 | var phase = i / N * 2 * Math.PI; 33 | var value = Math.sin(phase * (p + 1)) * vol; 34 | output[i] += value; 35 | } 36 | } 37 | 38 | Utils.normalize(output); 39 | 40 | return output; 41 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Polyhedrus - Digital Synthesizer 2 | 3 | Polyhedrus is a wavetable synthesizer, currently in the early stages of development. 4 | 5 | ![](Polyhedrus.png) 6 | 7 | ## Technical Specifications 8 | 9 | * Cross-platform synthesis engine written in C++. 10 | * Hard real-time audio synthesis algorithms. 11 | * Portable code, uses Boost Libraries (v 1.59) and C++ 11. 12 | * User Interface and audio engine communicate via Open Sound Control messages via UDP (using Boost.Asio in the sound engine). 13 | * User interface implemented as a separate process. 14 | * Currently using C# WPF. 15 | * cross-platform replacement written in Javascript/React.js/Electron is under development. 16 | 17 | ## Features 18 | 19 | * Bandlimited Wavetable Oscillators. 20 | * Multimode filter with several unique filter designs. Current model includes: 21 | * 4-pole Ladder filder with Zero-delay Feedback. 22 | * 4-Poly cascaded ladder filter. 23 | * Dual SVF Filter. 24 | * Individual Voice architecture. 25 | * 16 slot Modulation matrix. 26 | * Visual Feedback for waveforms and envelopes. 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/ValueTables.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_VALUETABLES 2 | #define AUDIOLIB_VALUETABLES 3 | 4 | namespace AudioLib 5 | { 6 | class ValueTables 7 | { 8 | public: 9 | static const int TableSize = 4001; 10 | 11 | static double Sqrt[TableSize]; 12 | static double Sqrt3[TableSize]; 13 | static double Pow1_5[TableSize]; 14 | static double Pow2[TableSize]; 15 | static double Pow3[TableSize]; 16 | static double Pow4[TableSize]; 17 | static double x2Pow3[TableSize]; 18 | 19 | // octave response. value double every step (2,3,4,5 or 6 steps) 20 | static double Response2Oct[TableSize]; 21 | static double Response3Oct[TableSize]; 22 | static double Response4Oct[TableSize]; 23 | static double Response5Oct[TableSize]; 24 | static double Response6Oct[TableSize]; 25 | 26 | // decade response, value multiplies by 10 every step 27 | static double Response2Dec[TableSize]; 28 | static double Response3Dec[TableSize]; 29 | static double Response4Dec[TableSize]; 30 | 31 | static void Init(); 32 | static double Get(double index, double* table); 33 | }; 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterHp.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_FILTER_HP 2 | #define POLYHEDRUS_FILTER_HP 3 | 4 | #include "AudioLib/SvfZeroFilter.h" 5 | #include "Parameters.h" 6 | #include "CvFreq.h" 7 | 8 | namespace Polyhedrus 9 | { 10 | class FilterHp 11 | { 12 | public: 13 | bool IsEnabled; 14 | float Cutoff; 15 | float Resonance; 16 | float CutoffMod; 17 | float ResonanceMod; 18 | 19 | private: 20 | float* buffer; 21 | int samplerate; 22 | int modulationUpdateRate; 23 | AudioLib::SvfZeroFilter svf; 24 | CvFreq cvToFreq; 25 | 26 | public: 27 | FilterHp(); 28 | ~FilterHp(); 29 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate); 30 | void SetParameter(FilterHpParameters parameter, double value); 31 | void Process(float* input, int len); 32 | inline float* GetOutput() { return buffer; } 33 | inline float GetCutoff() { return svf.Fc; } 34 | std::vector GetVisual(FilterHpParameters parameter, int* baseLevel); 35 | 36 | static std::vector GetKeytrackVisual(double keytrack); 37 | private: 38 | void Update(); 39 | }; 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /Polyhedrus.Native/VoiceAllocator.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_VOICE_ALLOCATOR 2 | #define POLYHEDRUS_VOICE_ALLOCATOR 3 | 4 | #include "SynthDefines.h" 5 | #include "Voice.h" 6 | 7 | namespace Polyhedrus 8 | { 9 | class VoiceAllocator 10 | { 11 | public: 12 | VoiceMode voiceMode; 13 | int polyphony; 14 | int unison; 15 | 16 | VoiceAllocator(); 17 | void Initialize(Voice* voices); 18 | void UpdateVoiceStates(); 19 | void NoteOn(uint8_t note, float velocity); 20 | void NoteOff(uint8_t note); 21 | 22 | inline int GetVoiceCount() 23 | { 24 | return effectivePolyphony; 25 | } 26 | 27 | private: 28 | Voice* voices; 29 | float currentVelocity; 30 | 31 | int effectivePolyphony; 32 | int effectiveUnison; 33 | VoiceMode effectiveVoiceMode; 34 | int lastAllocatedVoice; 35 | 36 | int noteCounters[128]; // used to track newest/oldest notes in mono mode 37 | int voiceCounters[128]; // used to track least recently triggered voice in poly mode 38 | 39 | int noteCounter; 40 | int voiceCounter; 41 | 42 | int FindNextMonoNote(); 43 | int FindNextVoice(); 44 | }; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /Wavetables/Additive/Additive-Line-Hard.txt: -------------------------------------------------------------------------------- 1 | wt.initialize = function(N, K) { 2 | 3 | wavetableCountTextBox.value = "128"; 4 | wavetableSizeTextBox.value = "2048"; 5 | downloadLink.download = "Additive-Line-Hard.wav"; 6 | 7 | tones = []; 8 | 9 | for (var k = 0; k < K; k++) { 10 | tones.push([]); 11 | var divisor = 1 / (k + 1); 12 | 13 | for (var i = 0; i < K; i++) { 14 | var val = 1 - i * divisor; 15 | if (val < 0) val = 0; 16 | tones[k][i] = val; 17 | } 18 | } 19 | } 20 | 21 | wt.makeWave = function (k, N, K) { 22 | var output = new Array(N); 23 | var partials = tones[k]; 24 | for (var i = 0; i < N; i++) { 25 | output[i] = 0.0; 26 | } 27 | 28 | for (var p = 0; p < partials.length; p++) { 29 | var vol = partials[p]; 30 | 31 | for (var i = 0; i < N; i++) { 32 | var phase = i / N * 2 * Math.PI; 33 | var value = Math.sin(phase * (p + 1)) * vol; 34 | output[i] += value; 35 | } 36 | } 37 | 38 | Utils.normalize(output); 39 | Utils.gain(output, 3); 40 | Utils.tanh(output); 41 | 42 | return output; 43 | } -------------------------------------------------------------------------------- /Polyhedrus.Native/Fft/TwiddleFactors.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef TWIDDLE 3 | #define TWIDDLE 4 | 5 | #include "Complex.h" 6 | #include 7 | 8 | template 9 | class TwiddleFactors 10 | { 11 | public: 12 | static std::map*> Factors; 13 | static void Setup(); 14 | }; 15 | 16 | 17 | // ------------ Implementation ------------ 18 | 19 | #define PI 3.1415926535897932384626433832795 20 | 21 | template 22 | std::map*> TwiddleFactors::Factors; 23 | 24 | template 25 | void TwiddleFactors::Setup() 26 | { 27 | int N = 65536; 28 | Complex* masterArray = new Complex[N/2](); 29 | 30 | for(int i = 0; i < N/2; i++) 31 | { 32 | masterArray[i] = Complex::CExp((TVal)(-i * 2 * PI / N)); 33 | } 34 | 35 | TwiddleFactors::Factors[N] = masterArray; 36 | 37 | int hop = 1; 38 | while (N > 2) 39 | { 40 | hop = hop * 2; 41 | N = N / 2; 42 | 43 | Complex* arr = new Complex[N/2]; 44 | TwiddleFactors::Factors[N] = arr; 45 | 46 | for (int i = 0; i < N/2; i++) 47 | arr[i] = masterArray[i * hop]; 48 | } 49 | } 50 | 51 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Ui/Messaging/OscTranceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Polyhedrus.Ui.Messaging 10 | { 11 | public class OscTranceiver 12 | { 13 | private readonly Socket sendSocket; 14 | private readonly IPEndPoint sendEp; 15 | 16 | private readonly UdpClient listener; 17 | private IPEndPoint receiveEp; 18 | 19 | public OscTranceiver(int sendPort, int receivePort) 20 | { 21 | sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 22 | sendEp = new IPEndPoint(IPAddress.Loopback, sendPort); 23 | 24 | listener = new UdpClient(receivePort); 25 | receiveEp = new IPEndPoint(IPAddress.Any, receivePort); 26 | } 27 | 28 | public void Send(byte[] bytes) 29 | { 30 | sendSocket.SendTo(bytes, sendEp); 31 | } 32 | 33 | public byte[] Receive() 34 | { 35 | if (listener.Available == 0) 36 | return null; 37 | 38 | var received = listener.Receive(ref receiveEp); 39 | return received; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/MatrixSection.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Windows.Controls; 3 | 4 | namespace Polyhedrus.Ui.Components 5 | { 6 | /// 7 | /// Interaction logic for MatrixSection.xaml 8 | /// 9 | public partial class MatrixSection : UserControl 10 | { 11 | private int startIndex; 12 | 13 | public MatrixSection() 14 | { 15 | InitializeComponent(); 16 | } 17 | 18 | public int StartIndex 19 | { 20 | get { return startIndex; } 21 | set { startIndex = value; SetIndex(); } 22 | } 23 | 24 | private void SetIndex() 25 | { 26 | int index = StartIndex; 27 | foreach (var childP in RoutesStackPanel.Children) 28 | { 29 | var childPanel = childP as StackPanel; 30 | if (childPanel == null) 31 | continue; 32 | 33 | var children = OscAddress.GetChildrenWithValue(childPanel); 34 | foreach (var child in children) 35 | { 36 | var currentVal = OscAddress.GetAddress(child.Key); 37 | var newVal = currentVal.Replace("*", index.ToString()); 38 | OscAddress.SetAddress(child.Key, newVal); 39 | } 40 | 41 | index++; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Polyhedrus.Ui.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Wavetable Editor/compilerconfig.json.defaults: -------------------------------------------------------------------------------- 1 | { 2 | "compilers": { 3 | "less": { 4 | "autoPrefix": "", 5 | "cssComb": "none", 6 | "ieCompat": true, 7 | "strictMath": false, 8 | "strictUnits": false, 9 | "relativeUrls": true, 10 | "rootPath": "", 11 | "soruceMapRoot": null, 12 | "soruceMapBasePath": null, 13 | "sourceMap": false 14 | }, 15 | "sass": { 16 | "includePath": null, 17 | "indentType": "space", 18 | "indentWidth": 2, 19 | "outputStyle": "nested", 20 | "Precision": 5, 21 | "relativeUrls": true, 22 | "soruceMapRoot": null, 23 | "sourceMap": false 24 | }, 25 | "stylus": { 26 | "sourceMap": false 27 | }, 28 | "babel": { 29 | "sourceMap": false 30 | }, 31 | "coffeescript": { 32 | "bare": false, 33 | "runtimeMode": "node", 34 | "sourceMap": false 35 | } 36 | }, 37 | "minifiers": { 38 | "css": { 39 | "enabled": true, 40 | "termSemicolons": true, 41 | "gzip": false 42 | }, 43 | "javascript": { 44 | "enabled": true, 45 | "termSemicolons": true, 46 | "gzip": false 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Optimizations/3-Noise.md: -------------------------------------------------------------------------------- 1 | First I had to rewrite the noise module somewhat, as it was fairly shit and was missing the ability to mix between different types of noise. 2 | 3 | Initial results: 4 | 5 | PerfNoise 6 | Seconds,TimeMillis 7 | 500,431 8 | 500,447 9 | 500,430 10 | 500,428 11 | 500,425 12 | 500,424 13 | 500,427 14 | 500,429 15 | 16 | My initial attempt was going to be pre-computing 1 second buffers of noise, but the cycle hum was too noticeable, so I had to continue to process everything in the main audio loop. 17 | 18 | I also experimented with adding a more efficient GetFloats() method to the RNG, but this did not seem to improve performance at all; in fact it was slightly slower, even when using SSE instructions to cast the random integers to floats in a vectorized manner. 19 | 20 | One optimization I was able to do was to use 31 bit precision in the NextFloat() method in the RNG. This meant I could use signed ints, and as one of the optimization points stated, unsigned ints take longer to convert. This seems to hold true: 21 | 22 | PerfNoise 23 | Seconds,TimeMillis 24 | 500,402 25 | 500,416 26 | 500,402 27 | 500,395 28 | 500,400 29 | 500,400 30 | 500,402 31 | 500,400 32 | 500,392 33 | -------------------------------------------------------------------------------- /Polyhedrus.Native/OscillatorWt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Default.h" 4 | #include "WavetableManager.h" 5 | #include "OscillatorBase.h" 6 | 7 | namespace Polyhedrus 8 | { 9 | class OscillatorWt : public OscillatorBase 10 | { 11 | private: 12 | float* buffer; 13 | std::shared_ptr wavetable; 14 | int samplerate; 15 | int modulationUpdateRate; 16 | uint32_t increment; 17 | uint32_t iterator; 18 | int tableSize; 19 | float iteratorScaler; 20 | float waveMix; 21 | float* waveA; 22 | float* waveB; 23 | 24 | float glideRate; 25 | float currentPitch; 26 | 27 | public: 28 | OscillatorWt(); 29 | ~OscillatorWt(); 30 | 31 | OscillatorType GetType() const override; 32 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate) override; 33 | void UpdateGlide() override; 34 | void Reset() override; 35 | void Process(int count) override; 36 | float* GetOutput() const override; 37 | std::vector GetVisual() const override; 38 | std::string GetShapeString() const override; 39 | 40 | void SetWavetable(std::shared_ptr wavetable); 41 | std::shared_ptr GetWavetable() const; 42 | 43 | private: 44 | void Update(); 45 | }; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Polyhedrus.Native/PlatformSpecific.cpp: -------------------------------------------------------------------------------- 1 | #include "PlatformSpecific.h" 2 | 3 | // Windows-specific Code here 4 | #include 5 | 6 | namespace Polyhedrus 7 | { 8 | HINSTANCE GetMyModuleHandle() 9 | { 10 | MEMORY_BASIC_INFORMATION mbi; 11 | VirtualQuery(GetMyModuleHandle, &mbi, sizeof(mbi)); 12 | return (HINSTANCE)(mbi.AllocationBase); 13 | } 14 | 15 | std::string PlatformSpecific::GetDllDir() 16 | { 17 | char dllName[512]; 18 | HINSTANCE thismodule = GetMyModuleHandle(); 19 | GetModuleFileName(thismodule, dllName, 512); 20 | 21 | std::string filename(dllName); 22 | std::string directory; 23 | const size_t last_slash_idx = filename.rfind('\\'); 24 | if (std::string::npos != last_slash_idx) 25 | { 26 | directory = filename.substr(0, last_slash_idx); 27 | } 28 | 29 | return directory; 30 | } 31 | 32 | void PlatformSpecific::StartEditor(int port) 33 | { 34 | 35 | } 36 | 37 | long long PlatformSpecific::PerformanceFrequency() 38 | { 39 | LARGE_INTEGER li; 40 | QueryPerformanceFrequency(&li); 41 | return li.QuadPart; 42 | } 43 | 44 | long long PlatformSpecific::PerformanceCounter() 45 | { 46 | LARGE_INTEGER li; 47 | QueryPerformanceCounter(&li); 48 | return li.QuadPart; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Polyhedrus.Native/PresetManager.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_PRESET_MANAGER 2 | #define POLYHEDRUS_PRESET_MANAGER 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Polyhedrus 9 | { 10 | class Preset 11 | { 12 | public: 13 | std::string BankName; 14 | std::string PresetName; 15 | std::string FilePath; 16 | 17 | std::map Metadata; 18 | std::map Values; 19 | 20 | std::string Serialize(); 21 | }; 22 | 23 | class PresetManager 24 | { 25 | public: 26 | const static char* Osc1Waveform; 27 | const static char* Osc2Waveform; 28 | const static char* Osc3Waveform; 29 | 30 | std::string BaseDirectory; 31 | 32 | std::map> PresetBanks; 33 | 34 | PresetManager(); 35 | void Initialize(std::string baseDirectory); 36 | Preset GetDefaultPreset(); 37 | Preset GetPreset(std::string bankName, std::string presetName); 38 | std::string GetBankString(); 39 | std::string GetPresetString(std::string bankName); 40 | void LoadBanks(); 41 | void LoadPresetsByBank(std::string bankName); 42 | Preset ReadPresetFile(std::string bankName, std::string presetFilepath); 43 | void SavePreset(Preset* preset); 44 | }; 45 | } 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "MathDefs.h" 2 | #include 3 | 4 | #include "Utils.h" 5 | 6 | namespace AudioLib 7 | { 8 | float Utils::note2Freq[12800]; 9 | float Utils::sintable[TableSize]; 10 | float Utils::costable[TableSize]; 11 | float Utils::tableScaler; 12 | 13 | float Utils::tanhTable[TanhTableSize]; 14 | 15 | void Utils::Initialize() 16 | { 17 | for (int i = 0; i < 12800; i++) 18 | { 19 | note2Freq[i] = (float)(440.0 * std::pow(2, (0.01*i - 69) / 12.0)); 20 | } 21 | 22 | tableScaler = (float)(1.0 / (2.0f * M_PI) * TableSize); 23 | 24 | for (int i = 0; i < TableSize; i++) 25 | { 26 | sintable[i] = (float)std::sin(i / (double)TableSize * M_PI); 27 | costable[i] = (float)std::cos(i / (double)TableSize * M_PI); 28 | } 29 | 30 | for (int i = 0; i < TanhTableSize; i++) 31 | { 32 | tanhTable[i] = std::tanh(-3.0f + i / 10000.0f); 33 | } 34 | } 35 | 36 | float Utils::Note2Freq(float note) 37 | { 38 | auto base = (int)(note * 100); 39 | auto partial = note * 100 - base; 40 | 41 | if (base > 12799) 42 | { 43 | base = 12799; 44 | partial = 0; 45 | } 46 | 47 | auto freq = note2Freq[base]; 48 | auto slope = note2Freq[base + 1] - freq; 49 | 50 | return freq + partial * slope; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterDualSvf.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_FILTER_DUAL_SVF 2 | #define POLYHEDRUS_FILTER_DUAL_SVF 3 | 4 | #include 5 | #include "FilterMain.h" 6 | #include "AudioLib/SvfFilter.h" 7 | #include "CvFreq.h" 8 | 9 | namespace Polyhedrus 10 | { 11 | class FilterDualSvf : public FilterMain 12 | { 13 | private: 14 | float* buffer; 15 | float gain; 16 | float gainInv; 17 | float totalResonance; 18 | float volLp; 19 | float volBp; 20 | float volHp; 21 | CvFreq cvToFreq; 22 | AudioLib::SvfFilter svf1; 23 | AudioLib::SvfFilter svf2; 24 | 25 | float fsinv; 26 | int samplerate; 27 | int modulationUpdateRate; 28 | 29 | public: 30 | FilterDualSvf(); 31 | ~FilterDualSvf() override; 32 | 33 | FilterMainType GetType() const override; 34 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate) override; 35 | void SetParameter(FilterMainParameters parameter, double value) override; 36 | void Process(float* input, int len) override; 37 | float* GetOutput() override; 38 | 39 | std::vector GetVisual() override; 40 | std::vector GetDriveVisual() override; 41 | std::string GetModeString() override; 42 | 43 | std::vector GetMagnitudeResponse() const; 44 | 45 | private: 46 | void Update(); 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterMain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Parameters.h" 4 | 5 | namespace Polyhedrus 6 | { 7 | enum class FilterMainType 8 | { 9 | TrueZero = 0, 10 | DualSvf = 1, 11 | }; 12 | 13 | class FilterMain 14 | { 15 | public: 16 | bool IsEnabled; 17 | float Cutoff; 18 | float Resonance; 19 | float Drive; 20 | float Mode; 21 | 22 | protected: 23 | float CutoffMod = 0; 24 | float ResonanceMod = 0; 25 | float DriveMod = 0; 26 | float ModeMod = 0; 27 | 28 | public: 29 | virtual ~FilterMain() { } 30 | 31 | virtual FilterMainType GetType() const = 0; 32 | virtual void Initialize(int samplerate, int bufferSize, int modulationUpdateRate) = 0; 33 | virtual void SetParameter(FilterMainParameters parameter, double value) = 0; 34 | virtual void Process(float* input, int len) = 0; 35 | virtual float* GetOutput() = 0; 36 | virtual std::vector GetVisual() = 0; 37 | virtual std::vector GetDriveVisual() = 0; 38 | virtual std::string GetModeString() = 0; 39 | 40 | virtual void SetCutoffMod(float modAmount) { CutoffMod = modAmount; } 41 | virtual void SetResonanceMod(float modAmount) { ResonanceMod = modAmount; } 42 | virtual void SetDriveMod(float modAmount) { DriveMod = modAmount; } 43 | virtual void SetModeMod(float modAmount) { ModeMod = modAmount; } 44 | }; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AllpassDiffuser.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_ALLPASS_DIFFUSER 2 | #define POLYHEDRUS_ALLPASS_DIFFUSER 3 | 4 | #include "AudioLib/LcgRandom.h" 5 | 6 | namespace Polyhedrus 7 | { 8 | class SchroederAllpass 9 | { 10 | public: 11 | static const int DelayBufferSamples = 9600; // 50ms at 192Khz 12 | 13 | private: 14 | float* delayBuffer; 15 | float* output; 16 | int bufferSize; 17 | int index; 18 | public: 19 | 20 | int SampleDelay; 21 | float Feedback; 22 | 23 | SchroederAllpass(); 24 | ~SchroederAllpass(); 25 | void Initialize(int bufferSize); 26 | 27 | float* GetOutput(); 28 | void ClearBuffers(); 29 | float Process(float input); 30 | void Process(float* input, int sampleCount); 31 | private: 32 | float Get(int delay); 33 | }; 34 | 35 | class AllpassDiffuser 36 | { 37 | public: 38 | static const int StageCount = 8; 39 | 40 | private: 41 | int samplerate; 42 | SchroederAllpass filters[StageCount]; 43 | AudioLib::LcgRandom random; 44 | int delay; 45 | 46 | public: 47 | AllpassDiffuser(); 48 | ~AllpassDiffuser(); 49 | void Initialize(int bufferSize, int samplerate); 50 | float* GetOutput(); 51 | 52 | float Process(float input); 53 | void Process(float* input, int sampleCount); 54 | void ClearBuffers(); 55 | void UpdateParameters(float sampleDelay, float feedback); 56 | }; 57 | } 58 | 59 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/Fft/BitReverse.cpp: -------------------------------------------------------------------------------- 1 | #include "BitReverse.h" 2 | 3 | std::map BitReverse::Tables; 4 | std::map BitReverse::Bitsize; 5 | 6 | void BitReverse::Setup() 7 | { 8 | BitReverse::Bitsize[4] = 2; 9 | BitReverse::Bitsize[8] = 3; 10 | BitReverse::Bitsize[16] = 4; 11 | BitReverse::Bitsize[32] = 5; 12 | BitReverse::Bitsize[64] = 6; 13 | BitReverse::Bitsize[128] = 7; 14 | BitReverse::Bitsize[256] = 8; 15 | BitReverse::Bitsize[512] = 9; 16 | BitReverse::Bitsize[1024] = 10; 17 | BitReverse::Bitsize[2048] = 11; 18 | BitReverse::Bitsize[4096] = 12; 19 | BitReverse::Bitsize[8192] = 13; 20 | BitReverse::Bitsize[16384] = 14; 21 | BitReverse::Bitsize[32768] = 15; 22 | BitReverse::Bitsize[65536] = 16; 23 | 24 | int i = 4; 25 | while (i <= 65536) 26 | { 27 | Generate(i); 28 | i = i * 2; 29 | } 30 | } 31 | 32 | void BitReverse::Generate(int size) 33 | { 34 | int bits = Bitsize[size]; 35 | int* table = new int[size](); 36 | Tables[size] = table; 37 | 38 | for(int i = 0; i < size; i++) 39 | { 40 | table[i] = Reverse(i, bits); 41 | } 42 | } 43 | 44 | int BitReverse::Reverse(int input, int bits) 45 | { 46 | int output = 0; 47 | for(int i = 0; i < bits; i++) 48 | { 49 | int mask = 1 << i; 50 | int outmask = 1 << (bits - 1 - i); 51 | if ((mask & input) > 0) 52 | output += outmask; 53 | } 54 | 55 | return output; 56 | } 57 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/SavePresetDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Shapes; 14 | using LowProfile.Core.Extensions; 15 | 16 | namespace Polyhedrus.Ui.Components 17 | { 18 | /// 19 | /// Interaction logic for SavePresetDialog.xaml 20 | /// 21 | public partial class SavePresetDialog : Window 22 | { 23 | public SavePresetDialog() 24 | { 25 | InitializeComponent(); 26 | this.IsVisibleChanged += (s, x) => 27 | { 28 | this.Center(); 29 | MainTextBox.Focus(); 30 | }; 31 | } 32 | 33 | public bool Cancelled { get; private set; } 34 | 35 | public string ShowDialog(string title) 36 | { 37 | TitleLabel.Content = title; 38 | this.ShowDialog(); 39 | return Cancelled ? null : MainTextBox.Text; 40 | } 41 | 42 | private void Save(object sender, RoutedEventArgs e) 43 | { 44 | Cancelled = false; 45 | Close(); 46 | } 47 | 48 | private void Cancel(object sender, RoutedEventArgs e) 49 | { 50 | Cancelled = true; 51 | Close(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Optimizations/1-Character.md: -------------------------------------------------------------------------------- 1 | ### Character Section 2 | 3 | Initial Results from test: 4 | 5 | PerftCharacter 6 | Seconds,TimeMillis 7 | 500,693 8 | 500,675 9 | 500,681 10 | 500,679 11 | 500,679 12 | 500,683 13 | 500,683 14 | 15 | with audio processing loop commented out (only update runs, this is lower bound on performance) 16 | 17 | PerftCharacter 18 | Seconds,TimeMillis 19 | 500,208 20 | 500,221 21 | 500,211 22 | 500,212 23 | 500,204 24 | 25 | after (implicit) vectorization of Reduce loops: 26 | 27 | PerftCharacter 28 | Seconds,TimeMillis 29 | 500,596 30 | 500,595 31 | 500,602 32 | 500,608 33 | 500,610 34 | 500,609 35 | 500,605 36 | 37 | after (explicit) vectorization of Clip loops: 38 | 39 | PerftCharacter 40 | Seconds,TimeMillis 41 | 500,547 42 | 500,549 43 | 500,526 44 | 500,530 45 | 500,539 46 | 500,528 47 | 500,537 48 | 49 | after improving Out of order execution in Biquad: 50 | 51 | litte/no change 52 | 53 | So far, about a 30% speed improvement in the processing loop. 54 | 55 | Profiler shows the if() statement in the decimation loop as being fairly costly. My previous attempts to eliminate it proved more expensive. 56 | 57 | Unfortunately that's as low as I seem to be able to get it for now. 58 | 59 | I also tested using aligned malloc to allocate the buffer, it did not change anything (possibly already aligned correctly, or my CPU is not that sensitive to unaligned data). -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/VoiceIndicator.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Character.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_CHARACTER 2 | #define POLYHEDRUS_CHARACTER 3 | 4 | #include "AudioLib/Biquad.h" 5 | #include "Parameters.h" 6 | #include "AudioLib/Biquad.h" 7 | 8 | 9 | namespace Polyhedrus 10 | { 11 | class Character 12 | { 13 | public: 14 | bool IsEnabled; 15 | int Note; 16 | 17 | float Bottom; 18 | float Top; 19 | float Decimate; 20 | float Reduce; 21 | float Clip; 22 | 23 | float BottomMod; 24 | float TopMod; 25 | float DecimateMod; 26 | float ReduceMod; 27 | float ClipMod; 28 | 29 | private: 30 | float* buffer; 31 | int samplerate; 32 | int modulationUpdateRate; 33 | AudioLib::Biquad bqBottom; 34 | AudioLib::Biquad bqTop; 35 | int biquadUpdateCounter; 36 | 37 | float decimateCounter; 38 | float lastDecimateVal; 39 | 40 | float glideRate; 41 | float currentPitch; 42 | 43 | float clip; 44 | float decimateFactor; 45 | float bitReduceFactor; 46 | float bitReduceFactorInv; 47 | bool reduceOn; 48 | bool clipOn; 49 | 50 | public: 51 | Character(); 52 | ~Character(); 53 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate); 54 | void SetParameter(CharacterParameters parameter, double value); 55 | void Process(float* input, int len); 56 | float* GetOutput(); 57 | void SetGlide(float value); 58 | void Update(); 59 | std::vector GetVisual(CharacterParameters parameter, int* baseLevel); 60 | }; 61 | } 62 | 63 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/Osc/OscMessage.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_OSC_MESSAGE 2 | #define POLYHEDRUS_OSC_MESSAGE 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using std::vector; 9 | using std::string; 10 | 11 | namespace Polyhedrus 12 | { 13 | class OscMessage 14 | { 15 | public: 16 | string Address; 17 | vector TypeTags; 18 | vector Data; 19 | vector ArgumentIndices; 20 | private: 21 | bool isWriteable; 22 | public: 23 | 24 | static vector ParseRawBytes(vector byteData); 25 | static vector ParseBundle(vector dataBundle); 26 | 27 | OscMessage(vector data); 28 | OscMessage(std::string address); 29 | 30 | void SetInt(int value); 31 | void SetFloat(float value); 32 | void SetString(std::string value); 33 | void SetBlob(vector data); 34 | vector GetBytes(); 35 | 36 | int GetInt(int argumentIndex); 37 | float GetFloat(int argumentIndex); 38 | string GetString(int argumentIndex); 39 | vector GetBlob(int argumentIndex); 40 | 41 | private: 42 | int ParseAddress(uint8_t* data, int parseIndex); 43 | int ParseTypeTags(uint8_t* data, int parseIndex); 44 | 45 | int ParseInt(uint8_t* data, int parseIndex); 46 | int ParseFloat(uint8_t* data, int parseIndex); 47 | int ParseString(uint8_t* data, int parseIndex); 48 | int ParseBlob(uint8_t* data, int parseIndex); 49 | 50 | int ParseNulls(uint8_t* data, int parseIndex); 51 | }; 52 | } 53 | #endif 54 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Drive.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_DRIVE 2 | #define POLYHEDRUS_DRIVE 3 | 4 | #include "Parameters.h" 5 | #include "AudioLib/OnePoleFilters.h" 6 | #include "AudioLib/Utils.h" 7 | #include 8 | 9 | namespace Polyhedrus 10 | { 11 | enum class DriveType 12 | { 13 | None = 0, 14 | Clip = 1, 15 | Tanh = 2, 16 | Asym = 3, 17 | }; 18 | 19 | class Drive 20 | { 21 | public: 22 | float Gain; 23 | float Bias; 24 | bool Post; 25 | bool IsEnabled; 26 | DriveType Type; 27 | float Mellow; 28 | float Volume; 29 | 30 | float GainMod; 31 | float BiasMod; 32 | float MellowMod; 33 | 34 | private: 35 | float* buffer; 36 | int samplerate; 37 | AudioLib::Lp1 lp; 38 | AudioLib::Hp1 hp; 39 | float gainTotal; 40 | float biasTotal; 41 | 42 | public: 43 | Drive(); 44 | ~Drive(); 45 | void Initialize(int samplerate, int bufferSize); 46 | void SetParameter(DriveParameters parameter, double value); 47 | void Process(float* input, int len); 48 | inline float* GetOutput() { return buffer; } 49 | private: 50 | void Update(); 51 | inline static float None(float x) { return x; } 52 | inline static float Asym(float x) { return x <= -1 ? -1.0f : (x > 2.0f ? AsymCurve[299999] : AsymCurve[(int)((x + 1) * 100000)]); } 53 | inline static float Clip(float x) { return AudioLib::Utils::Limit(x, -1.0f, 1.0f); } 54 | inline static float Tanh(float x) { return AudioLib::Utils::TanhLookup(x); } 55 | 56 | static bool curvesInitialized; 57 | static float AsymCurve[300000]; 58 | static void InitCurves(); 59 | }; 60 | } 61 | 62 | #endif -------------------------------------------------------------------------------- /CreateWavetables/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CreateWavetables")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CreateWavetables")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0e526da3-24e4-46f8-b85f-93cbb15aca38")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Polyhedrus.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Polyhedrus.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Polyhedrus.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9c0b7331-b39c-49c5-96a6-30ead1eb0e0c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Polyhedrus.Plugin/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Polyhedrus.Plugin")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Polyhedrus.Plugin")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("99c70666-72ed-4004-8119-7bca1d18fc52")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Performance1.psess: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Polyhedrus.sln 5 | Instrumentation 6 | None 7 | true 8 | true 9 | Timestamp 10 | Cycles 11 | 10000000 12 | 10 13 | 10 14 | 15 | false 16 | 17 | 18 | 19 | false 20 | 500 21 | 22 | \Memory\Pages/sec 23 | \PhysicalDisk(_Total)\Avg. Disk Queue Length 24 | \Processor(_Total)\% Processor Time 25 | 26 | 27 | 28 | true 29 | false 30 | 31 | false 32 | 33 | 34 | false 35 | 36 | -------------------------------------------------------------------------------- /Polyhedrus.Native/ModMatrix.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_MOD_MATRIX 2 | #define POLYHEDRUS_MOD_MATRIX 3 | 4 | #include "ModSourceDest.h" 5 | #include "Default.h" 6 | 7 | namespace Polyhedrus 8 | { 9 | class ModRoute 10 | { 11 | public: 12 | ModSource Source; 13 | ModDest Destination; 14 | ModSource ViaSource; 15 | float Amount; 16 | float ViaAmount; 17 | }; 18 | 19 | class ModMatrix 20 | { 21 | public: 22 | static const int FixedRouteCount = 10; 23 | static const int MatrixCount = 16; 24 | 25 | static const int FixedRouteOscAllPitchbend = 0; 26 | static const int FixedRouteOscAllUnisonDetune = 1; 27 | static const int FixedRouteOscAllUnisonSpread = 2; 28 | 29 | static const int FixedRouteFilterHpKeytrack = 3; 30 | static const int FixedRouteFilterMainKeytrack = 4; 31 | static const int FixedRouteFilterHpEnv = 5; 32 | static const int FixedRouteFilterMainEnv = 6; 33 | 34 | static const int FixedRouteSlop1 = 7; 35 | static const int FixedRouteSlop2 = 8; 36 | static const int FixedRouteSlop3 = 9; 37 | 38 | float* voiceTuning; 39 | float ModSourceValues[(unsigned int)ModSource::Count]; 40 | float ModDestinationValues[(unsigned int)ModDest::Count]; 41 | ModRoute FixedRoutes[FixedRouteCount]; 42 | ModRoute Matrix[MatrixCount]; 43 | 44 | public: 45 | ModMatrix(); 46 | ~ModMatrix(); 47 | void Process(); 48 | 49 | private: 50 | void CreateFixedRoutes(); 51 | void ApplyRoute(ModRoute* route); 52 | void ApplyVoiceTuning(); 53 | void SumOscAllRoutes(); 54 | }; 55 | } 56 | #endif 57 | 58 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/ContextMenus.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/MenuSelector.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Wavetable Editor/WtOsc.js: -------------------------------------------------------------------------------- 1 | 2 | window.WtOsc = (function () { 3 | 4 | var wtOsc = {}; 5 | 6 | var audioCtx = new AudioContext(); 7 | var scriptNode = audioCtx.createScriptProcessor(512, 1, 1); 8 | 9 | var sinewave = []; 10 | for (var i = 0; i < 128; i++) { 11 | sinewave[i] = Math.sin(i / 128 * 2 * Math.PI); 12 | } 13 | 14 | wtOsc.Fs = audioCtx.sampleRate; 15 | wtOsc.wave = sinewave; 16 | wtOsc.iterator = 0; 17 | wtOsc.activeNote = null; 18 | wtOsc.increment = 0.01; 19 | wtOsc.scriptNode = scriptNode; // Need to keep a reference to this, otherwise it seems that Chrome Garbage collects the items and stops running the process loop! 20 | 21 | scriptNode.onaudioprocess = function (e) { 22 | try 23 | { 24 | var iterator = wtOsc.iterator; 25 | var wave = wtOsc.wave; 26 | var increment = wtOsc.increment; 27 | var len = wave.length; 28 | var output = e.outputBuffer.getChannelData(0); 29 | var volume = wtOsc.activeNote === null ? 0.0 : 0.2; 30 | 31 | for (var n = 0; n < output.length; n++) { 32 | 33 | if (iterator >= 1.0) 34 | iterator -= 1.0; 35 | 36 | output[n] = wave[Math.floor(iterator * len)] * volume; 37 | iterator += increment; 38 | } 39 | 40 | wtOsc.iterator = iterator; 41 | } 42 | catch (ex) 43 | { 44 | console.log(ex.message); 45 | } 46 | } 47 | 48 | scriptNode.connect(audioCtx.destination); 49 | 50 | return wtOsc; 51 | })(); 52 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Arpeggiator.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_ARPEGGIATOR 2 | #define POLYHEDRUS_ARPEGGIATOR 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Parameters.h" 8 | #include "VoiceAllocator.h" 9 | 10 | namespace Polyhedrus 11 | { 12 | enum class ArpPattern 13 | { 14 | Up = 0, 15 | Down = 1, 16 | UpDown1 = 2, 17 | UpDown2 = 3, 18 | DownUp1 = 4, 19 | DownUp2 = 5 20 | }; 21 | 22 | class Arpeggiator 23 | { 24 | public: 25 | int Range; 26 | ArpPattern NotePtn; 27 | ArpPattern OctavePtn; 28 | float Gate; 29 | float Divide; 30 | int Bpm; 31 | bool Sync; 32 | 33 | private: 34 | bool isEnabled; 35 | int samplerate; 36 | VoiceAllocator* voiceAllocator; 37 | double notePhase; 38 | 39 | std::unordered_set heldNotes; 40 | std::unordered_set playingNotes; 41 | int currentNote; 42 | bool trigger; 43 | 44 | // stores a precomputed pattern for the entire arp sequence 45 | vector pattern; 46 | int patternIndex; 47 | 48 | public: 49 | Arpeggiator(); 50 | ~Arpeggiator(); 51 | void Initialize(int samplerate, VoiceAllocator* voiceAllocator); 52 | void SetParameter(ArpParameters parameter, double value); 53 | void Process(int len); 54 | 55 | void SetEnabled(bool isEnabled); 56 | void NoteOn(uint8_t note, float velocity); 57 | void NoteOff(uint8_t note); 58 | 59 | private: 60 | void ComputeFullPattern(); 61 | vector GetOctPattern(); 62 | vector Arpeggiator::MakePattern(ArpPattern pattern, vector& input); 63 | int RecomputeCurrentIndex(const vector& newPattern); 64 | }; 65 | } 66 | 67 | #endif 68 | 69 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterCascade.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_FILTER_CASCADE 2 | #define POLYHEDRUS_FILTER_CASCADE 3 | 4 | #include 5 | #include "FilterInternalMode.h" 6 | 7 | namespace Polyhedrus 8 | { 9 | class FilterCascade 10 | { 11 | private: 12 | static const int CVtoAlphaSize = 10500; 13 | static float CVtoAlpha[CVtoAlphaSize]; 14 | static void ComputeCVtoAlpha(int samplerate); 15 | public: 16 | static inline float GetCvFreq(float cv) 17 | { 18 | // Voltage is 1V/OCt, C0 = 16.3516Hz 19 | // 10.3V = Max = 20614.33hz 20 | float freq = (float)(440.0 * std::pow(2, (cv * 12 - 69.0 + 12) / 12)); 21 | return freq; 22 | } 23 | 24 | public: 25 | const int Oversample = 2; 26 | 27 | float Drive; 28 | float Cutoff; 29 | float Resonance; 30 | float CutoffMod; 31 | float ResonanceMod; 32 | float DriveMod; 33 | 34 | float c0, c1, c2, c3, c4; 35 | 36 | private: 37 | float* buffer; 38 | float gain; 39 | float totalResonance; 40 | float oversampledInput; 41 | 42 | float p; 43 | float x; 44 | float a; 45 | float b; 46 | float c; 47 | float d; 48 | float feedback; 49 | 50 | float fsinv; 51 | int samplerate; 52 | int modulationUpdateRate; 53 | float gInv; 54 | float mx; 55 | 56 | public: 57 | FilterCascade(); 58 | ~FilterCascade(); 59 | 60 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate); 61 | void Process(float* input, int len); 62 | inline float* GetOutput() { return buffer; } 63 | void SetMode(InternalFilterMode mode); 64 | 65 | private: 66 | float ProcessSample(float input); 67 | void Update(); 68 | }; 69 | } 70 | 71 | #endif 72 | 73 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/FlatToggleButton.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 15 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/LcgRandom.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_LCGRANDOM 2 | #define AUDIOLIB_LCGRANDOM 3 | 4 | #include 5 | #include "Sse.h" 6 | #include "Utils.h" 7 | 8 | namespace AudioLib 9 | { 10 | class LcgRandom 11 | { 12 | private: 13 | uint64_t x; 14 | uint64_t a; 15 | uint64_t c; 16 | //uint64_t m; 17 | 18 | double doubleInv; 19 | float floatUintInv; 20 | float floatIntInv; 21 | 22 | public: 23 | inline LcgRandom(uint64_t seed = 0) 24 | { 25 | x = seed; 26 | //m = 1 << 32; 27 | a = 22695477; 28 | c = 1; 29 | 30 | doubleInv = 1.0 / (double)UINT32_MAX; 31 | floatUintInv = 1.0 / (float)UINT32_MAX; 32 | floatIntInv = 1.0 / (float)INT32_MAX; 33 | } 34 | 35 | inline void SetSeed(uint64_t seed) 36 | { 37 | x = seed; 38 | } 39 | 40 | inline uint32_t NextUInt() 41 | { 42 | uint64_t axc = a * x + c; 43 | //x = axc % m; 44 | x = axc & 0xFFFFFFFF; 45 | return (uint32_t)x; 46 | } 47 | 48 | inline int32_t NextInt() 49 | { 50 | int64_t axc = a * x + c; 51 | //x = axc % m; 52 | x = axc & 0x7FFFFFFF; 53 | return (int32_t)x; 54 | } 55 | 56 | inline double NextDouble() 57 | { 58 | auto n = NextUInt(); 59 | return n * doubleInv; 60 | } 61 | 62 | inline float NextFloat() 63 | { 64 | auto n = NextInt(); 65 | return n * floatIntInv; 66 | } 67 | 68 | inline void GetFloats(float* buffer, int len) 69 | { 70 | for (int i = 0; i < len; i++) 71 | buffer[i] = NextFloat(); 72 | } 73 | 74 | inline void GetFloatsBipolar(float* buffer, int len) 75 | { 76 | for (int i = 0; i < len; i++) 77 | buffer[i] = NextFloat() * 2 - 1; 78 | } 79 | }; 80 | } 81 | 82 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/OnePoleFilters.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_ONEPOLEFILTERS 2 | #define AUDIOLIB_ONEPOLEFILTERS 3 | 4 | #include "MathDefs.h" 5 | #include 6 | 7 | namespace AudioLib 8 | { 9 | class Lp1 10 | { 11 | private: 12 | float z1_state; 13 | //float g; 14 | float g2; 15 | public: 16 | Lp1() 17 | { 18 | z1_state = 0; 19 | g2 = 0; 20 | } 21 | 22 | inline float Process(float x) 23 | { 24 | // perform one sample tick of the lowpass filter 25 | //float v = (x - z1_state) * g / (1 + g); 26 | float v = (x - z1_state) * g2; 27 | float y = v + z1_state; 28 | z1_state = y + v; 29 | return y; 30 | } 31 | 32 | inline float ProcessNoUpdate(float x) 33 | { 34 | // perform one sample tick of the lowpass filter 35 | //float v = (x - z1_state) * g / (1 + g); 36 | float v = (x - z1_state) * g2; 37 | return v + z1_state; 38 | } 39 | 40 | // 0...1 41 | inline void SetFc(float fcRel) 42 | { 43 | //this->g = fcRel * M_PI; 44 | float g = (float)(fcRel * M_PI); 45 | g2 = g / (1 + g); 46 | } 47 | }; 48 | 49 | class Hp1 50 | { 51 | private: 52 | float z1_state; 53 | //float g; 54 | float g2; 55 | public: 56 | Hp1() 57 | { 58 | z1_state = 0; 59 | g2 = 0; 60 | } 61 | 62 | inline float Process(float x) 63 | { 64 | // perform one sample tick of the lowpass filter 65 | //float v = (x - z1_state) * g / (1 + g); 66 | float v = (x - z1_state) * g2; 67 | float y = v + z1_state; 68 | z1_state = y + v; 69 | return x - y; 70 | } 71 | 72 | // 0...1 73 | inline void SetFc(float fcRel) 74 | { 75 | //this->g = fcRel * M_PI; 76 | float g = (float)(fcRel * M_PI); 77 | g2 = g / (1 + g); 78 | } 79 | }; 80 | } 81 | 82 | #endif 83 | 84 | -------------------------------------------------------------------------------- /Wavetables/Additive/Additive-1.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | 4 | wavetableCountTextBox.value = "64"; 5 | wavetableSizeTextBox.value = "2048"; 6 | downloadLink.download = "Additive-1.wav"; 7 | 8 | // random 9 | var x = 2; 10 | var a = 22695477; 11 | var c = 1; 12 | 13 | var getRand = function() { 14 | var axc = a * x + c; 15 | //x = axc % m; 16 | x = axc % 0xFFFFFFFF; 17 | return x / 4294967295.0; 18 | 19 | }; 20 | 21 | tones = []; 22 | 23 | for (var k = 0; k < K; k++) { 24 | tones.push([]); 25 | 26 | for (var u = 0; u < 8; u++) { 27 | var tone = 1 + getRand() * (k * 2 + 32); 28 | tones[k].push({tone: Math.round(tone), vol: getRand() / tone }); 29 | } 30 | } 31 | 32 | var summer = function(arr) { 33 | var result = 0.0; 34 | for (var i = 0; i < arr.length; i++) 35 | //result += arr[i].vol * arr[i].tone * arr[i].tone; 36 | result = arr[i].tone > result ? arr[i].tone : result;; 37 | 38 | 39 | return result; 40 | } 41 | 42 | 43 | tones.sort(function(a, b) { 44 | 45 | return summer(a) - summer(b); 46 | 47 | 48 | }); 49 | 50 | } 51 | 52 | wt.makeWave = function (k, N, K) { 53 | var output = new Array(N); 54 | 55 | for (var i = 0; i < N; i++) { 56 | var phase = i / N * 2 * Math.PI; 57 | var value = 0.0; 58 | var totalVol = 0.0; 59 | 60 | for (var u = 0; u < tones[k].length; u++) { 61 | var vol = tones[k][u].vol; 62 | totalVol += vol; 63 | value += Math.sin(phase * tones[k][u].tone) * vol; 64 | } 65 | 66 | output[i] = value / totalVol; 67 | } 68 | 69 | return output; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterTrueZero.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "CvFreq.h" 4 | #include "AudioLib/OnePoleFilters.h" 5 | #include "FilterMain.h" 6 | 7 | namespace Polyhedrus 8 | { 9 | class FilterTrueZero : public FilterMain 10 | { 11 | private: 12 | static const int CVtoAlphaSize = 10500; 13 | static float CVtoAlpha[CVtoAlphaSize]; 14 | static void ComputeCVtoAlpha(int samplerate); 15 | public: 16 | static inline float GetCvFreq(float cv) 17 | { 18 | // Voltage is 1V/OCt, C0 = 16.3516Hz 19 | // 10.3V = Max = 20614.33hz 20 | float freq = (float)(440.0 * std::pow(2, (cv * 12 - 69.0 + 12) / 12)); 21 | return freq; 22 | } 23 | 24 | private: 25 | CvFreq CvToFreq; 26 | float* buffer; 27 | float gain; 28 | float gainInv; 29 | float totalResonance; 30 | 31 | AudioLib::Lp1 stage1; 32 | AudioLib::Lp1 stage2; 33 | AudioLib::Lp1 stage3; 34 | AudioLib::Lp1 stage4; 35 | float stage4Output; 36 | float convergenceRate; 37 | float outputDiff; 38 | int cntr; 39 | 40 | float fsinv; 41 | int samplerate; 42 | int bufferSize; 43 | int modulationUpdateRate; 44 | 45 | public: 46 | FilterTrueZero(); 47 | ~FilterTrueZero() override; 48 | 49 | FilterMainType GetType() const override; 50 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate) override; 51 | void SetParameter(FilterMainParameters parameter, double value) override; 52 | void Process(float* input, int len) override; 53 | inline float* GetOutput() override; 54 | 55 | std::vector GetVisual() override; 56 | std::vector GetDriveVisual() override; 57 | std::string GetModeString() override; 58 | 59 | private: 60 | void ProcessSample(float input); 61 | void Update(); 62 | }; 63 | } 64 | 65 | -------------------------------------------------------------------------------- /Wavetables/Additive/Additive-1-Hard.txt: -------------------------------------------------------------------------------- 1 | 2 | wt.initialize = function(N, K) { 3 | 4 | wavetableCountTextBox.value = "64"; 5 | wavetableSizeTextBox.value = "2048"; 6 | downloadLink.download = "Additive-1-Hard.wav"; 7 | 8 | // random 9 | var x = 2; 10 | var a = 22695477; 11 | var c = 1; 12 | 13 | var getRand = function() { 14 | var axc = a * x + c; 15 | //x = axc % m; 16 | x = axc % 0xFFFFFFFF; 17 | return x / 4294967295.0; 18 | 19 | }; 20 | 21 | tones = []; 22 | 23 | for (var k = 0; k < K; k++) { 24 | tones.push([]); 25 | 26 | for (var u = 0; u < 8; u++) { 27 | var tone = 1 + getRand() * (k * 2 + 32); 28 | tones[k].push({tone: Math.round(tone), vol: getRand() / tone }); 29 | } 30 | } 31 | 32 | var summer = function(arr) { 33 | var result = 0.0; 34 | for (var i = 0; i < arr.length; i++) 35 | //result += arr[i].vol * arr[i].tone * arr[i].tone; 36 | result = arr[i].tone > result ? arr[i].tone : result;; 37 | 38 | 39 | return result; 40 | } 41 | 42 | 43 | tones.sort(function(a, b) { 44 | 45 | return summer(a) - summer(b); 46 | 47 | 48 | }); 49 | 50 | } 51 | 52 | wt.makeWave = function (k, N, K) { 53 | var output = new Array(N); 54 | 55 | for (var i = 0; i < N; i++) { 56 | var phase = i / N * 2 * Math.PI; 57 | var value = 0.0; 58 | var totalVol = 0.0; 59 | 60 | for (var u = 0; u < tones[k].length; u++) { 61 | var vol = tones[k][u].vol; 62 | totalVol += vol; 63 | value += Math.sin(phase * tones[k][u].tone) * vol; 64 | } 65 | 66 | output[i] = Math.tanh(3 * value / totalVol); 67 | } 68 | 69 | return output; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/Spinner.xaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | 25 | 26 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/SlopGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_SLOP_GENERATOR 2 | #define AUDIOLIB_SLOP_GENERATOR 3 | 4 | #include 5 | #include "MathDefs.h" 6 | #include "LcgRandom.h" 7 | 8 | namespace AudioLib 9 | { 10 | class SlopGenerator 11 | { 12 | public: 13 | float Output; 14 | float Fc; 15 | 16 | inline SlopGenerator() 17 | { 18 | Output = 0.0f; 19 | Fc = 0.1f; 20 | 21 | fcSqrtInv = 0.1f; 22 | alpha = 0.1f; 23 | value = 0.0f; 24 | zState1 = 0.0f; 25 | zState2 = 0.0f; 26 | samplerate = 48000; 27 | fsInv = 1.0f / 48000.0f; 28 | updateShRate = 10; 29 | updateCounter = 0; 30 | } 31 | 32 | inline void Initialize(int samplerate, int modulationUpdateRate, int seed) 33 | { 34 | this->samplerate = samplerate / modulationUpdateRate; 35 | fsInv = 1.0f / this->samplerate; 36 | rand.SetSeed(seed * 12345); 37 | 38 | updateShRate = this->samplerate / 1000; 39 | updateCounter = 0; 40 | } 41 | 42 | inline void Update() 43 | { 44 | alpha = (float)std::sin(M_PI * Fc * fsInv); 45 | fcSqrtInv = 1.0f / std::sqrt(Fc); 46 | } 47 | 48 | inline float Process() 49 | { 50 | if (updateCounter == 0) 51 | { 52 | Update(); 53 | value = 2.0f * rand.NextFloat() - 1.0f; 54 | updateCounter = updateShRate; 55 | } 56 | 57 | zState1 = zState1 * (1.0f - alpha) + value * alpha; 58 | zState2 = zState2 * (1.0f - alpha) + zState1 * alpha; 59 | 60 | Output = zState2 * fcSqrtInv * 20; // normalized to 1Hz 61 | updateCounter--; 62 | return Output; 63 | } 64 | 65 | private: 66 | float fcSqrtInv; 67 | float alpha; 68 | float value; 69 | float zState1; 70 | float zState2; 71 | int samplerate; 72 | float fsInv; 73 | int updateShRate; 74 | int updateCounter; 75 | LcgRandom rand; 76 | }; 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Osc/UdpTranceiver.cpp: -------------------------------------------------------------------------------- 1 | #include "UdpTranceiver.h" 2 | 3 | namespace Polyhedrus 4 | { 5 | UdpTranceiver::UdpTranceiver(int receivePort, int sendPort) 6 | : UdpTranceiver(receivePort, sendPort, "127.0.0.1") 7 | { 8 | 9 | } 10 | 11 | UdpTranceiver::UdpTranceiver(int receivePort, int sendPort, std::string sendIp) 12 | { 13 | // ----- Setup Sending ----- 14 | 15 | if (sendPort != 0) 16 | { 17 | udp::resolver resolver(ioServiceSend); 18 | udp::resolver::query query(udp::v4(), sendIp, std::to_string(sendPort)); 19 | sendEndpoint = *resolver.resolve(query); 20 | sendPortEnabled = true; 21 | } 22 | else 23 | { 24 | sendPortEnabled = false; 25 | } 26 | 27 | // Open a random socket for sending 28 | sendSocket = new udp::socket(ioServiceSend); 29 | sendSocket->open(udp::v4()); 30 | 31 | // ------ Setup Receiving ------- 32 | 33 | receiveSocket = new udp::socket(ioServiceReceive, udp::endpoint(udp::v4(), receivePort)); 34 | } 35 | 36 | UdpTranceiver::~UdpTranceiver() 37 | { 38 | delete sendSocket; 39 | delete receiveSocket; 40 | } 41 | 42 | std::vector UdpTranceiver::Receive() 43 | { 44 | udp::endpoint remote_endpoint; 45 | boost::system::error_code error; 46 | int available = receiveSocket->available(); 47 | if (available == 0) 48 | return std::vector(); 49 | 50 | int byteCount = receiveSocket->receive_from(boost::asio::buffer(receiveBuffer), remote_endpoint, 0, error); 51 | 52 | std::vector output; 53 | 54 | for (int i = 0; i < byteCount; i++) 55 | output.push_back(receiveBuffer[i]); 56 | 57 | return output; 58 | } 59 | 60 | void UdpTranceiver::Send(std::vector message) 61 | { 62 | if (sendPortEnabled) 63 | sendSocket->send_to(boost::asio::buffer(message), sendEndpoint); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /Wavetable Editor/Wavetable Editor.sln.exclude: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Wavetable Editor", ".", "{C3A8B3CC-B5E4-4045-83ED-03360FE48075}" 7 | ProjectSection(WebsiteProperties) = preProject 8 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" 9 | Debug.AspNetCompiler.VirtualPath = "/localhost_30634" 10 | Debug.AspNetCompiler.PhysicalPath = "..\Wavetable Editor\" 11 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_30634\" 12 | Debug.AspNetCompiler.Updateable = "true" 13 | Debug.AspNetCompiler.ForceOverwrite = "true" 14 | Debug.AspNetCompiler.FixedNames = "false" 15 | Debug.AspNetCompiler.Debug = "True" 16 | Release.AspNetCompiler.VirtualPath = "/localhost_30634" 17 | Release.AspNetCompiler.PhysicalPath = "..\Wavetable Editor\" 18 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_30634\" 19 | Release.AspNetCompiler.Updateable = "true" 20 | Release.AspNetCompiler.ForceOverwrite = "true" 21 | Release.AspNetCompiler.FixedNames = "false" 22 | Release.AspNetCompiler.Debug = "False" 23 | VWDPort = "30634" 24 | SlnRelativePath = "..\Wavetable Editor\" 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {C3A8B3CC-B5E4-4045-83ED-03360FE48075}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {C3A8B3CC-B5E4-4045-83ED-03360FE48075}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /Polyhedrus.Tests/BasicTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using OxyPlot; 4 | using LowProfile.Visuals; 5 | using System.Linq; 6 | using Polyhedrus.Plugin; 7 | 8 | namespace Polyhedrus.Tests 9 | { 10 | [TestClass] 11 | public class BasicTests 12 | { 13 | [TestMethod] 14 | public void TestMethod1() 15 | { 16 | var t = new PolyhedrusNative(48000, 12003, 12004); 17 | var data = t.GetWavetable(0); 18 | 19 | var pm = new PlotModel(); 20 | pm.AddLine(data.WavetableData.Take(20000)); 21 | pm.Show(); 22 | } 23 | 24 | [TestMethod] 25 | public void RunCppTests() 26 | { 27 | PolyhedrusNative.RunTests(); 28 | Assert.IsTrue(true); 29 | } 30 | 31 | [TestMethod] 32 | public void TestMethodSlop() 33 | { 34 | var data = new double[48000]; 35 | var dataF = new double[48000]; 36 | var random = new Random(0); 37 | 38 | double sum = 0.0; 39 | double filter = 0.0; 40 | var fs = 4800; 41 | Func fcToAlpha = fc => (2 * Math.PI * fc / fs) / (2 * Math.PI * fc / fs + 1); 42 | var freq = 1.0; 43 | var samplesPerPoint = (int)(fs / (freq * 10)); 44 | 45 | var aHp = fcToAlpha(freq * 0.5); 46 | var aLp = fcToAlpha(freq * 0.2); 47 | 48 | var scale = 1.0 / samplesPerPoint; 49 | var sample = 0.0; 50 | var n = 0; 51 | for (int i = 0; i < data.Length; i++) 52 | { 53 | if (n <= 0) 54 | { 55 | sample = (2 * random.NextDouble() - 1) * scale; 56 | n = samplesPerPoint; 57 | } 58 | 59 | sum += sample; 60 | filter = filter * (1 - aLp) + aLp * sum; 61 | data[i] = sum; 62 | dataF[i] = filter; 63 | sum *= (1 - aHp); 64 | 65 | n--; 66 | } 67 | 68 | var pm = new PlotModel(); 69 | pm.AddLine(data); 70 | pm.AddLine(dataF); 71 | pm.Show(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Polyhedrus.Native/NoiseSimple.cpp: -------------------------------------------------------------------------------- 1 | #include "NoiseSimple.h" 2 | #include "AudioLib/Utils.h" 3 | 4 | namespace Polyhedrus 5 | { 6 | NoiseSimple::NoiseSimple() 7 | { 8 | lpL.SetFc(0.01f); 9 | lpR.SetFc(0.01f); 10 | hpL.SetFc(10.0f / 22000.0f); 11 | hpR.SetFc(10.0f / 22000.0f); 12 | bufferL = 0; 13 | bufferR = 0; 14 | randBuf = 0; 15 | brownL = 0.0f; 16 | brownR = 0.0f; 17 | } 18 | 19 | NoiseSimple::~NoiseSimple() 20 | { 21 | delete bufferL; 22 | delete bufferR; 23 | delete randBuf; 24 | } 25 | 26 | void NoiseSimple::Initialize(int samplerate, int bufferSize, int voiceIndex) 27 | { 28 | this->samplerate = samplerate; 29 | this->bufferSize = bufferSize; 30 | 31 | bufferL = new float[bufferSize](); 32 | bufferR = new float[bufferSize](); 33 | 34 | random.Cycle(voiceIndex * 10); 35 | randBuf = new float[2 * bufferSize](); 36 | 37 | output[0] = bufferL; 38 | output[1] = bufferR; 39 | } 40 | 41 | void NoiseSimple::Process(int len) 42 | { 43 | float volWhite = AudioLib::Utils::Limit(1.0f - Type, 0.0f, 1.0f); 44 | float volBrown = AudioLib::Utils::Limit(Type - 1.0f, 0.0f, 1.0f); 45 | float volPink = (1.0f - volWhite - volBrown) * 3.0f; 46 | volBrown *= 0.15f; 47 | // the 0.15 and 3.0 scalars are just tuned by ear. 48 | 49 | random.Sample(randBuf, 2 * len); 50 | 51 | for (int i = 0; i < len; i++) 52 | { 53 | float randL = randBuf[2 * i]; 54 | float randR = randBuf[2 * i + 1]; 55 | 56 | brownL += randL; 57 | brownR += randR; 58 | 59 | // brown 60 | float bl = hpL.Process(brownL); 61 | float br = hpR.Process(brownR); 62 | 63 | // pink 64 | float pl = lpL.Process(randL); 65 | float pr = lpR.Process(randR); 66 | 67 | bufferL[i] = volWhite * randL + volPink * pl + volBrown * bl; 68 | bufferR[i] = volWhite * randR + volPink * pr + volBrown * br; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Polyhedrus.Native/OscillatorBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "AudioLib/Utils.h" 6 | #include "Utils.h" 7 | #include "AudioLib/ValueTables.h" 8 | 9 | namespace Polyhedrus 10 | { 11 | enum class OscillatorType 12 | { 13 | Wavetable = 0, 14 | }; 15 | 16 | class OscillatorBase 17 | { 18 | public: 19 | float Keytrack; 20 | int Note; 21 | int Octave; 22 | int Semi; 23 | int Cent; 24 | float Linear; 25 | float BaseHz; 26 | float BaseNote; 27 | 28 | float Glide; 29 | float GlideFactor; 30 | 31 | float Phase; 32 | float Shape; 33 | 34 | float PitchMod; 35 | float LinearMod; 36 | float ShapeMod; 37 | 38 | float SelfFm; 39 | float* FmBuffer; 40 | 41 | virtual ~OscillatorBase() { } 42 | virtual OscillatorType GetType() const = 0; 43 | virtual void Initialize(int samplerate, int bufferSize, int modulationUpdateRate) = 0; 44 | virtual void UpdateGlide() = 0; 45 | virtual void Reset() = 0; 46 | virtual void Process(int count) = 0; 47 | virtual float* GetOutput() const = 0; 48 | virtual std::vector GetVisual() const = 0; 49 | virtual std::string GetShapeString() const = 0; 50 | 51 | virtual std::string GetLinearString() 52 | { 53 | return SPrint("%0.2f", Linear) + " Hz"; 54 | } 55 | 56 | virtual std::string GetGlideString() 57 | { 58 | return SPrint("%0.2f", GlideFactor) + "x"; 59 | } 60 | 61 | virtual void SetBaseHz(double value) 62 | { 63 | BaseHz = GetLinearFrequency(value); 64 | BaseNote = AudioLib::Utils::Freq2NoteT(BaseHz); 65 | } 66 | 67 | virtual std::string GetBaseHzString() 68 | { 69 | return SPrint("%0.2f", BaseHz) + " Hz"; 70 | } 71 | 72 | inline static float GetLinearFrequency(double value) 73 | { 74 | return (float)(AudioLib::ValueTables::Get(value, AudioLib::ValueTables::Response3Dec) * 10000); 75 | } 76 | 77 | 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Sse.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_SSE 2 | #define AUDIOLIB_SSE 3 | 4 | #include 5 | 6 | namespace AudioLib 7 | { 8 | class Sse 9 | { 10 | public: 11 | 12 | static void Min(float* const input, const float min, const int len) 13 | { 14 | __m128 mini = _mm_load1_ps(&min); 15 | __m128* inputPtr = (__m128*)input; 16 | for (int i = 0; i < (len / 4); i++) 17 | { 18 | inputPtr[i] = _mm_min_ps(inputPtr[i], mini); 19 | } 20 | } 21 | 22 | static void Max(float* const input, const float max, const int len) 23 | { 24 | __m128 maxi = _mm_load1_ps(&max); 25 | __m128* inputPtr = (__m128*)input; 26 | for (int i = 0; i < (len / 4); i++) 27 | { 28 | inputPtr[i] = _mm_max_ps(inputPtr[i], maxi); 29 | } 30 | } 31 | 32 | static inline void Floor(float* const input, const int len) 33 | { 34 | __m128* inputPtr = (__m128*)input; 35 | for (int i = 0; i < len / 4; i++) 36 | { 37 | inputPtr[i] = _mm_floor_ps(inputPtr[i]); 38 | } 39 | } 40 | 41 | static inline void ConvertToFloats(const int* const input, float* const output, const int len) 42 | { 43 | __m128i* inputPtr = (__m128i*)input; 44 | __m128* outputPtr = (__m128*)output; 45 | 46 | for (int i = 0; i < len / 4; i++) 47 | { 48 | outputPtr[i] = _mm_cvtepi32_ps(inputPtr[i]); 49 | } 50 | } 51 | 52 | // Returns a 16-byte aligned array of type T 53 | template 54 | static inline T* AlignedMalloc(int size) 55 | { 56 | T* result = (T*)_aligned_malloc(size * sizeof(T), 16); 57 | return result; 58 | } 59 | 60 | template 61 | static inline void AlignedFree(T* ptr) 62 | { 63 | _aligned_free(ptr); 64 | } 65 | 66 | static inline void PreventDernormals() 67 | { 68 | _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); 69 | _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); 70 | } 71 | 72 | }; 73 | 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Decimator.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_DECIMATOR 2 | #define AUDIOLIB_DECIMATOR 3 | 4 | 5 | namespace AudioLib 6 | { 7 | class Decimator 8 | { 9 | private: 10 | float values[19]; 11 | 12 | public: 13 | static float Coeff[19]; 14 | 15 | inline Decimator() 16 | { 17 | for (int i = 0; i < 19; i++) 18 | values[i] = 0; 19 | } 20 | 21 | inline float Process(float x0, float x1) 22 | { 23 | values[0] = values[2]; 24 | values[1] = values[3]; 25 | values[2] = values[4]; 26 | values[3] = values[5]; 27 | values[4] = values[6]; 28 | values[5] = values[7]; 29 | values[6] = values[8]; 30 | values[7] = values[9]; 31 | values[8] = values[10]; 32 | values[9] = values[11]; 33 | values[10] = values[12]; 34 | values[11] = values[13]; 35 | values[12] = values[14]; 36 | values[13] = values[15]; 37 | values[14] = values[16]; 38 | values[15] = values[17]; 39 | values[16] = values[18]; 40 | values[17] = x0; 41 | values[18] = x1; 42 | 43 | // This is faster than the vectorized version 44 | float output = 45 | ( 46 | ( 47 | Coeff[0] * values[0] + 48 | Coeff[1] * values[1] + 49 | Coeff[2] * values[2] + 50 | Coeff[3] * values[3] + 51 | Coeff[4] * values[4] 52 | ) + 53 | ( 54 | Coeff[5] * values[5] + 55 | Coeff[6] * values[6] + 56 | Coeff[7] * values[7] + 57 | Coeff[8] * values[8] 58 | ) 59 | ) + 60 | ( 61 | ( 62 | Coeff[9] * values[9] + 63 | Coeff[10] * values[10] + 64 | Coeff[11] * values[11] + 65 | Coeff[12] * values[12] + 66 | Coeff[13] * values[13] 67 | ) + 68 | ( 69 | Coeff[14] * values[14] + 70 | Coeff[15] * values[15] + 71 | Coeff[16] * values[16] + 72 | Coeff[17] * values[17] + 73 | Coeff[18] * values[18] 74 | ) 75 | ); 76 | 77 | return output; 78 | } 79 | }; 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /Polyhedrus.Native/FilterCascadeZero.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_FILTER_CASCADE_ZERO 2 | #define POLYHEDRUS_FILTER_CASCADE_ZERO 3 | 4 | #include 5 | 6 | #include "CvFreq.h" 7 | 8 | namespace Polyhedrus 9 | { 10 | struct ZeroDelay2Lp 11 | { 12 | float z1State; 13 | float y; 14 | float g2; 15 | //float g; 16 | 17 | inline float Process(float x) 18 | { 19 | // perform one sample tick of the lowpass filter 20 | //float v = (x - z1State) * g / (1 + g); 21 | 22 | float v = (x - z1State) * g2; 23 | y = v + z1State; 24 | z1State = y + v; 25 | return y; 26 | } 27 | 28 | inline void SetG(float g) 29 | { 30 | //this->g = g; 31 | g2 = g / (1.0f + g); 32 | } 33 | }; 34 | 35 | class FilterCascadeZero 36 | { 37 | public: 38 | static inline float GetCvFreq(float cv) 39 | { 40 | // Voltage is 1V/OCt, C0 = 16.3516Hz 41 | // 10.3V = Max = 20614.33hz 42 | float freq = (float)(440.0 * std::pow(2, (cv * 12 - 69.0 + 12) / 12)); 43 | return freq; 44 | } 45 | 46 | public: 47 | float Drive; 48 | float Cutoff; 49 | float Resonance; 50 | float CutoffMod; 51 | float ResonanceMod; 52 | float DriveMod; 53 | CvFreq CvToFreq; 54 | 55 | private: 56 | float* buffer; 57 | float gain; 58 | float gainInv; 59 | float totalResonance; 60 | 61 | float k; 62 | float uScaler; 63 | float gainCompensation; 64 | float g, g2, g3, g4; 65 | 66 | ZeroDelay2Lp lp1; 67 | ZeroDelay2Lp lp2; 68 | ZeroDelay2Lp lp3; 69 | ZeroDelay2Lp lp4; 70 | 71 | float fsinv; 72 | int samplerate; 73 | int bufferSize; 74 | int modulationUpdateRate; 75 | 76 | public: 77 | FilterCascadeZero(); 78 | ~FilterCascadeZero(); 79 | 80 | void Initialize(int samplerate, int bufferSize, int modulationUpdateRate); 81 | void Process(float* input, int len); 82 | inline float* GetOutput() { return buffer; } 83 | 84 | private: 85 | void ProcessSample(float input); 86 | void Update(); 87 | }; 88 | } 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/OscAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Media; 9 | 10 | namespace Polyhedrus.Ui 11 | { 12 | class OscAddress : FrameworkElement 13 | { 14 | public static string GetAddress(DependencyObject obj) 15 | { 16 | return (string)obj.GetValue(ParameterProperty); 17 | } 18 | 19 | public static void SetAddress(DependencyObject obj, string value) 20 | { 21 | obj.SetValue(ParameterProperty, value); 22 | } 23 | 24 | public static readonly DependencyProperty ParameterProperty = DependencyProperty.RegisterAttached("Address", 25 | typeof(string), typeof(OscAddress), new PropertyMetadata(null, PropertyChanged)); 26 | 27 | private static void PropertyChanged(DependencyObject item, DependencyPropertyChangedEventArgs e) 28 | { 29 | item.SetValue(e.Property, e.NewValue); 30 | } 31 | 32 | public static Dictionary GetChildrenWithValue(DependencyObject depObj) 33 | { 34 | if (depObj is ContentControl && ((ContentControl)depObj).Content is DependencyObject) 35 | return GetChildrenWithValue(((ContentControl)depObj).Content as DependencyObject); 36 | 37 | var output = new Dictionary(); 38 | 39 | if (depObj == null) 40 | return new Dictionary(); 41 | 42 | for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 43 | { 44 | DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 45 | if (child == null) 46 | continue; 47 | 48 | var para = GetAddress(child); 49 | if (para != null) 50 | output[child] = para; 51 | 52 | var subChildren = GetChildrenWithValue(child); 53 | foreach (var subChild in subChildren) 54 | output[subChild.Key] = subChild.Value; 55 | } 56 | 57 | return output; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Modulator.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_MODULATOR 2 | #define POLYHEDRUS_MODULATOR 3 | 4 | #include "Envelope.h" 5 | #include "Parameters.h" 6 | #include "AudioLib/ValueTables.h" 7 | #include "AudioLib/LcgRandom.h" 8 | 9 | namespace Polyhedrus 10 | { 11 | enum class LfoShape 12 | { 13 | Triangle = 0, 14 | Sine = 1, 15 | Saw = 2, 16 | Ramp = 3, 17 | Square = 4, 18 | Pulse1 = 5, 19 | Pulse2 = 6, 20 | SampleHold = 7, 21 | 22 | Count = 8 23 | }; 24 | 25 | class Modulator 26 | { 27 | public: 28 | static const int MaxFreq = 400; 29 | 30 | bool IsEnabled; 31 | float Delay; 32 | float Attack; 33 | float Hold; 34 | float Decay; 35 | float Sustain; 36 | float Release; 37 | 38 | float DelayMod; 39 | float AttackMod; 40 | float HoldMod; 41 | float DecayMod; 42 | float SustainMod; 43 | float ReleaseMod; 44 | 45 | LfoShape Shape; 46 | 47 | float Phase; 48 | float Freq; 49 | float Slew; 50 | float Steps; 51 | bool Sync; 52 | 53 | float FreqMod; 54 | float SlewMod; 55 | float StepsMod; 56 | 57 | float Output; 58 | float OutputUnipolar; 59 | float OutputEnv; 60 | 61 | private: 62 | Envelope env; 63 | int samplerate; 64 | 65 | uint32_t iterator; 66 | uint32_t increment; 67 | bool gate; 68 | float slewPerSample; 69 | int stepCount; 70 | float prevLfoSample; 71 | float slewTotal; 72 | 73 | // for noise 74 | uint32_t prevIterator; 75 | float currentSh; 76 | AudioLib::LcgRandom random; 77 | 78 | public: 79 | Modulator(); 80 | ~Modulator(); 81 | void Initialize(int samplerate); 82 | void SetParameter(ModParameters parameter, double value); 83 | float Process(int samples); 84 | void Reset(); 85 | inline void SetGate(bool gate, float velocity) { env.SetGate(gate, velocity); } 86 | inline static float GetFrequency(float value) 87 | { 88 | return (float)(ValueTables::Get(value, ValueTables::Response4Dec) * MaxFreq); 89 | } 90 | 91 | private: 92 | void Update(); 93 | float GetSample(); 94 | }; 95 | } 96 | 97 | #endif 98 | 99 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/LightKnob.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/SavePresetDialog.xaml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/WaveFile.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_WAVEFILE 2 | #define AUDIOLIB_WAVEFILE 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using std::string; 9 | using std::vector; 10 | 11 | namespace AudioLib 12 | { 13 | class FormatChunkData 14 | { 15 | public: 16 | int AudioFormat; 17 | int NumChannels; 18 | int SampleRate; 19 | int ByteRate; 20 | int BlockAlign; 21 | int BitsPerSample; 22 | int BytesPerSample; 23 | }; 24 | 25 | class WaveFile 26 | { 27 | public: 28 | /// 29 | /// Read a WAVE file. Supports multiple channels, any bitrate. 30 | /// Supported formats are IEEE 32bit floating point and uncompressed PCM 8/16/24/32 bit 31 | /// 32 | /// 33 | /// 34 | static vector> ReadWaveFile(string filename); 35 | 36 | /// 37 | /// Read a WAVE file. Supports multiple channels, any bitrate. 38 | /// Supported formats are IEEE 32bit floating point and uncompressed PCM 8/16/24/32 bit 39 | /// 40 | /// 41 | /// 42 | static vector> ReadWaveFile(const vector& data); 43 | 44 | private: 45 | 46 | /// 47 | /// Parses the data chunk, containing the audio data 48 | /// 49 | /// data array to work on 50 | /// starting index of the data chunk 51 | /// size of the data chunk 52 | /// the format description, read from the 'fmt ' chunk 53 | /// 54 | static vector> ParseDataChunk(const vector& data, int idx, int chunkSize, FormatChunkData format); 55 | 56 | /// 57 | /// Reads the fmt chunk 58 | /// 59 | /// 60 | /// 61 | /// 62 | static FormatChunkData ParseFmtChunk(const vector& data, int idx); 63 | }; 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/Biquad.h: -------------------------------------------------------------------------------- 1 | #ifndef BIQUAD 2 | #define BIQUAD 3 | 4 | #include 5 | using namespace std; 6 | 7 | namespace AudioLib 8 | { 9 | class Biquad 10 | { 11 | public: 12 | enum class FilterType 13 | { 14 | LowPass = 0, 15 | HighPass, 16 | BandPass, 17 | Notch, 18 | Peak, 19 | LowShelf, 20 | HighShelf 21 | }; 22 | 23 | private: 24 | int samplerate; 25 | float _gainDb; 26 | float _q; 27 | float a0, a1, a2, b0, b1, b2; 28 | float x1, x2, y, y1, y2; 29 | float gain; 30 | 31 | public: 32 | FilterType Type; 33 | float Output; 34 | float Frequency; 35 | float Slope; 36 | 37 | Biquad(); 38 | Biquad(FilterType filterType, int samplerate); 39 | ~Biquad(); 40 | 41 | int GetSamplerate(); 42 | void SetSamplerate(int samplerate); 43 | float GetGainDb(); 44 | void SetGainDb(float value); 45 | float GetGain(); 46 | void SetGain(float value); 47 | float GetQ(); 48 | void SetQ(float value); 49 | vector GetA(); 50 | vector GetB(); 51 | 52 | void Update(); 53 | float GetResponse(float freq) const; 54 | 55 | float inline Process(float x) 56 | { 57 | y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; 58 | x2 = x1; 59 | y2 = y1; 60 | x1 = x; 61 | y1 = y; 62 | 63 | Output = y; 64 | return Output; 65 | } 66 | 67 | void inline Process(float* input, float* output, int len) 68 | { 69 | for (int i = 0; i < len; i++) 70 | { 71 | float x = input[i]; 72 | y = ((b0 * x) + (b1 * x1) + (b2 * x2)) - (a1 * y1) - (a2 * y2); 73 | x2 = x1; 74 | y2 = y1; 75 | x1 = x; 76 | y1 = y; 77 | 78 | output[i] = y; 79 | } 80 | 81 | Output = y; 82 | } 83 | 84 | void ClearBuffers(); 85 | 86 | static std::vector GetLowpassMagnitude(float cutoff, float resonance); 87 | static std::vector GetBandpassMagnitude(float cutoff, float resonance); 88 | static std::vector GetHighpassMagnitude(float cutoff, float resonance); 89 | 90 | private: 91 | static std::vector GetSystemResponse(const Biquad& biquad); 92 | }; 93 | } 94 | 95 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/Delay.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_DELAY 2 | #define POLYHEDRUS_DELAY 3 | 4 | #include "AudioLib/OnePoleFilters.h" 5 | #include "Parameters.h" 6 | #include "AllpassDiffuser.h" 7 | #include "AudioLib/TempoSync.h" 8 | #include "ParameterFormatters.h" 9 | 10 | namespace Polyhedrus 11 | { 12 | class Delay 13 | { 14 | public: 15 | bool IsEnabled; 16 | double Bpm; 17 | 18 | private: 19 | float diffuseAmount; 20 | float delayL; 21 | float delayR; 22 | float feedbackL; 23 | float feedbackR; 24 | float highpass; 25 | float lowpass; 26 | float saturate; 27 | bool sync; 28 | float wet; 29 | 30 | AudioLib::Lp1 lpL; 31 | AudioLib::Lp1 lpR; 32 | AudioLib::Hp1 hpL; 33 | AudioLib::Hp1 hpR; 34 | 35 | AllpassDiffuser diffuserL; 36 | AllpassDiffuser diffuserR; 37 | 38 | int delayBufferSize; 39 | float* bufferL; 40 | float* bufferR; 41 | float* delayLineL; 42 | float* delayLineR; 43 | int samplerate; 44 | float T; 45 | int samplePos; 46 | 47 | int delaySamplesL; 48 | int delaySamplesR; 49 | float totalFeedbackL; 50 | float totalFeedbackR; 51 | float totalSaturate; 52 | float satInner; 53 | float satOuter; 54 | float dryGain; 55 | float wetGain; 56 | 57 | AudioLib::Quantization delayQL; 58 | AudioLib::Quantization delayQR; 59 | double delayTimeL; 60 | double delayTimeR; 61 | 62 | public: 63 | Delay(); 64 | ~Delay(); 65 | void Initialize(int samplerate, int bufferSize); 66 | void SetParameter(DelayParameters parameter, double value); 67 | void Process(float* inputL, float* inputR, int len); 68 | float* GetOutputL(); 69 | float* GetOutputR(); 70 | void ClearBuffers(); 71 | inline std::string GetDelayLString() 72 | { 73 | return sync ? AudioLib::TempoSync::ToString(delayQL) : (ParameterFormatters::FormatIntRounded(delayTimeL * 1000) + "ms"); 74 | } 75 | inline std::string GetDelayRString() 76 | { 77 | return sync ? AudioLib::TempoSync::ToString(delayQR) :( ParameterFormatters::FormatIntRounded(delayTimeR * 1000) + "ms"); 78 | } 79 | private: 80 | void Update(); 81 | int GetIndex(int offset); 82 | }; 83 | 84 | } 85 | 86 | #endif 87 | 88 | -------------------------------------------------------------------------------- /Todo List.txt: -------------------------------------------------------------------------------- 1 | # To Complete 2 | 3 | Show graphics for all modules 4 | Graphics repr. decoupled from actual module 5 | Make sure internal processing is always at 96Khz or more (remove hi quality button) 6 | Need to connect more mod destinations 7 | Chorus 8 | 9 | # Rework and features 10 | 11 | For Wavetable osc: 12 | - Add Stepping/Mixing for wave transition within table 13 | - Add a Scan speed 14 | - Scan mode: Repeat, Rewind, One Shot 15 | - Granular osc should have same option 16 | 17 | make oscillator interface support stereo. 18 | For FM, use L+R signal as modulator. 19 | Add osc envelopes for each osc 20 | Special oscs should have a second panel for advanced settings 21 | Add an additive osc type with features: 22 | - 16 partials 23 | - waveshape (for each partial - this should be done additively as well, i.e. a saw wave simple adds partials at 2x, 3x, 4x... as well. Limit the number of overtones to 16) 24 | - Bit Depth 25 | - Waveshaper (shapes final, combined wave) 26 | - Waveshaper amount 27 | Add supersaw oscillator 28 | Add 4th oscillator 29 | Add at least one more modulator 30 | Make LFO and Envelope output of modulators available as sources 31 | Add Osc Envelopes 32 | Add FM synthesis section - new tab where the mixer is. 33 | 1>1 1>2 1>3 1>4 34 | 2>2 2>3 2>4 35 | 3>3 3>4 36 | 4>4 37 | 38 | Make the AM options separate feeds into the mixer 39 | Change the AM to RM (ringmod, bipolar, not unipolar) 40 | re-think mixing between stages (character/HP/LP filter) - would be good to get output straight from each stage 41 | Generalize the oscillator block to allow more types of oscs 42 | Add granular synthesis 43 | Make the editor one-instance only 44 | improve UDP communication so that new plugin instances notify the editor of their existence 45 | 46 | # DSP Improvements 47 | 48 | Parameter smoothing (is it necesary? why is the update rate so bad?) 49 | Replace HP Filter with ZDF State variable version 50 | 51 | # Bugs for later 52 | 53 | Make sure you can't crash the plugin by sending bogus OSC parameters 54 | Warn user if loading preset with missing wavetable. 55 | 56 | # Unfinushed features 57 | 58 | Filter mode for TrueZero filter -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/VoiceIndicator.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace Polyhedrus.Ui.Components 18 | { 19 | 20 | public partial class VoiceIndicator : UserControl 21 | { 22 | static internal DependencyProperty VoiceIsActiveProperty = DependencyProperty.Register("VoiceIsActive", typeof(bool), typeof(VoiceIndicator), 23 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 24 | 25 | static internal DependencyProperty VoiceIsDecayingProperty = DependencyProperty.Register("VoiceIsDecaying", typeof(bool), typeof(VoiceIndicator), 26 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 27 | 28 | static internal DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(int), typeof(VoiceIndicator), 29 | new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 30 | 31 | public VoiceIndicator() 32 | { 33 | InitializeComponent(); 34 | 35 | DependencyPropertyDescriptor.FromProperty(StateProperty, this.GetType()) 36 | .AddValueChanged(this, (s, e) => 37 | { 38 | VoiceIsActive = State == 2; 39 | VoiceIsDecaying = State == 1; 40 | }); 41 | } 42 | 43 | public bool VoiceIsActive 44 | { 45 | get { return (bool)GetValue(VoiceIsActiveProperty); } 46 | set { SetValue(VoiceIsActiveProperty, value); } 47 | } 48 | 49 | public bool VoiceIsDecaying 50 | { 51 | get { return (bool)GetValue(VoiceIsDecayingProperty); } 52 | set { SetValue(VoiceIsDecayingProperty, value); } 53 | } 54 | 55 | public int State 56 | { 57 | get { return (int)GetValue(StateProperty); } 58 | set { SetValue(StateProperty, value); } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/FilterHpSection.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/ModKnob.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Exports.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "Synth.h" 4 | #include "Parameters.h" 5 | #include "AudioLib/Utils.h" 6 | #include "AudioLib/Noise.h" 7 | #include "AudioLib/ValueTables.h" 8 | #include "AudioLib/FastSin.h" 9 | #include "Fft/FastFFT.h" 10 | #include "PlatformSpecific.h" 11 | 12 | using namespace std; 13 | using namespace Polyhedrus; 14 | 15 | extern "C" 16 | { 17 | _declspec(dllexport) Polyhedrus::Synth* Create() 18 | { 19 | Polyhedrus::Parameters::Init(); 20 | AudioLib::ValueTables::Init(); 21 | AudioLib::Utils::Initialize(); 22 | AudioLib::Noise::Initialize(); 23 | AudioLib::FastSin::Init(); 24 | FastFFT::Setup(); 25 | 26 | return new Polyhedrus::Synth(); 27 | } 28 | 29 | _declspec(dllexport) void Initialize(Polyhedrus::Synth* instance, int samplerate, int udpListenPort, int udpSendPort) 30 | { 31 | instance->Initialize(samplerate, true, udpListenPort, udpSendPort); 32 | } 33 | 34 | _declspec(dllexport) void SetParameter(Polyhedrus::Synth* instance, int parameter, double value) 35 | { 36 | instance->SetParameter(parameter, value); 37 | } 38 | 39 | _declspec(dllexport) void ProcessMidi(Polyhedrus::Synth* instance, uint8_t* message) 40 | { 41 | instance->ProcessMidi(message); 42 | } 43 | 44 | _declspec(dllexport) void ProcessAudio(Polyhedrus::Synth* instance, double** buffer, int bufferSize) 45 | { 46 | float l[1024]; 47 | float r[1024]; 48 | for (int i = 0; i < bufferSize; i++) 49 | { 50 | l[i] = (float)buffer[0][i]; 51 | r[i] = (float)buffer[1][i]; 52 | } 53 | 54 | float* lr[2]; 55 | lr[0] = l; 56 | lr[1] = r; 57 | instance->ProcessAudio(lr, bufferSize); 58 | 59 | for (int i = 0; i < bufferSize; i++) 60 | { 61 | buffer[0][i] = l[i]; 62 | buffer[1][i] = r[i]; 63 | } 64 | } 65 | 66 | _declspec(dllexport) void Delete(Polyhedrus::Synth* instance) 67 | { 68 | delete instance; 69 | } 70 | 71 | 72 | std::shared_ptr wt; 73 | 74 | _declspec(dllexport) Polyhedrus::Wavetable* GetWavetable(Polyhedrus::Synth* instance, int tableIndex) 75 | { 76 | Polyhedrus::WavetableManager wtman; 77 | wtman.Setup(Polyhedrus::PlatformSpecific::GetDllDir()); 78 | wt = wtman.LoadWavetable(tableIndex); 79 | return &(*wt); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Envelope.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_ENVELOPE 2 | #define POLYHEDRUS_ENVELOPE 3 | 4 | #include 5 | 6 | #include "Parameters.h" 7 | #include "AudioLib/ValueTables.h" 8 | 9 | using AudioLib::ValueTables; 10 | 11 | namespace Polyhedrus 12 | { 13 | class Envelope 14 | { 15 | public: 16 | static const int CurveTableSize = 200; 17 | 18 | static const int SectionAttack = 0; 19 | static const int SectionHold = 1; 20 | static const int SectionDecay = 2; 21 | static const int SectionSustain = 3; 22 | static const int SectionRelease = 4; 23 | static const int SectionPostRelease = 5; 24 | 25 | static const float MaxTimeSeconds; // defined in body 26 | 27 | static inline float GetDecayTime(float input) 28 | { 29 | return (float)ValueTables::Get(input, ValueTables::Response3Dec) * MaxTimeSeconds; 30 | } 31 | 32 | bool Retrigger; 33 | float VelocityAmount; 34 | 35 | private: 36 | 37 | float attackCurve[CurveTableSize]; 38 | float decayCurve[CurveTableSize]; 39 | float releaseCurve[CurveTableSize]; 40 | float velocityCurve[CurveTableSize]; 41 | 42 | float velocity; 43 | bool gate; 44 | float attack; 45 | float hold; 46 | float decay; 47 | float release; 48 | 49 | int samplerate; 50 | int section; 51 | float iterator; 52 | float attackInc; 53 | float holdInc; 54 | float decayInc; 55 | float sustain; 56 | float releaseInc; 57 | float output; 58 | 59 | float attackLevel; 60 | float releaseLevel; 61 | 62 | inline float Lookup(float* table, float phase) 63 | { 64 | float idx = (float)(phase * (CurveTableSize - 0.001)); 65 | int x0 = (int)idx; 66 | int x1 = x0 + 1; 67 | if (x1 >= CurveTableSize) x1 = CurveTableSize - 1; 68 | float mix = idx - x0; 69 | 70 | float value = table[x0] * (1 - mix) + table[x1] * mix; 71 | return value; 72 | } 73 | 74 | public: 75 | Envelope(); 76 | ~Envelope(); 77 | void Initialize(int samplerate); 78 | void SetParameter(EnvParameters parameter, double value); 79 | float Process(int samples); 80 | inline float GetOutput() { return output * (1 - VelocityAmount + velocity * VelocityAmount); } 81 | void SetGate(bool gate, float velocity); 82 | void Silence(); 83 | void Reset(); 84 | std::vector GetVisual(); 85 | std::vector GetVelocityVisual(); 86 | void CreateCurve(float* table, double expo); 87 | }; 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/ModSourceDest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Polyhedrus.Ui 8 | { 9 | enum ModSource 10 | { 11 | Off = 0, 12 | EnvAmp = 1, 13 | EnvFilter = 2, 14 | 15 | Mod1 = 10, 16 | Mod2 = 11, 17 | Mod3 = 12, 18 | Mod1Unipolar = 13, 19 | Mod2Unipolar = 14, 20 | Mod3Unipolar = 15, 21 | Mod1Env = 16, 22 | Mod2Env = 17, 23 | Mod3Env = 18, 24 | 25 | SlopGen1 = 20, 26 | SlopGen2 = 21, 27 | SlopGen3 = 22, 28 | 29 | KeyTrack = 30, 30 | KeyTrackUnipolar = 31, 31 | Pitchbend = 32, 32 | 33 | Velocity = 40, 34 | Gate = 41, 35 | 36 | ModWheel = 50, 37 | KeyPressure = 51, 38 | ChannelPressure = 52, 39 | 40 | VoiceIndex = 60, 41 | UnisonIndex = 61, 42 | 43 | Macro1 = 70, 44 | Macro2 = 71, 45 | Macro3 = 72, 46 | Macro4 = 73, 47 | Macro5 = 74, 48 | Macro6 = 75, 49 | Macro7 = 76, 50 | Macro8 = 77, 51 | 52 | Count = 80 53 | } 54 | 55 | enum ModDest 56 | { 57 | Off = 0, 58 | 59 | OscAllPitch = 10, 60 | OscAllShape = 11, 61 | OscAllPan = 12, 62 | OscAllVolume = 13, 63 | 64 | Osc1Pitch = 20, 65 | Osc1Shape = 21, 66 | Osc1Pan = 22, 67 | Osc1Volume = 23, 68 | 69 | Osc2Pitch = 30, 70 | Osc2Shape = 31, 71 | Osc2Pan = 32, 72 | Osc2Volume = 33, 73 | 74 | Osc3Pitch = 40, 75 | Osc3Shape = 41, 76 | Osc3Pan = 42, 77 | Osc3Volume = 43, 78 | 79 | MixerAm12 = 60, 80 | MixerAm23 = 61, 81 | MixerFm12 = 62, 82 | MixerFm13 = 63, 83 | MixerFm23 = 64, 84 | MixerNoise = 65, 85 | MixerOutput = 66, 86 | 87 | //CharacterBottom = 70, 88 | //CharacterTop = 71, 89 | //CharacterDecimate = 72, 90 | //CharacterReduce = 73, 91 | //CharacterClip = 74, 92 | 93 | FilterHpCutoff = 80, 94 | 95 | FilterMainCutoff = 90, 96 | FilterMainDrive = 91, 97 | FilterMainResonance = 92, 98 | 99 | //EnvAmp = 100 100 | //EnvFilter = 110 101 | 102 | /*Lfo1Phase = 120, 103 | Lfo1Freq = 121, 104 | Lfo1Attack = 122, 105 | Lfo1Decay = 123, 106 | Lfo1Sustain = 124, 107 | 108 | Lfo2Phase = 130, 109 | Lfo2Freq = 131, 110 | Lfo2Attack = 132, 111 | Lfo2Decay = 133, 112 | Lfo2Sustain = 134, 113 | 114 | ArpGate = 140, 115 | ArpTempo = 141,*/ 116 | 117 | Count = 150 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/MenuSelector.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using LowProfile.Core.Ui; 16 | 17 | namespace Polyhedrus.Ui.Components 18 | { 19 | /// 20 | /// Interaction logic for MenuSelector.xaml 21 | /// 22 | public partial class MenuSelector : ListBox 23 | { 24 | public MenuSelector() 25 | { 26 | InitializeComponent(); 27 | SelectOptionCommand = new DelegateCommand(x => SelectedItem = x); 28 | } 29 | 30 | public DelegateCommand SelectOptionCommand { get; private set; } 31 | 32 | private void ContextMenu_Opened(object sender, RoutedEventArgs e) 33 | { 34 | contextMenu.Items.Clear(); 35 | var me = new MenuItem(); 36 | 37 | var items = new List>(); 38 | foreach (var item in Items) 39 | items.Add((KeyValuePair)item); 40 | 41 | AddMenuItems(contextMenu, items.Select(x => Tuple.Create(x, x.Value.Split('/'))).ToArray()); 42 | 43 | } 44 | 45 | /// 46 | /// Splits slash-separated strings into a submenu tree 47 | /// 48 | /// 49 | /// 50 | private void AddMenuItems(ItemsControl menu, Tuple, string[]>[] items) 51 | { 52 | foreach (var group in items.Where(x => x.Item2.Length > 1).GroupBy(x => x.Item2.First())) 53 | { 54 | var menuItem = new MenuItem { Command = null, CommandParameter = null, Header = group.Key }; 55 | menu.Items.Add(menuItem); 56 | var subItems = group.Select(x => Tuple.Create(x.Item1, x.Item2.Skip(1).ToArray())).ToArray(); 57 | AddMenuItems(menuItem, subItems); 58 | } 59 | 60 | foreach (var item in items.Where(x => x.Item2.Length == 1)) 61 | { 62 | menu.Items.Add(new MenuItem { Command = SelectOptionCommand, CommandParameter = item.Item1, Header = item.Item2.Last() }); 63 | } 64 | } 65 | 66 | private void Main_MouseUp(object sender, MouseButtonEventArgs e) 67 | { 68 | ContextMenu.IsOpen = true; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Polyhedrus.Ui")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Polyhedrus.Ui")] 15 | [assembly: AssemblyCopyright("Copyright © 2015")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /Polyhedrus.Native/ModSourceDest.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_MOD_SOURCE_DEST 2 | #define POLYHEDRUS_MOD_SOURCE_DEST 3 | 4 | enum class ModSource 5 | { 6 | Off = 0, 7 | EnvAmp = 1, 8 | EnvFilter = 2, 9 | 10 | Mod1 = 10, 11 | Mod2 = 11, 12 | Mod3 = 12, 13 | Mod1Unipolar = 13, 14 | Mod2Unipolar = 14, 15 | Mod3Unipolar = 15, 16 | Mod1Env = 16, 17 | Mod2Env = 17, 18 | Mod3Env = 18, 19 | 20 | SlopGen1 = 20, 21 | SlopGen2 = 21, 22 | SlopGen3 = 22, 23 | 24 | KeyTrack = 30, 25 | KeyTrackUnipolar = 31, 26 | Pitchbend = 32, 27 | 28 | Velocity = 40, 29 | Gate = 41, 30 | 31 | ModWheel = 50, 32 | KeyPressure = 51, 33 | ChannelPressure = 52, 34 | 35 | VoiceIndex = 60, 36 | UnisonIndex = 61, 37 | 38 | Macro1 = 70, 39 | Macro2 = 71, 40 | Macro3 = 72, 41 | Macro4 = 73, 42 | Macro5 = 74, 43 | Macro6 = 75, 44 | Macro7 = 76, 45 | Macro8 = 77, 46 | 47 | Count = 80 48 | }; 49 | 50 | enum class ModDest 51 | { 52 | Off = 0, 53 | 54 | OscAllPitch = 10, 55 | OscAllShape = 11, 56 | OscAllPan = 12, 57 | OscAllVolume = 13, 58 | 59 | Osc1Pitch = 20, 60 | Osc1Shape = 21, 61 | Osc1Pan = 22, 62 | Osc1Volume = 23, 63 | 64 | Osc2Pitch = 30, 65 | Osc2Shape = 31, 66 | Osc2Pan = 32, 67 | Osc2Volume = 33, 68 | 69 | Osc3Pitch = 40, 70 | Osc3Shape = 41, 71 | Osc3Pan = 42, 72 | Osc3Volume = 43, 73 | 74 | MixerAm12 = 60, 75 | MixerAm23 = 61, 76 | MixerFm12 = 62, 77 | MixerFm13 = 63, 78 | MixerFm23 = 64, 79 | MixerNoise = 65, 80 | MixerCharacterOut = 66, 81 | MixerFilterHpOut = 67, 82 | MixerFilterMainOut = 68, 83 | 84 | //CharacterBottom = 70, 85 | //CharacterTop = 71, 86 | //CharacterDecimate = 72, 87 | //CharacterReduce = 73, 88 | //CharacterClip = 74, 89 | 90 | FilterHpCutoff = 80, 91 | 92 | FilterMainCutoff = 90, 93 | FilterMainDrive = 91, 94 | FilterMainResonance = 92, 95 | 96 | EnvAmpAttack = 100, 97 | EnvAmpHold = 101, 98 | EnvAmpDecay = 102, 99 | EnvAmpSustain = 103, 100 | EnvAmpRelease = 104, 101 | 102 | EnvFilterAttack = 110, 103 | EnvFilterHold = 111, 104 | EnvFilterDecay = 112, 105 | EnvFilterSustain = 113, 106 | EnvFilterRelease = 114, 107 | 108 | /*Lfo1Phase = 120, 109 | Lfo1Freq = 121, 110 | Lfo1Attack = 122, 111 | Lfo1Decay = 123, 112 | Lfo1Sustain = 124, 113 | 114 | Lfo2Phase = 130, 115 | Lfo2Freq = 131, 116 | Lfo2Attack = 132, 117 | Lfo2Decay = 133, 118 | Lfo2Sustain = 134, 119 | 120 | ArpGate = 140, 121 | ArpTempo = 141,*/ 122 | 123 | Count = 150 124 | 125 | }; 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /Wavetable Editor/Wav.js: -------------------------------------------------------------------------------- 1 | window.Wav = (function() { 2 | 3 | var Wav = function(settings) { 4 | 5 | this.sampleRate = 48000; 6 | this.channels = 1; 7 | this.buffer = null; 8 | }; 9 | 10 | Wav.prototype.setBuffer = function(buffer) { 11 | this.buffer = this.getWavInt16Array(buffer); 12 | }; 13 | 14 | Wav.prototype.getWavInt16Array = function(buffer) { 15 | 16 | var intBuffer = new Int16Array(buffer.length + 23), tmp; 17 | 18 | intBuffer[0] = 0x4952; // "RI" 19 | intBuffer[1] = 0x4646; // "FF" 20 | 21 | intBuffer[2] = (2 * buffer.length + 15) & 0x0000ffff; // RIFF size 22 | intBuffer[3] = ((2 * buffer.length + 15) & 0xffff0000) >> 16; // RIFF size 23 | 24 | intBuffer[4] = 0x4157; // "WA" 25 | intBuffer[5] = 0x4556; // "VE" 26 | 27 | intBuffer[6] = 0x6d66; // "fm" 28 | intBuffer[7] = 0x2074; // "t " 29 | 30 | intBuffer[8] = 0x0012; // fmt chunksize: 18 31 | intBuffer[9] = 0x0000; // 32 | 33 | intBuffer[10] = 0x0001; // format tag : 1 34 | intBuffer[11] = this.channels; // channels: 1 35 | 36 | intBuffer[12] = this.sampleRate & 0x0000ffff; // sample per sec 37 | intBuffer[13] = (this.sampleRate & 0xffff0000) >> 16; // sample per sec 38 | 39 | intBuffer[14] = (2 * this.channels * this.sampleRate) & 0x0000ffff; // byte per sec 40 | intBuffer[15] = ((2 * this.channels * this.sampleRate) & 0xffff0000) >> 16; // byte per sec 41 | 42 | intBuffer[16] = 0x0004; // block align 43 | intBuffer[17] = 0x0010; // bit per sample 44 | intBuffer[18] = 0x0000; // cb size 45 | intBuffer[19] = 0x6164; // "da" 46 | intBuffer[20] = 0x6174; // "ta" 47 | intBuffer[21] = (2 * buffer.length) & 0x0000ffff; // data size[byte] 48 | intBuffer[22] = ((2 * buffer.length) & 0xffff0000) >> 16; // data size[byte] 49 | 50 | for (var i = 0; i < buffer.length; i++) { 51 | tmp = buffer[i]; 52 | if (tmp >= 1) { 53 | intBuffer[i + 23] = (1 << 15) - 1; 54 | } else if (tmp <= -1) { 55 | intBuffer[i + 23] = -(1 << 15); 56 | } else { 57 | intBuffer[i + 23] = Math.round(tmp * (1 << 15)); 58 | } 59 | } 60 | 61 | return intBuffer; 62 | }; 63 | 64 | Wav.prototype.getBlob = function() { 65 | 66 | var srclist = []; 67 | srclist.push(this.buffer); 68 | 69 | var b = new Blob(srclist, { type: 'application/octet-stream' }); 70 | return b; 71 | }; 72 | 73 | return Wav; 74 | 75 | })(); -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/DriveSection.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/CharacterSection.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/SvfZeroFilter.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_SVF_ZERO_FILTER 2 | #define AUDIOLIB_SVF_ZERO_FILTER 3 | 4 | #include "MathDefs.h" 5 | #include "Utils.h" 6 | #include 7 | 8 | namespace AudioLib 9 | { 10 | // Sources: 11 | // https://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_1.1.1.pdf 12 | class SvfZeroFilter 13 | { 14 | public: 15 | float Fc; 16 | float Resonance; 17 | float Fs; 18 | 19 | float Lp; 20 | float Bp; 21 | float Hp; 22 | 23 | private: 24 | float g; 25 | float s1, s2; 26 | float R; 27 | float divisor; 28 | 29 | public: 30 | 31 | inline SvfZeroFilter() 32 | { 33 | Fc = 0.5f; 34 | Resonance = 0.5f; 35 | Fs = 48000.0f; 36 | 37 | g = 0.5f; 38 | R = 1.0f; 39 | s1 = 0.0f; 40 | s2 = 0.0f; 41 | divisor = 1.0f; 42 | 43 | Lp = 0.0f; 44 | Bp = 0.0f; 45 | Hp = 0.0f; 46 | } 47 | 48 | inline void Update() 49 | { 50 | float fcRel = Fc / Fs; 51 | g = (float)(fcRel * M_PI); 52 | 53 | R = 1 - Resonance; 54 | divisor = 1.0f / (1 + 2 * R * g + g * g); 55 | } 56 | 57 | inline void SetFc(float fcRel) 58 | { 59 | Fc = fcRel * 2; // OK, I've no idea why I need that x2 factor, but without it, the resonance freq. is just an octave too low!! 60 | g = (float)(fcRel * M_PI); 61 | } 62 | 63 | inline void Process(float x) 64 | { 65 | //Hp = (x - 2 * R * s1 - g * s1 - s2) / (1 + 2 * R * g + g * g); 66 | Hp = (x - 2 * R * s1 - g * s1 - s2) * divisor; 67 | 68 | // Integrator 1 69 | 70 | // store temp 71 | float z = s1; 72 | // compute input to z 73 | s1 = Bp + g * Hp; 74 | // Compute new output 75 | Bp = z + g * Hp; 76 | 77 | // Integrator 2 78 | 79 | // store temp 80 | z = s2; 81 | // compute input to z 82 | s2 = Lp + g * Bp; 83 | // Compute new output 84 | Lp = z + g * Bp; 85 | } 86 | 87 | inline void ProcessHp(float* input, float* output, int len) 88 | { 89 | register float hp = Hp; 90 | register float lp = Lp; 91 | register float bp = Bp; 92 | register float ss1 = s1; 93 | register float ss2 = s2; 94 | register float r = R; 95 | register float gg = g; 96 | register float divi = divisor; 97 | register float z; 98 | 99 | for (int i = 0; i < len; i++) 100 | { 101 | hp = (input[i] - 2 * r * ss1 - gg * ss1 - ss2) * divi; 102 | 103 | // Integrator 1 104 | z = ss1; 105 | ss1 = bp + gg * hp; 106 | bp = z + gg * hp; 107 | 108 | // Integrator 2 109 | z = ss2; 110 | ss2 = lp + gg * bp; 111 | lp = z + gg * bp; 112 | 113 | output[i] = hp; 114 | } 115 | 116 | Hp = hp; 117 | Lp = lp; 118 | Bp = bp; 119 | s1 = ss1; 120 | s2 = ss2; 121 | } 122 | }; 123 | } 124 | 125 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/MacroControls.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Polyhedrus.Native/SynthParameters.cpp: -------------------------------------------------------------------------------- 1 | #include "Synth.h" 2 | #include "ParameterFormatters.h" 3 | 4 | namespace Polyhedrus 5 | { 6 | std::string Synth::FormatParameter(Module module, int parameter, double value) 7 | { 8 | auto info = Parameters::ParamInfo[module][parameter]; 9 | return info.Formatter(value); 10 | } 11 | 12 | std::string Synth::GetParameterText(Module module, int parameter, double value) 13 | { 14 | if (module == Module::Osc1) 15 | { 16 | if (parameter == (int)OscParameters::Shape) 17 | return Voices[0].osc1->GetShapeString(); 18 | if (parameter == (int)OscParameters::BaseHz) 19 | return Voices[0].osc1->GetBaseHzString(); 20 | if (parameter == (int)OscParameters::Linear) 21 | return Voices[0].osc1->GetLinearString(); 22 | if (parameter == (int)OscParameters::GlideFactor) 23 | return Voices[0].osc1->GetGlideString(); 24 | } 25 | else if (module == Module::Osc2) 26 | { 27 | if (parameter == (int)OscParameters::Shape) 28 | return Voices[0].osc2->GetShapeString(); 29 | if (parameter == (int)OscParameters::BaseHz) 30 | return Voices[0].osc2->GetBaseHzString(); 31 | if (parameter == (int)OscParameters::Linear) 32 | return Voices[0].osc2->GetLinearString(); 33 | if (parameter == (int)OscParameters::GlideFactor) 34 | return Voices[0].osc2->GetGlideString(); 35 | } 36 | else if (module == Module::Osc3) 37 | { 38 | if (parameter == (int)OscParameters::Shape) 39 | return Voices[0].osc3->GetShapeString(); 40 | if (parameter == (int)OscParameters::BaseHz) 41 | return Voices[0].osc3->GetBaseHzString(); 42 | if (parameter == (int)OscParameters::Linear) 43 | return Voices[0].osc3->GetLinearString(); 44 | if (parameter == (int)OscParameters::GlideFactor) 45 | return Voices[0].osc3->GetGlideString(); 46 | } 47 | else if (module == Module::Delay) 48 | { 49 | if (parameter == (int)DelayParameters::DelayL) 50 | return this->Delay.GetDelayLString(); 51 | if (parameter == (int)DelayParameters::DelayR) 52 | return this->Delay.GetDelayRString(); 53 | } 54 | else if (module == Module::FilterMain) 55 | { 56 | if (parameter == (int)FilterMainParameters::Mode) 57 | return this->Voices[0].mainFilterL->GetModeString(); 58 | } 59 | 60 | return FormatParameter(module, parameter, value); 61 | } 62 | 63 | void Synth::SendBackParameter(Module module, int parameter) 64 | { 65 | auto idx = PackParameter(module, parameter); 66 | double value = currentPreset.Values[idx]; 67 | std::string text = GetParameterText(module, parameter, value); 68 | 69 | formattedParameters[idx] = text; 70 | OscMessage oscMsg("/Control/ParameterData"); 71 | oscMsg.SetInt((int)module); 72 | oscMsg.SetInt(parameter); 73 | oscMsg.SetFloat((float)value); 74 | oscMsg.SetString(text); 75 | auto bytes = oscMsg.GetBytes(); 76 | udpTranceiver->Send(bytes); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/FlatToggleButton.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Controls.Primitives; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Navigation; 16 | using System.Windows.Shapes; 17 | 18 | namespace Polyhedrus.Ui.Components 19 | { 20 | /// 21 | /// Interaction logic for FlatToggleButton.xaml 22 | /// 23 | public partial class FlatToggleButton : ToggleButton 24 | { 25 | static new internal DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(FlatToggleButton), 26 | new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Black), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 27 | 28 | static new internal DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(FlatToggleButton), 29 | new FrameworkPropertyMetadata(new SolidColorBrush(Colors.White), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 30 | 31 | static internal DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FlatToggleButton), 32 | new FrameworkPropertyMetadata("Off", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 33 | 34 | private string textOn; 35 | private string textOff; 36 | 37 | public FlatToggleButton() 38 | { 39 | InitializeComponent(); 40 | 41 | DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(IsCheckedProperty, this.GetType()); 42 | prop.AddValueChanged(this, (x, y) => SetValue(TextProperty, IsChecked.GetValueOrDefault() ? TextOn ?? "On" : TextOff ?? "Off")); 43 | } 44 | 45 | public new Brush Background 46 | { 47 | get { return (Brush)base.GetValue(BorderBrushProperty); } 48 | set { SetValue(BorderBrushProperty, value); } 49 | } 50 | 51 | public new Brush BorderBrush 52 | { 53 | get { return (Brush)base.GetValue(BorderBrushProperty); } 54 | set { SetValue(BorderBrushProperty, value); } 55 | } 56 | 57 | public string Text 58 | { 59 | get { return (string)base.GetValue(TextProperty); } 60 | } 61 | 62 | public string TextOn 63 | { 64 | get 65 | { 66 | return textOn; 67 | } 68 | set 69 | { 70 | textOn = value; 71 | if (IsChecked.GetValueOrDefault()) 72 | SetValue(TextProperty, value); 73 | } 74 | } 75 | 76 | public string TextOff 77 | { 78 | get 79 | { 80 | return textOff; 81 | } 82 | set 83 | { 84 | textOff = value; 85 | if (!IsChecked.GetValueOrDefault()) 86 | SetValue(TextProperty, value); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Polyhedrus.Native/Fft/Complex.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef COMPLEX 3 | #define COMPLEX 4 | 5 | #pragma pack(push, 4) 6 | template 7 | class Complex 8 | { 9 | public: 10 | TVal Real; 11 | TVal Imag; 12 | 13 | Complex(); 14 | Complex(const TVal real, const TVal imag); 15 | 16 | static Complex CExp(const TVal phase); 17 | static void Multiply(Complex& dest, const Complex& c1, const Complex& c2); 18 | static void Add(Complex& dest, const Complex& c1, const Complex& c2); 19 | static void Subtract(Complex& dest, const Complex& c1, const Complex& c2); 20 | 21 | static void Multiply(Complex& dest, const Complex& c1); 22 | static void Add(Complex& dest, const Complex& c1); 23 | static void Subtract(Complex& dest, const Complex& c1); 24 | 25 | static const Complex I; 26 | }; 27 | #pragma pack(pop) 28 | 29 | // ------------ Implementation ------------ 30 | 31 | #include 32 | 33 | template 34 | const Complex Complex::I = Complex(0, 1); 35 | 36 | template 37 | inline Complex::Complex() 38 | { 39 | Real = 0; 40 | Imag = 0; 41 | } 42 | 43 | template 44 | inline Complex::Complex(const TVal real, const TVal imag) 45 | { 46 | Real = real; 47 | Imag = imag; 48 | } 49 | 50 | template 51 | inline Complex Complex::CExp(const TVal phase) 52 | { 53 | TVal x = cos(phase); 54 | TVal y = sin(phase); 55 | return Complex(x, y); 56 | } 57 | 58 | 59 | // ------------- fast operations ------------- 60 | 61 | template 62 | inline void Complex::Multiply(Complex& dest, const Complex& c1, const Complex& c2) 63 | { 64 | TVal r = c1.Real * c2.Real - c1.Imag * c2.Imag; 65 | TVal i = c1.Real * c2.Imag + c1.Imag * c2.Real; 66 | dest.Real = r; 67 | dest.Imag = i; 68 | } 69 | 70 | template 71 | inline void Complex::Add(Complex& dest, const Complex& c1, const Complex& c2) 72 | { 73 | dest.Real = c1.Real + c2.Real; 74 | dest.Imag = c1.Imag + c2.Imag; 75 | } 76 | 77 | template 78 | inline void Complex::Subtract(Complex& dest, const Complex& c1, const Complex& c2) 79 | { 80 | dest.Real = c1.Real - c2.Real; 81 | dest.Imag = c1.Imag - c2.Imag; 82 | } 83 | 84 | 85 | template 86 | inline void Complex::Multiply(Complex& dest, const Complex& c1) 87 | { 88 | TVal r = dest.Real * c1.Real - dest.Imag * c1.Imag; 89 | dest.Imag = dest.Real * c1.Imag + dest.Imag * c1.Real; 90 | dest.Real = r; 91 | } 92 | 93 | template 94 | inline void Complex::Add(Complex& dest, const Complex& c1) 95 | { 96 | dest.Real += c1.Real; 97 | dest.Imag += c1.Imag; 98 | } 99 | 100 | template 101 | inline void Complex::Subtract(Complex& dest, const Complex& c1) 102 | { 103 | dest.Real -= c1.Real; 104 | dest.Imag -= c1.Imag; 105 | } 106 | 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /Polyhedrus.Plugin/Polyhedrus.Plugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {99C70666-72ED-4004-8119-7BCA1D18FC52} 8 | Library 9 | Properties 10 | Polyhedrus.Plugin 11 | Polyhedrus.Plugin 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | true 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | true 33 | 34 | 35 | 36 | ..\Binaries\SharpSoundDevice\x86\Release\SharpSoundDevice.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 60 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/ChorusSection.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Wavetable Editor/Index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 37 |
Wavetable Count
Table Size
Table Size 29 | 35 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
C1C2C3C4C5C6Off
RecomputeDownload
56 |
57 | 58 |
59 | 79 |
80 |
81 | 82 |
83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Polyhedrus.Ui.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Polyhedrus.Ui.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/ValueTables.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "ValueTables.h" 4 | 5 | namespace AudioLib 6 | { 7 | double ValueTables::Sqrt[TableSize]; 8 | double ValueTables::Sqrt3[TableSize]; 9 | double ValueTables::Pow1_5[TableSize]; 10 | double ValueTables::Pow2[TableSize]; 11 | double ValueTables::Pow3[TableSize]; 12 | double ValueTables::Pow4[TableSize]; 13 | double ValueTables::x2Pow3[TableSize]; 14 | 15 | // octave response. value double every step (2,3,4,5 or 6 steps) 16 | double ValueTables::Response2Oct[TableSize]; 17 | double ValueTables::Response3Oct[TableSize]; 18 | double ValueTables::Response4Oct[TableSize]; 19 | double ValueTables::Response5Oct[TableSize]; 20 | double ValueTables::Response6Oct[TableSize]; 21 | 22 | // decade response, value multiplies by 10 every step 23 | double ValueTables::Response2Dec[TableSize]; 24 | double ValueTables::Response3Dec[TableSize]; 25 | double ValueTables::Response4Dec[TableSize]; 26 | 27 | void ValueTables::Init() 28 | { 29 | // Initialize tables 30 | 31 | for (int i = 0; i <= 4000; i++) 32 | { 33 | double x = i / 4000.0; 34 | 35 | Sqrt[i] = std::sqrt(x); 36 | Sqrt3[i] = std::pow(x, 1.0 / 3.0); 37 | Pow1_5[i] = std::pow(x, 1.5); 38 | Pow2[i] = std::pow(x, 2.0); 39 | Pow3[i] = std::pow(x, 3.0); 40 | Pow4[i] = std::pow(x, 4.0); 41 | 42 | x2Pow3[i] = std::pow(2 * x, 3.0); 43 | Response2Oct[i] = (std::pow(4, x) - 1.0) / 4.0 + 0.25; 44 | Response3Oct[i] = (std::pow(8, x) - 1.0) / 8.0 + 0.125; 45 | Response4Oct[i] = (std::pow(16, x) - 1.0) / 16.0 + 0.125 / 2.0; 46 | Response5Oct[i] = (std::pow(32, x) - 1.0) / 32.0 + 0.125 / 4.0; 47 | Response6Oct[i] = (std::pow(64, x) - 1.0) / 64.0 + 0.125 / 8.0; 48 | 49 | Response2Dec[i] = std::pow(100, x) / 100.0; 50 | Response3Dec[i] = std::pow(1000, x) / 1000.0; 51 | Response4Dec[i] = std::pow(10000, x) / 10000.0; 52 | } 53 | 54 | for (int i = 1; i <= 4000; i++) 55 | { 56 | Response2Oct[i] = (Response2Oct[i] - Response2Oct[0]) / (1 - Response2Oct[0]); 57 | Response3Oct[i] = (Response3Oct[i] - Response3Oct[0]) / (1 - Response3Oct[0]); 58 | Response4Oct[i] = (Response4Oct[i] - Response4Oct[0]) / (1 - Response4Oct[0]); 59 | Response5Oct[i] = (Response5Oct[i] - Response5Oct[0]) / (1 - Response5Oct[0]); 60 | Response6Oct[i] = (Response6Oct[i] - Response6Oct[0]) / (1 - Response6Oct[0]); 61 | Response2Dec[i] = (Response2Dec[i] - Response2Dec[0]) / (1 - Response2Dec[0]); 62 | Response3Dec[i] = (Response3Dec[i] - Response3Dec[0]) / (1 - Response3Dec[0]); 63 | Response4Dec[i] = (Response4Dec[i] - Response4Dec[0]) / (1 - Response4Dec[0]); 64 | } 65 | 66 | Response2Oct[0] = 0; 67 | Response3Oct[0] = 0; 68 | Response4Oct[0] = 0; 69 | Response5Oct[0] = 0; 70 | Response6Oct[0] = 0; 71 | Response2Dec[0] = 0; 72 | Response3Dec[0] = 0; 73 | Response4Dec[0] = 0; 74 | } 75 | 76 | double ValueTables::Get(double index, double* table) 77 | { 78 | if (table == nullptr) 79 | return index; 80 | 81 | int idx = (int)(index * 4000.999); 82 | return table[idx]; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/ControlManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using Polyhedrus.Ui.Components; 10 | using Polyhedrus.Ui.Messaging; 11 | using LowProfile.Core.Extensions; 12 | using SharpOSC; 13 | 14 | namespace Polyhedrus.Ui 15 | { 16 | class ControlManager 17 | { 18 | private readonly SynthViewModel vm; 19 | 20 | private readonly OscTranceiver tranceiver; 21 | private readonly Dictionary sendMessages; 22 | 23 | public ControlManager(SynthViewModel vm) 24 | { 25 | this.vm = vm; 26 | tranceiver = new OscTranceiver(12003, 12004); 27 | sendMessages = new Dictionary(); 28 | 29 | var oscThread = new Thread(() => ProcessOscMessages()) { IsBackground = true }; 30 | oscThread.Start(); 31 | } 32 | 33 | public void SendOscMessage(string address, double value) 34 | { 35 | Console.WriteLine("Sending {0} - {1}", address, value); 36 | var oscMsg = new OscMessage(address, (float)value); 37 | 38 | lock (sendMessages) 39 | { 40 | sendMessages[oscMsg.Address] = oscMsg; 41 | } 42 | } 43 | 44 | public void SendOscControlMessage(string address, params object[] values) 45 | { 46 | Console.WriteLine("Sending {0}", address); 47 | var oscMsg = new OscMessage(address, values); 48 | 49 | lock (sendMessages) 50 | { 51 | sendMessages[oscMsg.Address] = oscMsg; 52 | } 53 | } 54 | 55 | /// 56 | /// Update loop for OSC messaging 57 | /// 58 | private void ProcessOscMessages() 59 | { 60 | while (true) 61 | { 62 | try 63 | { 64 | while (true) 65 | { 66 | var msg = tranceiver.Receive(); 67 | if (msg == null) 68 | break; 69 | 70 | ProcessOscMessage(msg); 71 | } 72 | 73 | lock (sendMessages) 74 | { 75 | var keys = sendMessages.Keys.ToArray(); 76 | foreach (var key in keys) 77 | { 78 | try 79 | { 80 | var oscMsg = sendMessages[key]; 81 | sendMessages.Remove(key); 82 | var bytes = oscMsg.GetBytes(); 83 | tranceiver.Send(bytes); 84 | } 85 | catch (Exception ex) 86 | { 87 | Console.WriteLine(ex.GetTrace()); 88 | } 89 | } 90 | } 91 | } 92 | catch (Exception ex) 93 | { 94 | Console.WriteLine(ex.GetTrace()); 95 | } 96 | 97 | Thread.Sleep(2); 98 | } 99 | } 100 | 101 | private void ProcessOscMessage(byte[] bytes) 102 | { 103 | try 104 | { 105 | var msg = OscPacket.GetPacket(bytes) as OscMessage; 106 | Console.WriteLine(msg.ToString()); 107 | if (msg.Address.StartsWith("/Control/")) 108 | vm.ProcessControlMessage(msg); 109 | } 110 | catch (Exception ex) 111 | { 112 | Console.WriteLine(ex.GetTrace()); 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Polyhedrus.Ui/Components/ArpeggiatorSection.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Polyhedrus.Native/AudioLib/SvfFilter.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIOLIB_SVFFILTER 2 | #define AUDIOLIB_SVFFILTER 3 | 4 | #include "MathDefs.h" 5 | #include "Utils.h" 6 | #include 7 | 8 | namespace AudioLib 9 | { 10 | // Sources: 11 | // https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Digital_Sound/Digital_Sound_Generation_2.pdf 12 | // http://musicdsp.org/showone.php?id=142 13 | // It is HIGHLY recommended to oversample this filter by at least 2x 14 | class SvfFilter 15 | { 16 | public: 17 | float Fc; 18 | float Resonance; 19 | float Fs; 20 | bool Nonlinear; 21 | 22 | float Lp; 23 | float Bp; 24 | float Hp; 25 | float No; 26 | 27 | inline SvfFilter() 28 | { 29 | Nonlinear = false; 30 | Fc = 0.5f; 31 | Resonance = 0.5f; 32 | f = 0.2f; 33 | d = 1.0f; 34 | 35 | Lp = 0.0f; 36 | Bp = 0.0f; 37 | Hp = 0.0f; 38 | No = 0.0f; 39 | zState1 = 0.0f; 40 | zState2 = 0.0f; 41 | } 42 | 43 | inline void Update() 44 | { 45 | // usually the parameter Q is used, and then Q = [0....inf[ 46 | // and then D = 1 / Q. for Q >= 0.5, this becomes a range [0...2] 47 | //Resonance = Utils::Limit(Resonance, 0, 1.0f); 48 | d = (1 - Resonance) * 2; 49 | 50 | f = (float)(2 * std::sin(M_PI * Fc / Fs)); 51 | //f = f * (1.85 - 0.85 * d * f); 52 | 53 | // adjustment factors from paper 54 | d = std::fmin(d, 2 - f); 55 | } 56 | 57 | inline void ProcessLinear(float x) 58 | { 59 | Lp = zState2 + f * zState1; 60 | Hp = (x - Lp) - (d * zState1); 61 | Bp = f * Hp + zState1; 62 | No = Hp + Lp; 63 | 64 | zState1 = Bp; 65 | zState2 = Lp; 66 | } 67 | 68 | inline void ProcessNonlinear(float x) 69 | { 70 | Lp = zState2 + f * zState1; 71 | Hp = (x - Lp) - (d * zState1); 72 | Bp = f * Hp + zState1; 73 | No = Hp + Lp; 74 | 75 | zState1 = AudioLib::Utils::CubicNonlin(Bp); 76 | zState2 = Lp; 77 | } 78 | 79 | inline void ProcessLinearHp2x(float* input, float* output, int len) 80 | { 81 | // local vars give a slight boost 82 | register float lp; 83 | register float hp; 84 | register float zzState1 = zState1; 85 | register float zzState2 = zState2; 86 | register float ff = f; 87 | register float dd = d; 88 | 89 | for (int i = 0; i < len; i++) 90 | { 91 | float x = input[i]; 92 | 93 | // iter 1 94 | lp = zzState2 + ff * zzState1; 95 | hp = (x - lp) - (dd * zzState1); 96 | zzState1 = ff * hp + zzState1; 97 | 98 | //zzState1 = bp; 99 | zzState2 = lp; 100 | 101 | // iter 2 102 | lp = zzState2 + ff * zzState1; 103 | hp = (x - lp) - (dd * zzState1); 104 | zzState1 = ff * hp + zzState1; 105 | 106 | //zzState1 = bp; 107 | zzState2 = lp; 108 | 109 | output[i] = hp; 110 | } 111 | 112 | zState1 = zzState1; 113 | zState2 = zzState2; 114 | Lp = lp; 115 | Hp = hp; 116 | Bp = zzState1; // = bp; 117 | No = Hp + Lp; 118 | } 119 | 120 | private: 121 | float f; 122 | float d; 123 | 124 | float zState1; 125 | float zState2; 126 | }; 127 | } 128 | 129 | #endif -------------------------------------------------------------------------------- /Polyhedrus.Native/Drive.cpp: -------------------------------------------------------------------------------- 1 | #include "Drive.h" 2 | #include "AudioLib/Utils.h" 3 | #include "AudioLib/ValueTables.h" 4 | 5 | using namespace AudioLib; 6 | 7 | namespace Polyhedrus 8 | { 9 | 10 | Drive::Drive() 11 | { 12 | InitCurves(); 13 | 14 | Gain = 0; 15 | Bias = 0; 16 | Post = true; 17 | Type = DriveType::None; 18 | Mellow = 0; 19 | buffer = 0; 20 | samplerate = 48000; 21 | IsEnabled = true; 22 | } 23 | 24 | Drive::~Drive() 25 | { 26 | delete buffer; 27 | } 28 | 29 | void Drive::Initialize(int samplerate, int bufferSize) 30 | { 31 | buffer = new float[bufferSize](); 32 | this->samplerate = samplerate; 33 | lp.SetFc(1); 34 | hp.SetFc(5.0f / 48000.0f); 35 | } 36 | 37 | void Drive::SetParameter(DriveParameters parameter, double value) 38 | { 39 | switch (parameter) 40 | { 41 | case DriveParameters::Gain: 42 | Gain = (float)value; 43 | break; 44 | case DriveParameters::Bias: 45 | Bias = (float)value; 46 | break; 47 | case DriveParameters::Volume: 48 | Volume = (float)value; 49 | break; 50 | case DriveParameters::Post: 51 | Post = value >= 0.5; 52 | break; 53 | case DriveParameters::Type: 54 | Type = (DriveType)Parameters::FloorToInt(value); 55 | if (value < 0 || value >= ((int)DriveType::Asym + 1)) 56 | Type = DriveType::None; 57 | break; 58 | case DriveParameters::Mellow: 59 | Mellow = (float)value; 60 | break; 61 | } 62 | } 63 | 64 | void Drive::Process(float* input, int len) 65 | { 66 | if (!IsEnabled) 67 | { 68 | Utils::Copy(input, buffer, len); 69 | return; 70 | } 71 | 72 | auto func = &None; 73 | 74 | if (Type == DriveType::Asym) 75 | func = &Asym; 76 | else if (Type == DriveType::Clip) 77 | func = &Clip; 78 | else if (Type == DriveType::Tanh) 79 | func = &Tanh; 80 | 81 | Update(); 82 | 83 | for (int i = 0; i < len; i++) 84 | { 85 | buffer[i] = input[i] * gainTotal + biasTotal; 86 | } 87 | 88 | for (int i = 0; i < len; i++) 89 | { 90 | buffer[i] = func(buffer[i]); 91 | } 92 | 93 | for (int i = 0; i < len; i++) 94 | { 95 | buffer[i] = lp.Process(buffer[i]); 96 | } 97 | 98 | for (int i = 0; i < len; i++) 99 | { 100 | buffer[i] = hp.Process(buffer[i]); 101 | } 102 | 103 | Utils::Gain(buffer, Volume, len); 104 | } 105 | 106 | void Drive::Update() 107 | { 108 | if (Type == DriveType::None) 109 | { 110 | gainTotal = 1.0; 111 | biasTotal = 0.0; 112 | } 113 | else 114 | { 115 | gainTotal = 1.0f + Utils::Limit(Gain + GainMod, 0.0, 1.0) * 20.0f; 116 | biasTotal = (Bias + BiasMod) * gainTotal; 117 | } 118 | 119 | float fc = Utils::Limit(1 - (Mellow + MellowMod), 0.0, 1.0); 120 | fc = 0.002f + (float)ValueTables::Get(fc, ValueTables::Response2Dec); 121 | lp.SetFc(fc); 122 | } 123 | 124 | float Drive::AsymCurve[300000]; 125 | bool Drive::curvesInitialized = false; 126 | 127 | void Drive::InitCurves() 128 | { 129 | if (curvesInitialized) 130 | return; 131 | 132 | for (size_t i = 0; i < 300000; i++) 133 | { 134 | float x = (float)(-1 + i / 100000.0); 135 | AsymCurve[i] = (float)(2 * std::tanh(0.2f * std::powf(2 * x + 2, 1.5)) - 1); 136 | } 137 | 138 | curvesInitialized = true; 139 | } 140 | } 141 | 142 | -------------------------------------------------------------------------------- /Polyhedrus.Native/UnitTests/OscTests.cpp: -------------------------------------------------------------------------------- 1 | #include "../Osc/OscMessage.h" 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | inline void Reverse(uint8_t* data) 7 | { 8 | auto a = data[0]; 9 | auto b = data[1]; 10 | auto c = data[2]; 11 | auto d = data[3]; 12 | 13 | data[0] = d; 14 | data[1] = c; 15 | data[2] = b; 16 | data[3] = a; 17 | } 18 | 19 | using namespace Polyhedrus; 20 | 21 | namespace Tests 22 | { 23 | namespace Osc 24 | { 25 | void TestOscParser1() 26 | { 27 | char message[32]; 28 | memcpy(message, "/Path\0\0\0,i\0\0\0\0\0A", 16); 29 | vector vec(&message[0], &message[16]); 30 | 31 | OscMessage osc(vec); 32 | assert(osc.Address == "/Path"); 33 | char tag = osc.TypeTags[0]; 34 | assert(strncmp(&tag, "i", 1) == 0); 35 | assert(osc.TypeTags.size() == 1); 36 | 37 | int val = osc.GetInt(0); 38 | assert(val == 65); 39 | } 40 | 41 | void TestOscParserFloatInt() 42 | { 43 | uint8_t message[32]; 44 | memcpy(message, "/Path\0\0\0,fi\0", 12); 45 | float* fp = (float*)&(message[12]); 46 | int* ip = (int*)&(message[16]); 47 | *fp = 23.45f; 48 | *ip = 56; 49 | Reverse(&message[12]); 50 | Reverse(&message[16]); 51 | vector vec(&message[0], &message[20]); 52 | 53 | OscMessage osc(vec); 54 | assert(osc.Address == "/Path"); 55 | assert(osc.TypeTags[0] == 'f'); 56 | assert(osc.TypeTags[1] == 'i'); 57 | assert(osc.TypeTags.size() == 2); 58 | 59 | assert(osc.GetFloat(0) == 23.45f); 60 | assert(osc.GetInt(1) == 56); 61 | } 62 | 63 | void TestOscParserStringBlobInt() 64 | { 65 | uint8_t message[40]; 66 | memcpy(message, "/Path\0\0\0,sbi\0\0\0\0", 16); 67 | char* sp = (char*)&(message[16]); 68 | uint8_t* bp = (uint8_t*)&(message[24]); 69 | int* ip = (int*)&(message[32]); 70 | 71 | sp[0] = 'a'; 72 | sp[1] = 'b'; 73 | sp[2] = 'c'; 74 | sp[3] = 'd'; 75 | sp[4] = 'e'; 76 | sp[5] = 0; 77 | sp[6] = 0; 78 | sp[7] = 0; 79 | 80 | bp[0] = 0; 81 | bp[1] = 0; 82 | bp[2] = 0; 83 | bp[3] = 3; 84 | bp[4] = 24; 85 | bp[5] = 45; 86 | bp[6] = 99; 87 | bp[7] = 0; 88 | 89 | *ip = 42; 90 | Reverse(&message[32]); 91 | 92 | vector vec(&message[0], &message[36]); 93 | 94 | OscMessage osc(vec); 95 | assert(osc.Address == "/Path"); 96 | assert(osc.TypeTags[0] == 's'); 97 | assert(osc.TypeTags[1] == 'b'); 98 | assert(osc.TypeTags[2] == 'i'); 99 | assert(osc.TypeTags.size() == 3); 100 | 101 | assert(osc.GetString(0) == "abcde"); 102 | assert(osc.GetInt(2) == 42); 103 | 104 | auto data = osc.GetBlob(1); 105 | assert(data.size() == 3); 106 | assert(data[0] == 24); 107 | assert(data[1] == 45); 108 | assert(data[2] == 99); 109 | } 110 | 111 | void TestBundleParse() 112 | { 113 | char message[40]; 114 | memcpy(message, "#bundle\0\0\0\0\0\0\0\0\0\0\0\0\x10/Path\0\0\0,i\0\0\0\0\0A", 36); 115 | vector vec(&message[0], &message[36]); 116 | 117 | auto messages = OscMessage::ParseRawBytes(vec); 118 | auto osc = messages[0]; 119 | assert(osc.Address == "/Path"); 120 | char tag = osc.TypeTags[0]; 121 | assert(strncmp(&tag, "i", 1) == 0); 122 | assert(osc.TypeTags.size() == 1); 123 | 124 | int val = osc.GetInt(0); 125 | assert(val == 65); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Polyhedrus.Native/MixerSettings.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_MIXER_SETTINGS 2 | #define POLYHEDRUS_MIXER_SETTINGS 3 | 4 | #include "AudioLib/Utils.h" 5 | 6 | namespace Polyhedrus 7 | { 8 | enum class RoutingStage 9 | { 10 | Character = 0, 11 | HpFilter = 1, 12 | MainFilter = 2, 13 | Drive = 3, 14 | Direct = 4, 15 | }; 16 | 17 | class MixerSettings 18 | { 19 | public: 20 | RoutingStage NoiseRouting; 21 | RoutingStage Osc1Routing; 22 | RoutingStage Osc2Routing; 23 | RoutingStage Osc3Routing; 24 | 25 | // ------------ Input Fields --------------- 26 | 27 | float Osc1Volume; 28 | float Osc2Volume; 29 | float Osc3Volume; 30 | 31 | float Osc1VolumeMod; 32 | float Osc2VolumeMod; 33 | float Osc3VolumeMod; 34 | 35 | float Osc1Pan; 36 | float Osc2Pan; 37 | float Osc3Pan; 38 | 39 | float Osc1PanMod; 40 | float Osc2PanMod; 41 | float Osc3PanMod; 42 | 43 | float Am12; 44 | float Am23; 45 | float Fm12; 46 | float Fm13; 47 | float Fm23; 48 | float Noise; 49 | float CharacterOut; 50 | float FilterHpOut; 51 | float FilterMainOut; 52 | 53 | float Am12Mod; 54 | float Am23Mod; 55 | float Fm12Mod; 56 | float Fm13Mod; 57 | float Fm23Mod; 58 | float NoiseMod; 59 | float CharacterOutMod; 60 | float FilterHpOutMod; 61 | float FilterMainOutMod; 62 | 63 | // --------- Computed Fields ------------ 64 | 65 | float Osc1VolL; 66 | float Osc1VolR; 67 | float Osc2VolL; 68 | float Osc2VolR; 69 | float Osc3VolL; 70 | float Osc3VolR; 71 | 72 | float Am12Total; 73 | float Am23Total; 74 | float Fm12Total; 75 | float Fm13Total; 76 | float Fm23Total; 77 | float NoiseTotal; 78 | float CharacterOutTotal; 79 | float FilterHpOutTotal; 80 | float FilterMainOutTotal; 81 | 82 | inline void ComputeOscVols() 83 | { 84 | float osc1Vol = AudioLib::Utils::LimitMin(Osc1Volume + Osc1VolumeMod, 0.0); 85 | float osc2Vol = AudioLib::Utils::LimitMin(Osc2Volume + Osc2VolumeMod, 0.0); 86 | float osc3Vol = AudioLib::Utils::LimitMin(Osc3Volume + Osc3VolumeMod, 0.0); 87 | float osc1Pan = Osc1Pan + Osc1PanMod; 88 | float osc2Pan = Osc2Pan + Osc2PanMod; 89 | float osc3Pan = Osc3Pan + Osc3PanMod; 90 | 91 | Osc1VolL = osc1Vol * AudioLib::Utils::Limit(-osc1Pan + 1, 0.0, 1.0); 92 | Osc1VolR = osc1Vol * AudioLib::Utils::Limit(osc1Pan + 1, 0.0, 1.0); 93 | 94 | Osc2VolL = osc2Vol * AudioLib::Utils::Limit(-osc2Pan + 1, 0.0, 1.0); 95 | Osc2VolR = osc2Vol * AudioLib::Utils::Limit(osc2Pan + 1, 0.0, 1.0); 96 | 97 | Osc3VolL = osc3Vol * AudioLib::Utils::Limit(-osc3Pan + 1, 0.0, 1.0); 98 | Osc3VolR = osc3Vol * AudioLib::Utils::Limit(osc3Pan + 1, 0.0, 1.0); 99 | 100 | Am12Total = AudioLib::Utils::Limit(Am12 + Am12Mod, 0.0, 1.0); 101 | Am23Total = AudioLib::Utils::Limit(Am23 + Am23Mod, 0.0, 1.0); 102 | Fm12Total = AudioLib::Utils::Limit(Fm12 + Fm12Mod, 0.0, 1.0); 103 | Fm13Total = AudioLib::Utils::Limit(Fm13 + Fm13Mod, 0.0, 1.0); 104 | Fm23Total = AudioLib::Utils::Limit(Fm23 + Fm23Mod, 0.0, 1.0); 105 | NoiseTotal = AudioLib::Utils::Limit(Noise + NoiseMod, 0.0, 1.0); 106 | CharacterOutTotal = AudioLib::Utils::Limit(CharacterOut + CharacterOutMod, 0.0, 1.0); 107 | FilterHpOutTotal = AudioLib::Utils::Limit(FilterHpOut + FilterHpOutMod, 0.0, 1.0); 108 | FilterMainOutTotal = AudioLib::Utils::Limit(FilterMainOut + FilterMainOutMod, 0.0, 1.0); 109 | } 110 | }; 111 | } 112 | 113 | #endif 114 | 115 | -------------------------------------------------------------------------------- /Polyhedrus.Native/WavetableManager.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYHEDRUS_WAVETABLE_MANAGER 2 | #define POLYHEDRUS_WAVETABLE_MANAGER 3 | 4 | #include "Default.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Polyhedrus 10 | { 11 | class Wavetable; 12 | 13 | class WavetableFile 14 | { 15 | public: 16 | std::string FilePath; 17 | std::string Selector; 18 | int Index; 19 | }; 20 | 21 | const static int NumWavetablePartials = 20; 22 | 23 | class WavetableManager 24 | { 25 | public: 26 | std::vector WavetableFiles; 27 | 28 | private: 29 | // How many partials to include in each wave 30 | int WavetablePartials[NumWavetablePartials] = { 512,389,291,218,163,122,91,68,51,38,28,21,16,12,9,6,5,3,2,1 }; 31 | 32 | // How large the wave for each wave is 33 | int WavetableSize[NumWavetablePartials] = { 2048,2048,1024,1024,1024,512,512,512,256,256,128,128,128,128,128,128,128,128,128,128 }; 34 | 35 | // The offset from zero from the base partial table to the top (integrates the WavetableSize array) 36 | int WavetableOffset[NumWavetablePartials]; 37 | 38 | int TotalSize; 39 | 40 | // Maps each midi note to the correct partial wave (0...NumPartials) 41 | int WavetableIndex[128] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,19,19,19 }; 42 | 43 | private: 44 | std::vector> loadedWavetables; 45 | std::shared_ptr ConvertTable(float* wavetable, int tableSize, int numTables); 46 | void Normalize(std::shared_ptr wavetable); 47 | std::vector ScanWavetableFiles(std::string pluginDirectory); 48 | 49 | public: 50 | 51 | //static std::vector Wavetables; 52 | void Setup(std::string waveformDirectory); 53 | std::shared_ptr LoadWavetable(int wtNum); 54 | int GetId(std::string selector); 55 | }; 56 | 57 | class Wavetable 58 | { 59 | public: 60 | int Count; 61 | int WavetableDataSize; 62 | 63 | // How many partials to include in each wave 64 | int WavetablePartials[NumWavetablePartials]; 65 | // How large the wave for each wave is 66 | int WavetableSize[NumWavetablePartials]; 67 | // The offset from zero from the base partial table to the top (integrates the WavetableSize array) 68 | int WavetableOffset[NumWavetablePartials]; 69 | int TotalSize; 70 | // Maps each midi note to the correct partial wave (0...NumPartials) 71 | int WavetableIndex[128]; 72 | 73 | private: 74 | float* wavetableData; 75 | 76 | public: 77 | inline Wavetable(int dataSize) 78 | { 79 | this->wavetableData = new float[dataSize](); 80 | } 81 | 82 | inline ~Wavetable() 83 | { 84 | delete wavetableData; 85 | } 86 | 87 | inline float* GetTable(int tableIndex, int partialIndex) 88 | { 89 | if (partialIndex < 0) 90 | partialIndex = 0; 91 | else if (partialIndex >= NumWavetablePartials) 92 | partialIndex = NumWavetablePartials - 1; 93 | 94 | if (tableIndex < 0) 95 | tableIndex = 0; 96 | else if (tableIndex >= Count) 97 | tableIndex = Count - 1; 98 | 99 | int idx = tableIndex * TotalSize + WavetableOffset[partialIndex]; 100 | return &wavetableData[idx]; 101 | } 102 | }; 103 | } 104 | 105 | #endif --------------------------------------------------------------------------------