| @@ -21,6 +21,7 @@ plugins: libs | |||||
| $(MAKE) all -C plugins/Meters | $(MAKE) all -C plugins/Meters | ||||
| $(MAKE) all -C plugins/Parameters | $(MAKE) all -C plugins/Parameters | ||||
| $(MAKE) all -C plugins/States | $(MAKE) all -C plugins/States | ||||
| $(MAKE) all -C plugins/Super_Saw | |||||
| gen: plugins dpf/utils/lv2_ttl_generator | gen: plugins dpf/utils/lv2_ttl_generator | ||||
| @$(CURDIR)/dpf/utils/generate-ttl.sh | @$(CURDIR)/dpf/utils/generate-ttl.sh | ||||
| @@ -0,0 +1,11 @@ | |||||
| #ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
| #define DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
| #define DISTRHO_PLUGIN_NAME "Super Saw" | |||||
| #define DISTRHO_PLUGIN_URI "https://github.com/cranixx/Super_Saw_DPF" | |||||
| #define DISTRHO_PLUGIN_NUM_INPUTS 1 | |||||
| #define DISTRHO_PLUGIN_NUM_OUTPUTS 1 | |||||
| #define DISTRHO_PLUGIN_IS_SYNTH 1 | |||||
| #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED | |||||
| @@ -0,0 +1,61 @@ | |||||
| #!/usr/bin/make -f | |||||
| # Makefile for DISTRHO Plugins # | |||||
| # ---------------------------- # | |||||
| # Created by falkTX | |||||
| # | |||||
| # -------------------------------------------------------------- | |||||
| # Project name, used for binaries | |||||
| NAME = Super_Saw | |||||
| # -------------------------------------------------------------- | |||||
| # Files to build | |||||
| OBJS_DSP = \ | |||||
| SuperSaw.cpp.o | |||||
| #OBJS_UI = \ | |||||
| # InfoExampleUI.cpp.o | |||||
| # -------------------------------------------------------------- | |||||
| # Do some magic | |||||
| include ../Makefile.mk | |||||
| # -------------------------------------------------------------- | |||||
| # Enable all possible plugin types | |||||
| ifeq ($(HAVE_DGL),true) | |||||
| ifeq ($(HAVE_JACK),true) | |||||
| TARGETS += jack | |||||
| endif | |||||
| endif | |||||
| ifeq ($(HAVE_DGL),true) | |||||
| TARGETS += lv2_sep | |||||
| else | |||||
| TARGETS += lv2_dsp | |||||
| endif | |||||
| TARGETS += vst | |||||
| ADA: | |||||
| gnat make -fPIC blep | |||||
| gnat bind -n blep | |||||
| gnat make -fPIC super_saw | |||||
| gnat bind -n super_saw | |||||
| gnat make -fPIC b~super_saw | |||||
| gnat bind -n b~super_saw | |||||
| gnat make -fPIC polyphony | |||||
| gnat bind -n polyphony | |||||
| gnat make -fPIC b~polyphony | |||||
| gnat bind -n b~polyphony | |||||
| #BASE_FLAGS += super_saw.o b~super_saw.o -lgnat -lgnarl -lgmem | |||||
| BASE_FLAGS += super_saw.o polyphony.o b~polyphony.o blep.o -lgnat -lgnarl -lgmem -ggdb | |||||
| BASE_FLAGS += -L/usr/lib/gcc/x86_64-redhat-linux/7/adalib | |||||
| all: ADA $(TARGETS) | |||||
| # -------------------------------------------------------------- | |||||
| @@ -0,0 +1,168 @@ | |||||
| #include "DistrhoPlugin.hpp" | |||||
| #include <math.h> | |||||
| extern "C" void adainit(void); | |||||
| extern "C" void adafinal(void); | |||||
| extern "C" void Add_Note(float); | |||||
| extern "C" void Remove_Note(float); | |||||
| extern "C" float Super_Saw(float,float,float,float,float); | |||||
| extern "C" float Compute_Polyphony(float,float,float,float); | |||||
| START_NAMESPACE_DISTRHO | |||||
| class SuperSaw : public Plugin | |||||
| { | |||||
| public: | |||||
| SuperSaw() : Plugin(2,0,0){ | |||||
| adainit(); | |||||
| } | |||||
| ~SuperSaw() { | |||||
| adafinal(); | |||||
| } | |||||
| protected: | |||||
| const char* getLabel() const override | |||||
| { | |||||
| return "Super Saw"; | |||||
| } | |||||
| const char* getDescription() const override | |||||
| { | |||||
| return "Roland JP-8000 Super Saw emulator"; | |||||
| } | |||||
| const char* getMaker() const override | |||||
| { | |||||
| return "Cranix"; | |||||
| } | |||||
| /** | |||||
| Get the plugin homepage. | |||||
| */ | |||||
| const char* getHomePage() const override | |||||
| { | |||||
| return "http://example.org/Super_Saw"; | |||||
| } | |||||
| /** | |||||
| Get the plugin license name (a single line of text). | |||||
| For commercial plugins this should return some short copyright information. | |||||
| */ | |||||
| const char* getLicense() const override | |||||
| { | |||||
| return "GPL"; | |||||
| } | |||||
| /** | |||||
| Get the plugin version, in hexadecimal. | |||||
| */ | |||||
| uint32_t getVersion() const override | |||||
| { | |||||
| return d_version(1, 0, 0); | |||||
| } | |||||
| /** | |||||
| Get the plugin unique Id. | |||||
| This value is used by LADSPA, DSSI and VST plugin formats. | |||||
| */ | |||||
| int64_t getUniqueId() const override | |||||
| { | |||||
| return d_cconst('d', 'N', 'f', 'o'); | |||||
| } | |||||
| void setParameterValue(uint32_t index, float value) override | |||||
| { | |||||
| if (index == 0) { | |||||
| detune = value; | |||||
| } else if (index == 1) { | |||||
| mix = value; | |||||
| } | |||||
| } | |||||
| float getParameterValue(uint32_t index) const override | |||||
| { | |||||
| if (index == 0) { | |||||
| return detune; | |||||
| } else if (index == 1) { | |||||
| return mix; | |||||
| } | |||||
| } | |||||
| void initParameter(uint32_t index, Parameter& parameter) override { | |||||
| if (index == 0) { /*Detune*/ | |||||
| parameter.hints = kParameterIsAutomable; | |||||
| parameter.name = "Detune"; | |||||
| parameter.symbol = "detune"; | |||||
| parameter.ranges.min = 0.0f; | |||||
| parameter.ranges.max = 0.9f; | |||||
| parameter.ranges.def = 0.5f; | |||||
| } else if (index == 1) { /*Mix*/ | |||||
| parameter.hints = kParameterIsAutomable; | |||||
| parameter.name = "Mix"; | |||||
| parameter.symbol = "mix"; | |||||
| parameter.ranges.min = 0.0f; | |||||
| parameter.ranges.max = 0.9f; | |||||
| parameter.ranges.def = 0.5f; | |||||
| } | |||||
| } | |||||
| void run(const float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, | |||||
| uint32_t midiEventCount) override { | |||||
| const uint8_t* data; | |||||
| uint8_t status; | |||||
| uint8_t note; | |||||
| float frequency; | |||||
| uint32_t framesDone=0; | |||||
| uint32_t curEventIndex=0; | |||||
| while (framesDone < frames) { | |||||
| while (curEventIndex < midiEventCount && framesDone == midiEvents[curEventIndex].frame) { | |||||
| if ( midiEvents[curEventIndex].size > MidiEvent::kDataSize ) | |||||
| continue; | |||||
| data=midiEvents[curEventIndex].data; | |||||
| status=data[0]&0xFF; | |||||
| if ( ! ( ( status == 0x80 || status == 0x90))) { | |||||
| curEventIndex++; | |||||
| continue; | |||||
| } | |||||
| note=data[1]; | |||||
| if (status == 0x90) { | |||||
| frequency=pow(2.0,(note-57.0)/12.0)*440.0; | |||||
| Add_Note(frequency); | |||||
| } else if (status == 0x80) { | |||||
| // frequency = 0.0; | |||||
| frequency=pow(2.0,(note-57.0)/12.0)*440.0; | |||||
| Remove_Note(frequency); | |||||
| } | |||||
| curEventIndex++; | |||||
| } | |||||
| //outputs[0][framesDone]=sin(phase*frequency/44100.0*2.0*3.14); | |||||
| //outputs[0][framesDone]=sin(phase*frequency/44100.0*2.0*3.14); | |||||
| outputs[0][framesDone]=Compute_Polyphony(phase,detune,mix,getSampleRate()); | |||||
| phase++; | |||||
| framesDone++; | |||||
| } | |||||
| /*data=midiEvents[0].data; | |||||
| status=data[0]&0xFF; | |||||
| if (status == 0x90){ | |||||
| note=data[1]; | |||||
| frequency=pow(2.0,(note-57.0)/12.0)*440.0; | |||||
| for (i=0;i<frames;i++){ | |||||
| outputs[0][i] = sin(phase*2.0*3.14*frequency); | |||||
| phase++; | |||||
| } | |||||
| }*/ | |||||
| } //run | |||||
| private: | |||||
| float phase=0; | |||||
| float detune; | |||||
| float mix; | |||||
| }; | |||||
| Plugin* createPlugin() | |||||
| { | |||||
| return new SuperSaw(); | |||||
| } | |||||
| END_NAMESPACE_DISTRHO | |||||
| @@ -0,0 +1,36 @@ | |||||
| package body Blep is | |||||
| function BLEP_Saw(Phase : Float; Pitch : Float) return Float is | |||||
| T : Float := modulo(Phase,1.0); | |||||
| begin | |||||
| return Naive_Saw(Phase, Pitch)+T-(T*T/2.0)-0.5; | |||||
| end BLEP_Saw; | |||||
| function Naive_Saw(Phase : Float; Frequency: Float) return Float is | |||||
| begin | |||||
| return Modulo((Phase*Frequency),1.0); | |||||
| end Naive_Saw; | |||||
| function modulo (Dividend : Float; Divisor : Float) return Float is | |||||
| Fraction : Float; | |||||
| Int_Part : Integer; | |||||
| begin | |||||
| Int_Part := Integer(Dividend); | |||||
| Fraction := Dividend - Float(Int_Part); | |||||
| return Float((Int_Part mod Integer(Divisor))) + Fraction; | |||||
| end modulo; | |||||
| function Sinc (Phase : Float) return Float is | |||||
| begin | |||||
| if Phase = 0.0 then | |||||
| return 1.0; | |||||
| else | |||||
| return Sin(Phase)/Phase; | |||||
| end if; | |||||
| end Sinc; | |||||
| function Hamming (N : Float; Size : Float) return Float is | |||||
| begin | |||||
| return 0.54 - 0.46*Cos(2.0*Pi*N/(Size - 1.0)); | |||||
| end Hamming; | |||||
| end Blep; | |||||
| @@ -0,0 +1,14 @@ | |||||
| with Ada.Numerics; | |||||
| use Ada.Numerics; | |||||
| with Ada.Numerics.Elementary_Functions; | |||||
| use Ada.Numerics.Elementary_Functions; | |||||
| package Blep is | |||||
| function BLEP_Saw(Phase : Float; Pitch : Float) return Float; | |||||
| private | |||||
| function Naive_Saw(Phase : Float; Frequency : Float) return Float; | |||||
| function modulo (Dividend : Float; Divisor : Float) return Float; | |||||
| function Sinc (Phase : Float) return Float; | |||||
| function Hamming (N : Float; Size : Float) return Float; | |||||
| end Blep; | |||||
| @@ -0,0 +1,52 @@ | |||||
| with Interfaces.C; | |||||
| use Interfaces.C; | |||||
| with Ada.Numerics.Generic_Elementary_Functions; | |||||
| with Super_Saw; | |||||
| package body Polyphony is | |||||
| procedure Add_Note (Pitch : C_Float) is | |||||
| begin | |||||
| if Note_Count <= Voices then | |||||
| for I in Notes'Range loop | |||||
| if Notes(I) = 0.0 then | |||||
| Notes(I) := Float(Pitch); | |||||
| Note_Count := Note_Count + 1; | |||||
| exit; | |||||
| end if; | |||||
| end loop; | |||||
| end if; | |||||
| end Add_Note; | |||||
| procedure Remove_Note (Pitch : C_Float) is | |||||
| begin | |||||
| if Note_Count > 0 then | |||||
| for I in Notes'Range loop | |||||
| if Notes(I) = Float(Pitch) then | |||||
| Notes(I) := 0.0; | |||||
| Note_Count := Note_Count - 1; | |||||
| exit; | |||||
| end if; | |||||
| end loop; | |||||
| end if; | |||||
| end Remove_Note; | |||||
| function Compute_Polyphony (Time : C_Float; | |||||
| Detune : C_Float; Mix : C_Float; | |||||
| Sample_Rate : C_Float) return C_Float is | |||||
| package Float_Functions is new Ada.Numerics.Generic_Elementary_Functions (Float); | |||||
| Sample : C_Float := 0.0; | |||||
| begin | |||||
| for I in Notes'Range loop | |||||
| if Notes(I) /= 0.0 then | |||||
| -- Compensate for changes in volume by dividing output by logarithm of frequency | |||||
| Sample := Sample + Super_Saw.Super_Saw(Time => Time, Pitch => C_Float(Notes(I)), | |||||
| Detune => Detune, Mix => Mix, | |||||
| Sample_Rate => Sample_Rate)/C_Float(Float_Functions.Log(Notes(I)*30.0,10.0)); | |||||
| end if; | |||||
| end loop; | |||||
| return Sample; | |||||
| end Compute_Polyphony; | |||||
| end Polyphony; | |||||
| @@ -0,0 +1,20 @@ | |||||
| with Interfaces.C; | |||||
| use Interfaces.C; | |||||
| package Polyphony is | |||||
| type Note_Array_Type is array (1..4) of Float; | |||||
| Notes : Note_Array_Type := (others => 0.0); | |||||
| Note_Count : Natural := 0; | |||||
| Voices : constant := 4; | |||||
| procedure Add_Note (Pitch : C_Float) | |||||
| with Post => Note_Count <= Voices and Note_Count >= 0; | |||||
| procedure Remove_Note (Pitch : C_Float) | |||||
| with Post => Note_Count <= Voices and Note_Count >= 0; | |||||
| function Compute_Polyphony (Time : C_Float; Detune : C_Float; Mix : C_Float; | |||||
| Sample_Rate : C_Float) return C_Float; | |||||
| pragma Export(CPP,Add_Note,"Add_Note"); | |||||
| pragma Export(CPP,Remove_Note,"Remove_Note"); | |||||
| pragma Export(CPP,Compute_Polyphony,"Compute_Polyphony"); | |||||
| end Polyphony; | |||||
| @@ -0,0 +1,58 @@ | |||||
| with Interfaces.C; | |||||
| use Interfaces.C; | |||||
| with Ada.Numerics; | |||||
| use Ada.Numerics; | |||||
| with Ada.Numerics.Elementary_Functions; | |||||
| use Ada.Numerics.Elementary_Functions; | |||||
| with Blep; | |||||
| package body Super_Saw is | |||||
| function Super_Saw(Time : Interfaces.C.C_Float; Pitch : Interfaces.C.C_Float; | |||||
| Detune : Interfaces.C.C_Float; Mix : Interfaces.C.C_Float; | |||||
| Sample_Rate : Interfaces.C.C_Float) | |||||
| return Interfaces.C.C_Float is | |||||
| Offsets : Offset_Array_Type := (0.01952356,0.06288439,0.11002313); | |||||
| Sample : Float := 0.0; | |||||
| Mix_Level : Mix_Level_Type := Compute_Mix(Float(Mix)); | |||||
| begin | |||||
| -- Main oscillator | |||||
| Sample := Sample + Saw(Float(Time),Float(Pitch), Float(Sample_Rate))*Mix_Level.Master; | |||||
| -- 3 oscillators of higher pitch than main | |||||
| Higher_Oscillators:for D in 1 .. 3 loop | |||||
| Sample := Sample + Saw(Float(Time),Float(Pitch)*(1.0+Offsets(D)*Compute_Detune(Float(Detune))), | |||||
| Float(Sample_Rate))*Mix_Level.Slave; | |||||
| end loop Higher_Oscillators; | |||||
| -- 3 oscillators of lower pitch than main | |||||
| Lower_Oscillators:for D in 1 .. 3 loop | |||||
| Sample := Sample + Saw(Float(Time),Float(Pitch)*(1.0+Offsets(D)*Compute_Detune(Float(Detune))), | |||||
| Float(Sample_Rate))*Mix_Level.Slave; | |||||
| end loop Lower_Oscillators; | |||||
| return Interfaces.C.C_FLoat(Sample)*Interfaces.C.C_Float(0.1); | |||||
| end Super_Saw; | |||||
| function Saw(Time : Float; Pitch : Float; Sample_Rate : Float) return Float is | |||||
| begin | |||||
| return Blep.BLEP_Saw(Time,Pitch/Sample_Rate); | |||||
| end Saw; | |||||
| function Compute_Detune(Amount : Float) return Float is | |||||
| begin | |||||
| return (10028.7312891634*Amount**11)-(50818.8652045924*Amount**10) | |||||
| +(111363.4808729368*Amount**9)-(138150.6761080548*Amount**8)+ | |||||
| (106649.6679158292*Amount**7)-(53046.9642751875*Amount**6)+ | |||||
| (17019.9518580080*Amount**5)-(3425.0836591318*Amount**4)+ | |||||
| (404.2703938388*Amount**3)-(24.1878824391*Amount**2)+ | |||||
| (0.6717417634*Amount)+0.0030115596; | |||||
| end Compute_Detune; | |||||
| function Compute_Mix(Level : Float) return Mix_Level_Type is | |||||
| Mix_Level : Mix_Level_Type; | |||||
| begin | |||||
| Mix_Level.Master := -0.55366*Level + 0.99785; | |||||
| Mix_Level.Slave := -0.73764*Level**2 + 1.2841*Level + 0.044372; | |||||
| return Mix_Level; | |||||
| end Compute_Mix; | |||||
| end Super_Saw; | |||||
| @@ -0,0 +1,19 @@ | |||||
| with Interfaces.C; | |||||
| use Interfaces.C; | |||||
| package Super_Saw is | |||||
| type Mix_Level_Type is record | |||||
| Master : Float; | |||||
| Slave : Float; | |||||
| end record; | |||||
| type Offset_Array_Type is array (1..3) of Float; | |||||
| function Saw(Time : Float; Pitch : Float; Sample_Rate : Float) return Float; | |||||
| function Compute_Detune(Amount : Float) return Float; | |||||
| function Compute_Mix(Level : Float) return Mix_Level_Type; | |||||
| function Super_Saw(Time : Interfaces.C.C_Float; | |||||
| Pitch : Interfaces.C.C_Float;Detune : Interfaces.C.C_Float; | |||||
| Mix : Interfaces.C.C_Float | |||||
| ;Sample_Rate : Interfaces.C.C_Float) | |||||
| return Interfaces.C.C_Float; | |||||
| pragma Export(CPP,Super_Saw,"Super_Saw"); | |||||
| end Super_Saw; | |||||