f01bb6ff68
This code includes the changes needed for compatibility with older patch/bank files, which assume a maximum count of 8. New patches/banks will be saved with an extra XML attribute (voiceCount="32"); if this is not present, then a value of "1.0" for the voice count will be interpreted as 8.
381 lines
7.6 KiB
C++
Executable file
381 lines
7.6 KiB
C++
Executable file
/*
|
||
==============================================================================
|
||
This file is part of Obxd synthesizer.
|
||
|
||
Copyright <20> 2013-2014 Filatov Vadim
|
||
|
||
Contact author via email :
|
||
justdat_@_e1.ru
|
||
|
||
This file may be licensed under the terms of of the
|
||
GNU General Public License Version 2 (the ``GPL'').
|
||
|
||
Software distributed under the License is distributed
|
||
on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
|
||
express or implied. See the GPL for the specific language
|
||
governing rights and limitations.
|
||
|
||
You should have received a copy of the GPL along with this
|
||
program. If not, go to http://www.gnu.org/licenses/gpl.html
|
||
or write to the Free Software Foundation, Inc.,
|
||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
==============================================================================
|
||
*/
|
||
#pragma once
|
||
#include <climits>
|
||
#include "VoiceQueue.h"
|
||
#include "SynthEngine.h"
|
||
#include "Lfo.h"
|
||
|
||
class Motherboard
|
||
{
|
||
private:
|
||
VoiceQueue vq;
|
||
int totalvc;
|
||
bool wasUni;
|
||
bool awaitingkeys[129];
|
||
int priorities[129];
|
||
|
||
Decimator17 left,right;
|
||
int asPlayedCounter;
|
||
float lkl,lkr;
|
||
float sampleRate,sampleRateInv;
|
||
//JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Motherboard)
|
||
public:
|
||
bool asPlayedMode;
|
||
Lfo mlfo,vibratoLfo;
|
||
float vibratoAmount;
|
||
bool vibratoEnabled;
|
||
|
||
float Volume;
|
||
const static int MAX_VOICES = 32;
|
||
const static int MAX_PANNINGS = 8;
|
||
float pannings[MAX_PANNINGS];
|
||
ObxdVoice voices[MAX_VOICES];
|
||
bool uni;
|
||
bool Oversample;
|
||
|
||
bool economyMode;
|
||
Motherboard(): left(),right()
|
||
{
|
||
economyMode = true;
|
||
lkl=lkr=0;
|
||
vibratoEnabled = true;
|
||
asPlayedMode = false;
|
||
asPlayedCounter = 0;
|
||
for(int i = 0 ; i < 129 ; i++)
|
||
{
|
||
awaitingkeys[i] = false;
|
||
priorities[i] = 0;
|
||
}
|
||
vibratoAmount = 0;
|
||
Oversample=false;
|
||
mlfo= Lfo();
|
||
vibratoLfo=Lfo();
|
||
vibratoLfo.waveForm = 1;
|
||
uni = false;
|
||
wasUni = false;
|
||
Volume=0;
|
||
// voices = new ObxdVoice* [MAX_VOICES];
|
||
// pannings = new float[MAX_VOICES];
|
||
totalvc = MAX_VOICES;
|
||
vq = VoiceQueue(MAX_VOICES,voices);
|
||
for(int i = 0 ; i < MAX_PANNINGS;++i)
|
||
{
|
||
pannings[i]= 0.5;
|
||
}
|
||
}
|
||
~Motherboard()
|
||
{
|
||
//delete pannings;
|
||
//for(int i = 0 ; i < MAX_VOICES;++i)
|
||
//{
|
||
// delete voices[i];
|
||
//}
|
||
//delete voices;
|
||
}
|
||
void setVoiceCount(int count)
|
||
{
|
||
for(int i = count ; i < MAX_VOICES;i++)
|
||
{
|
||
voices[i].NoteOff();
|
||
voices[i].ResetEnvelope();
|
||
}
|
||
vq.reInit(count);
|
||
totalvc = count;
|
||
}
|
||
void unisonOn()
|
||
{
|
||
//for(int i = 0 ; i < 110;i++)
|
||
// awaitingkeys[i] = false;
|
||
}
|
||
void setSampleRate(float sr)
|
||
{
|
||
sampleRate = sr;
|
||
sampleRateInv = 1 / sampleRate;
|
||
mlfo.setSamlpeRate(sr);
|
||
vibratoLfo.setSamlpeRate(sr);
|
||
for(int i = 0 ; i < MAX_VOICES;++i)
|
||
{
|
||
voices[i].setSampleRate(sr);
|
||
}
|
||
SetOversample(Oversample);
|
||
}
|
||
void sustainOn()
|
||
{
|
||
for(int i = 0 ; i < MAX_VOICES;i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
p->sustOn();
|
||
}
|
||
}
|
||
void sustainOff()
|
||
{
|
||
for(int i = 0 ; i < MAX_VOICES;i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
p->sustOff();
|
||
}
|
||
}
|
||
void setNoteOn(int noteNo,float velocity)
|
||
{
|
||
asPlayedCounter++;
|
||
priorities[noteNo] = asPlayedCounter;
|
||
bool processed=false;
|
||
if (wasUni != uni)
|
||
unisonOn();
|
||
if (uni)
|
||
{
|
||
if(!asPlayedMode)
|
||
{
|
||
int minmidi = 129;
|
||
for(int i = 0 ; i < totalvc; i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if(p->midiIndx < minmidi && p->Active)
|
||
{
|
||
minmidi = p->midiIndx;
|
||
}
|
||
}
|
||
if(minmidi < noteNo)
|
||
{
|
||
awaitingkeys[noteNo] = true;
|
||
}
|
||
else
|
||
{
|
||
for(int i = 0 ; i < totalvc;i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if(p->midiIndx > noteNo && p->Active)
|
||
{
|
||
awaitingkeys[p->midiIndx] = true;
|
||
p->NoteOn(noteNo,-0.5);
|
||
}
|
||
else
|
||
{
|
||
p->NoteOn(noteNo,velocity);
|
||
}
|
||
}
|
||
}
|
||
processed = true;
|
||
}
|
||
else
|
||
{
|
||
for(int i = 0 ; i < totalvc; i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if(p->Active)
|
||
{
|
||
awaitingkeys[p->midiIndx] = true;
|
||
p->NoteOn(noteNo,-0.5);
|
||
}
|
||
else
|
||
{
|
||
p->NoteOn(noteNo,velocity);
|
||
}
|
||
}
|
||
processed = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < totalvc && !processed; i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if (!p->Active)
|
||
{
|
||
p->NoteOn(noteNo,velocity);
|
||
processed = true;
|
||
}
|
||
}
|
||
}
|
||
// if voice steal occured
|
||
if(!processed)
|
||
{
|
||
//
|
||
if(!asPlayedMode)
|
||
{
|
||
int maxmidi = 0;
|
||
ObxdVoice* highestVoiceAvalible = NULL;
|
||
for(int i = 0 ; i < totalvc; i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if(p->midiIndx > maxmidi)
|
||
{
|
||
maxmidi = p->midiIndx;
|
||
highestVoiceAvalible = p;
|
||
}
|
||
}
|
||
if(maxmidi < noteNo)
|
||
{
|
||
awaitingkeys[noteNo] = true;
|
||
}
|
||
else
|
||
{
|
||
highestVoiceAvalible->NoteOn(noteNo,-0.5);
|
||
awaitingkeys[maxmidi] = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int minPriority = INT_MAX;
|
||
ObxdVoice* minPriorityVoice = NULL;
|
||
for(int i = 0 ; i < totalvc; i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if(priorities[p->midiIndx] <minPriority)
|
||
{
|
||
minPriority = priorities[p->midiIndx];
|
||
minPriorityVoice = p;
|
||
}
|
||
}
|
||
awaitingkeys[minPriorityVoice->midiIndx] = true;
|
||
minPriorityVoice->NoteOn(noteNo,-0.5);
|
||
}
|
||
}
|
||
wasUni = uni;
|
||
}
|
||
|
||
void setNoteOff(int noteNo)
|
||
{
|
||
awaitingkeys[noteNo] = false;
|
||
int reallocKey = 0;
|
||
//Voice release case
|
||
if(!asPlayedMode)
|
||
{
|
||
while(reallocKey < 129 &&(!awaitingkeys[reallocKey]))
|
||
{
|
||
reallocKey++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
reallocKey = 129;
|
||
int maxPriority = INT_MIN;
|
||
for(int i = 0 ; i < 129;i++)
|
||
{
|
||
if(awaitingkeys[i] && (maxPriority < priorities[i]))
|
||
{
|
||
reallocKey = i;
|
||
maxPriority = priorities[i];
|
||
}
|
||
}
|
||
}
|
||
if(reallocKey !=129)
|
||
{
|
||
for(int i = 0 ; i < totalvc; i++)
|
||
{
|
||
ObxdVoice* p = vq.getNext();
|
||
if((p->midiIndx == noteNo) && (p->Active))
|
||
{
|
||
p->NoteOn(reallocKey,-0.5);
|
||
awaitingkeys[reallocKey] = false;
|
||
}
|
||
|
||
}
|
||
}
|
||
else
|
||
//No realloc
|
||
{
|
||
for (int i = 0; i < totalvc; i++)
|
||
{
|
||
ObxdVoice* n = vq.getNext();
|
||
if (n->midiIndx==noteNo && n->Active)
|
||
{
|
||
n->NoteOff();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
void SetOversample(bool over)
|
||
{
|
||
if(over==true)
|
||
{
|
||
mlfo.setSamlpeRate(sampleRate*2);
|
||
vibratoLfo.setSamlpeRate(sampleRate*2);
|
||
}
|
||
else
|
||
{
|
||
mlfo.setSamlpeRate(sampleRate);
|
||
vibratoLfo.setSamlpeRate(sampleRate);
|
||
}
|
||
for(int i = 0 ; i < MAX_VOICES;i++)
|
||
{
|
||
voices[i].setHQ(over);
|
||
if(over)
|
||
voices[i].setSampleRate(sampleRate*2);
|
||
else
|
||
voices[i].setSampleRate(sampleRate);
|
||
}
|
||
Oversample = over;
|
||
}
|
||
inline float processSynthVoice(ObxdVoice& b,float lfoIn,float vibIn )
|
||
{
|
||
if(economyMode)
|
||
b.checkAdsrState();
|
||
if(b.shouldProcessed||(!economyMode))
|
||
{
|
||
b.lfoIn=lfoIn;
|
||
b.lfoVibratoIn=vibIn;
|
||
return b.ProcessSample();
|
||
}
|
||
return 0;
|
||
}
|
||
void processSample(float* sm1,float* sm2)
|
||
{
|
||
mlfo.update();
|
||
vibratoLfo.update();
|
||
float vl=0,vr=0;
|
||
float vlo = 0 , vro = 0 ;
|
||
float lfovalue = mlfo.getVal();
|
||
float viblfo = vibratoEnabled?(vibratoLfo.getVal() * vibratoAmount):0;
|
||
float lfovalue2=0,viblfo2;
|
||
if(Oversample)
|
||
{
|
||
mlfo.update();
|
||
vibratoLfo.update();
|
||
lfovalue2 = mlfo.getVal();
|
||
viblfo2 = vibratoEnabled?(vibratoLfo.getVal() * vibratoAmount):0;
|
||
}
|
||
|
||
for(int i = 0 ; i < totalvc;i++)
|
||
{
|
||
float x1 = processSynthVoice(voices[i],lfovalue,viblfo);
|
||
if(Oversample)
|
||
{
|
||
float x2 = processSynthVoice(voices[i],lfovalue2,viblfo2);
|
||
vlo+=x2*(1-pannings[i % MAX_PANNINGS]);
|
||
vro+=x2*(pannings[i % MAX_PANNINGS]);
|
||
}
|
||
vl+=x1*(1-pannings[i % MAX_PANNINGS]);
|
||
vr+=x1*(pannings[i % MAX_PANNINGS]);
|
||
}
|
||
if(Oversample)
|
||
{
|
||
vl = left.Calc(vl,vlo);
|
||
vr = right.Calc(vr,vro);
|
||
}
|
||
*sm1 = vl*Volume;
|
||
*sm2 = vr*Volume;
|
||
}
|
||
};
|