@@ -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; | |||||