#include "synth.h"
#include <stdlib.h>
#include <QDebug>
//#define DEBUG_TUNING 1
#ifdef DEBUG_TUNING
#include <math.h>
#endif
static const uint tune12[] = { 44000, 46616, 49388, 52325, 55437, 58733, 62225, 65926, 69846, 73999, 78399, 83061 };
Synthesizer::Synthesizer(QObject *parent) : QIODevice(parent),
forceBuffer(true),
gateState(false),
deviceId(0),
sizeLimit(0)
{
setSampleRate();
int sc = -6;
uint j = 3;
for(uint i = 0; i < sizeof(keytuning) / sizeof(*keytuning); ++i) {
uint tune = tune12[j];
if (sc > 0)
tune <<= sc;
else if (sc < 0)
tune = (tune + (1 << (-sc-1))) >> -sc;
keytuning[i] = tune;
if (++j >= sizeof(tune12) / sizeof(*tune12)) {
j = 0; ++sc;
}
}
}
Synthesizer::~Synthesizer()
{
}
uint Synthesizer::keyTune(uint key)
{
return keytuning[key];
}
void Synthesizer::setSampleRate(uint rate)
{
if (rate == 0)
rate = 44100;
srate = rate;
tuneConst = ((((10 << 27) + srate / 2) / srate) *
(((1U << 23) + 500) / 1000) + (1 << 13)
) >> 14;
}
void Synthesizer::setSizeLimit(uint limit)
{
sizeLimit = limit;
}
void Synthesizer::setKeyTuning(uint key, uint tune)
{
keytuning[key] = tune;
for(int ch = 0; ch < channelData.count(); ++ch) {
Channel &chd = channelData[ch];
for(uint ti = 0; ti < chd.threadsCount; ++ti) {
Thread &thr = threadData[chd.firstThread + ti];
if (thr.currAmp != 0 && thr.assignedKey == key) {
thr.targetFreq = calcTuning(ch, key);
// thr.legatoRate = (thr.targetFreq + (1 << 5)) >> 6;
}
}
}
}
void Synthesizer::setChannelsCount(uint count)
{
channelData.resize(count);
threadData.resize(count);
for(uint i = 0; i < count; ++i) {
channelData[i].firstThread = i;
channelData[i].threadsCount = 1;
}
}
void Synthesizer::setChannelPoly(int channel, uint poly)
{
int channelCount = channelData.count();
if (channel < 0 || channel >= channelCount)
return;
channelData[channel].threadsCount = poly;
uint threadCount = 0;
for(int i = 0; i < channelCount; ++i) {
channelData[i].firstThread = threadCount;
threadCount += channelData[i].threadsCount;
}
threadData.resize(threadCount);
}
void Synthesizer::setChannelMode(int channel, Synthesizer::ChannelMode mode)
{
if (channel < 0 || channel >= channelData.count())
return;
channelData[channel].chmode = mode;
}
void Synthesizer::setChannelSample(int channel, QString name, uint attack, uint decay, uint release, uint periods)
{
if (channel < 0 || channel >= channelData.count())
return;
Channel &chd = channelData[channel];
chd.attackRate = attack;
chd.decayRate = decay;
chd.releaseRate = release;
chd.samplePeriods = (periods == 0 ? 1 : periods);
chd.sampleRes = new QResource(name);
if (chd.sampleRes->isValid()) {
chd.sample = reinterpret_cast<const qint16 *>(chd.sampleRes->data());
chd.sampleLen = chd.sampleRes->size() / 2;
}
mute(channel);
}
void Synthesizer::setChannelVolume(int channel, int volume)
{
if (channel < 0 || channel >= channelData.count())
return;
channelData[channel].volume = volumeToAmp(volume) >> 16;
}
void Synthesizer::setChannelLegatoRate(int channel, int rate)
{
if (channel < 0 || channel >= channelData.count())
return;
uint rr = volumeToAmp(rate);
rr >>= 22;
channelData[channel].legatoRate = static_cast<uint>(rr);
}
void Synthesizer::clearRhythmEvents(int channel)
{
if (channel < 0 || channel >= channelData.count())
return;
channelData[channel].rhythmPos = 0;
channelData[channel].rhythmTime = 0;
channelData[channel].rhythm.clear();
}
void Synthesizer::addRhythmEvent(int channel, uint key, uint timeStep)
{
if (channel < 0 || channel >= channelData.count())
return;
if (timeStep > 0)
channelData[channel].rhythm.append(QPair<uint, int>(timeStep * srate, key));
}
bool Synthesizer::reset()
{
forceBuffer = true;
return true;
}
uint Synthesizer::volumeToAmp(int vol)
{
uint amp = 0;
if (vol > 60) vol = 60;
if (vol > 0) {
static uint ampt6[] = { 287, 323, 362, 406, 456, 512 };
vol += 5;
int vs = vol / 6, vm = vol % 6;
amp = (8388607U * ampt6[vm]) >> (10 - vs);
}
return amp;
}
void Synthesizer::keyDown(int channel, uint key, uint velocity)
{
if (channel < 0 || channel >= channelData.count() || key >= 128)
return;
if (velocity == 0) {
releaseKey(channel, key);
return;
}
Channel &ch = channelData[channel];
uint firstThread = ch.firstThread;
uint threadsCount = ch.threadsCount;
if (velocity > 127)
velocity = 127;
// Select free thread for channel
int thread = -1, prio = 0;
for(uint ti = 0; ti < threadsCount; ++ti) {
int th = static_cast<int>(firstThread + ti);
Thread &thr = threadData[th];
if (thr.assignedKey == key) {
if (prio < 2) {
thread = th;
prio = 2;
}
} else if (thr.targetAmp != 0) {
if (ch.chmode == Legato) {
if (prio < 3) {
thread = th;
prio = 3;
}
ch.unassigned.push(thr.assignedKey);
} else if (ch.chmode == Mono) {
thr.targetAmp = 0;
ch.unassigned.push(thr.assignedKey);
}
} else {
if (prio < 1 || (prio == 1 && thr.currAmp < threadData[thread].currAmp)) {
thread = th;
prio = 1;
}
}
}
if (thread != -1) {
assignKeyToThread(channel, thread, key, velocity);
#ifdef DEBUG_TUNING
static uint lastf = 0;
qDebug() << channel << key << threadData[thread].targetFreq
<< (lastf != 0 ? 72*log2(static_cast<double>(threadData[thread].targetFreq) / lastf) : 0);
lastf = threadData[thread].targetFreq;
#endif
} else {
ch.unassigned.push(key);
}
}
void Synthesizer::keyDown(uint key, uint velocity)
{
keyDown(0, key, velocity);
}
void Synthesizer::mute(int channel)
{
if (channel >= channelData.count())
return;
int t1 = (channel < 0) ? 0 : channelData[channel].firstThread;
int t2 = (channel < 0) ? threadData.count() - 1 : t1 + channelData[channel].threadsCount;
for(int th = t1; th < t2; ++th) {
threadData[th].targetAmp = 0;
threadData[th].ampRate = 8;
}
if (channel < 0) {
for(int ch = 0; ch < channelData.count(); ++ch)
channelData[ch].unassigned.clear();
} else {
channelData[channel].unassigned.clear();
}
}
qint64 Synthesizer::readData(char *data, qint64 maxSize)
{
uint dataOffset = 0;
/*
if (forceBuffer) {
if (sizeLimit != 0 && maxSize > sizeLimit * 2)
maxSize = sizeLimit * 2;
forceBuffer = false;
} else if (sizeLimit != 0 && maxSize > sizeLimit) {
maxSize = sizeLimit;
}
*/
while(dataOffset < maxSize) {
bool gate = false;
for(int ch = 0; dataOffset < maxSize && ch < channelData.count(); ++ch) {
int sample = 0;
uint threadsCount = channelData[ch].threadsCount;
uint sampleLen = channelData[ch].sampleLen;
if (threadsCount == 0 || sampleLen == 0)
continue;
uint volume = channelData[ch].volume;
int rhytmKey = -1;
if (!channelData[ch].rhythm.isEmpty()) {
int pos = channelData[ch].rhythmPos;
uint time = channelData[ch].rhythmTime;
time += 1000;
if (time >= channelData[ch].rhythm[pos].first) {
rhytmKey = channelData[ch].rhythm[pos].second;
time -= channelData[ch].rhythm[pos].first;
if (++pos >= channelData[ch].rhythm.count())
pos = 0;
channelData[ch].rhythmPos = pos;
}
channelData[ch].rhythmTime = time;
}
for(uint ti = 0; ti < threadsCount; ++ti) {
int th = channelData[ch].firstThread + ti;
if (rhytmKey != -1) {
assignKeyToThread(ch, th, rhytmKey, 127);
/*
threadData[th].targetAmp = 4294967295U;
threadData[th].ampRate = channelData[ch].attackRate;
uint z = (tuneConst * keyTuning[rhytmKey] + (1 << 10)) >> 11;
z = (z * channelData[ch].sampleLen / channelData[ch].samplePeriods + (1 << 8)) >> 9;
threadData[th].targetFreq = z;
threadData[th].legatoRate = (z + (1 << 5)) >> 6;
*/
}
uint amp = threadData[th].currAmp;
uint frq = threadData[th].currFreq;
uint toFrq = threadData[th].targetFreq;
// Do legato
if (toFrq != frq) {
uint lstep = threadData[th].legatoStep;
if (amp == 0)
frq = toFrq;
else if (toFrq > frq) {
frq += lstep;
if (frq > toFrq)
frq = toFrq;
} else {
frq -= lstep;
if (frq < toFrq)
frq = toFrq;
}
threadData[th].currFreq = frq;
}
// Do envelope
uint toAmp = threadData[th].targetAmp;
if (toAmp != amp) {
if (toAmp > amp) {
// Attack
uint newAmp = amp + qMax((toAmp - amp) >> (18 - threadData[th].ampRate), 128U);
if (newAmp >= toAmp || newAmp < amp) {
// Decay
amp = toAmp;
if (channelData[ch].decayRate != 0) {
threadData[th].targetAmp = 0;
threadData[th].ampRate = channelData[ch].decayRate;
}
} else {
amp = newAmp;
}
} else {
// Release
uint newAmp = amp - qMax((amp - toAmp) >> (18 - threadData[th].ampRate), 128U);
if (newAmp < toAmp || newAmp > amp) {
amp = toAmp;
} else
amp = newAmp;
if (amp == 0) {
threadData[th].samplePtr = 0;
emit keyStateChanged(ch, threadData[th].assignedKey, false);
}
}
threadData[th].currAmp = amp;
}
if (amp != 0) {
// Get sample with interpolation
uint ptr = threadData[th].samplePtr;
if (ptr >= sampleLen << 16)
ptr = 0;
uint p1 = ptr >> 16;
uint p2 = (ptr >> 4) & 0x0FFF;
int s1 = channelData[ch].sample[p1] * (0x1000 - p2);
++p1;
if (p1 >= sampleLen)
p1 = 0;
s1 += channelData[ch].sample[p1] * p2;
s1 = (s1 + (1 << 11)) >> 12;
sample += (((amp >> 16) * volume) >> 20) * s1;
ptr += frq;
if (ptr >= sampleLen << 16)
ptr -= sampleLen << 16;
threadData[th].samplePtr = ptr;
gate = true;
}
}
sample = (sample + (1 << 11)) >> 12;
if (sample > 32767)
sample = 32767;
else if (sample < -32767)
sample = -32767;
*reinterpret_cast<qint16 *>(&data[dataOffset]) = static_cast<qint16>(sample);
dataOffset += 2;
}
if (gate != gateState) {
gateState = gate;
emit gateStateChanged(gateState, deviceId);
}
}
return dataOffset;
}
qint64 Synthesizer::writeData(const char *data, qint64 maxSize)
{
Q_UNUSED(data)
Q_UNUSED(maxSize)
return 0;
}
uint Synthesizer::calcTuning(uint channel, uint key)
{
Channel &ch = channelData[channel];
uint z = (tuneConst * keytuning[key] + (1 << 12)) >> 13;
z = (z * ch.sampleLen / ch.samplePeriods + (1 << 6)) >> 7;
return z;
}
void Synthesizer::assignKeyToThread(uint channel, uint thread, uint key, uint velocity)
{
Channel &ch = channelData[channel];
Thread &thr = threadData[thread];
if (thr.assignedKey != 0 && thr.currAmp != 0 && thr.assignedKey != key && thr.targetAmp != 0) {
emit keyStateChanged(channel, thr.assignedKey, false);
}
thr.targetAmp = velocity * 33818640U;
thr.ampRate = ch.attackRate;
thr.assignedKey = key;
thr.assignedVelocity = velocity;
thr.targetFreq = calcTuning(channel, key);
uint step = thr.targetFreq > thr.currFreq ?
thr.targetFreq - thr.currFreq :
thr.currFreq - thr.targetFreq;
step = (step + (1 << 5)) >> 6;
if (ch.decayRate == 0)
step = (step * ch.legatoRate + (1 << 11)) >> 11;
if (step == 0)
step = 1;
thr.legatoStep = step;
emit keyStateChanged(channel, key, true);
//qDebug() << channel << thread << thr.targetFreq << step;
}
bool Synthesizer::releaseKey(int channel, uint key)
{
if (channel < 0 || channel >= channelData.count() || key >= 128)
return false;
bool ret = false;
Channel &ch = channelData[channel];
uint firstThread = ch.firstThread;
uint threadsCount = ch.threadsCount;
int i = ch.unassigned.indexOf(key);
if (i >= 0) {
ch.unassigned.remove(i);
ret = true;
}
for(uint ti = 0; ti < threadsCount; ++ti) {
uint th = firstThread + ti;
Thread &thr = threadData[th];
if (thr.assignedKey == key) {
if (thr.currAmp != 0)
emit keyStateChanged(channel, key, false);
if (thr.targetAmp != 0) {
thr.targetAmp = 0;
thr.ampRate = ch.releaseRate;
if (!ch.unassigned.empty()) {
assignKeyToThread(channel, th, ch.unassigned.pop(), thr.assignedVelocity); // FIXME: velocity lost
}
ret = true;
}
}
}
return ret;
}