@@ -22,6 +22,7 @@ plugins: dgl | |||
$(MAKE) all -C plugins/MidiThrough | |||
$(MAKE) all -C plugins/Parameters | |||
$(MAKE) all -C plugins/States | |||
$(MAKE) all -C plugins/Super_Saw | |||
ifneq ($(CROSS_COMPILING),true) | |||
gen: plugins dpf/utils/lv2_ttl_generator | |||
@@ -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; | |||