::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 |
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
--------------------------------------------------------------------------------