mirror of https://github.com/probonopd/MiniDexed
Use Synth_Dexed, thanks @dcoredump
https://github.com/rsta2/circle/issues/274#issuecomment-1032660587pull/4/head
parent
bdaacaac22
commit
89aca1d0cc
@ -1,3 +1,6 @@ |
|||||||
[submodule "circle-stdlib"] |
[submodule "circle-stdlib"] |
||||||
path = circle-stdlib |
path = circle-stdlib |
||||||
url = https://github.com/smuehlst/circle-stdlib |
url = https://github.com/smuehlst/circle-stdlib |
||||||
|
[submodule "Synth_Dexed"] |
||||||
|
path = Synth_Dexed |
||||||
|
url = https://codeberg.org/dcoredump/Synth_Dexed.git |
||||||
|
@ -0,0 +1 @@ |
|||||||
|
Subproject commit ffeef830ab1f8e99a709a5fdf08fc25f0b855ca0 |
@ -1,36 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
// A convenient wrapper for buffers with alignment constraints
|
|
||||||
|
|
||||||
// Note that if we were on C++11, we'd use aligned_storage or somesuch.
|
|
||||||
|
|
||||||
#ifndef __ALIGNED_BUF_H |
|
||||||
#define __ALIGNED_BUF_H |
|
||||||
|
|
||||||
#include<stddef.h> |
|
||||||
|
|
||||||
template<typename T, size_t size, size_t alignment = 16> |
|
||||||
class AlignedBuf { |
|
||||||
public: |
|
||||||
T *get() { |
|
||||||
return (T *)((((intptr_t)storage_) + alignment - 1) & -alignment); |
|
||||||
} |
|
||||||
private: |
|
||||||
unsigned char storage_[size * sizeof(T) + alignment]; |
|
||||||
}; |
|
||||||
|
|
||||||
#endif // __ALIGNED_BUF_H
|
|
@ -1,132 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef __CONTROLLERS_H |
|
||||||
#define __CONTROLLERS_H |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "../Dexed.h" |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
#ifdef _WIN32 |
|
||||||
#define snprintf _snprintf |
|
||||||
#endif |
|
||||||
|
|
||||||
// State of MIDI controllers
|
|
||||||
const int kControllerPitch = 128; |
|
||||||
const int kControllerPitchRangeUp = 129; |
|
||||||
const int kControllerPitchStep = 130; |
|
||||||
const int kControllerPitchRangeDn = 131; |
|
||||||
|
|
||||||
class FmCore; |
|
||||||
|
|
||||||
struct FmMod { |
|
||||||
int range; |
|
||||||
bool pitch; |
|
||||||
bool amp; |
|
||||||
bool eg; |
|
||||||
|
|
||||||
FmMod() { |
|
||||||
range = 0; |
|
||||||
pitch = false; |
|
||||||
amp = false; |
|
||||||
eg = false; |
|
||||||
} |
|
||||||
|
|
||||||
void parseConfig(const char *cfg) { |
|
||||||
int r = 0, p = 0, a = 0, e = 0; |
|
||||||
sscanf(cfg, "%d %d %d %d", &r, &p, &a, &e); |
|
||||||
|
|
||||||
range = r < 0 || r > 127 ? 0 : r; |
|
||||||
pitch = p != 0; |
|
||||||
amp = a != 0; |
|
||||||
eg = e != 0; |
|
||||||
} |
|
||||||
|
|
||||||
void setConfig(char *cfg) { |
|
||||||
snprintf(cfg, 13, "%d %d %d %d", range, pitch, amp, eg); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
class Controllers { |
|
||||||
void applyMod(int cc, FmMod &mod) { |
|
||||||
float range = 0.01 * mod.range; |
|
||||||
int total = cc * range; |
|
||||||
if ( mod.amp ) |
|
||||||
amp_mod = max(amp_mod, total); |
|
||||||
|
|
||||||
if ( mod.pitch ) |
|
||||||
pitch_mod = max(pitch_mod, total); |
|
||||||
|
|
||||||
if ( mod.eg ) |
|
||||||
eg_mod = max(eg_mod, total); |
|
||||||
} |
|
||||||
|
|
||||||
public: |
|
||||||
int values_[132]; |
|
||||||
|
|
||||||
char opSwitch[7]; |
|
||||||
|
|
||||||
int amp_mod; |
|
||||||
int pitch_mod; |
|
||||||
int eg_mod; |
|
||||||
|
|
||||||
int aftertouch_cc; |
|
||||||
int breath_cc; |
|
||||||
int foot_cc; |
|
||||||
int modwheel_cc; |
|
||||||
|
|
||||||
int masterTune; |
|
||||||
|
|
||||||
bool transpose12AsScale = true; |
|
||||||
|
|
||||||
// MPE configuration. FIXME - make this switchable
|
|
||||||
bool mpeEnabled = true; |
|
||||||
int mpePitchBendRange = 24; |
|
||||||
|
|
||||||
FmMod wheel; |
|
||||||
FmMod foot; |
|
||||||
FmMod breath; |
|
||||||
FmMod at; |
|
||||||
|
|
||||||
Controllers() { |
|
||||||
amp_mod = 0; |
|
||||||
pitch_mod = 0; |
|
||||||
eg_mod = 0; |
|
||||||
strcpy(opSwitch, "111111");
|
|
||||||
} |
|
||||||
|
|
||||||
void refresh() { |
|
||||||
amp_mod = 0; |
|
||||||
pitch_mod = 0; |
|
||||||
eg_mod = 0; |
|
||||||
|
|
||||||
applyMod(modwheel_cc, wheel); |
|
||||||
applyMod(breath_cc, breath); |
|
||||||
applyMod(foot_cc, foot); |
|
||||||
applyMod(aftertouch_cc, at); |
|
||||||
|
|
||||||
if ( ! ((wheel.eg || foot.eg) || (breath.eg || at.eg)) ) |
|
||||||
eg_mod = 127; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
FmCore *core; |
|
||||||
}; |
|
||||||
|
|
||||||
#endif // __CONTROLLERS_H
|
|
||||||
|
|
@ -1,358 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2016-2017 Pascal Gauthier. |
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <math.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include "synth.h" |
|
||||||
#include "freqlut.h" |
|
||||||
#include "exp2.h" |
|
||||||
#include "controllers.h" |
|
||||||
#include "dx7note.h" |
|
||||||
#include <iostream> |
|
||||||
#include <cmath> |
|
||||||
|
|
||||||
const int FEEDBACK_BITDEPTH = 8; |
|
||||||
|
|
||||||
const int32_t coarsemul[] = { |
|
||||||
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600, |
|
||||||
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816, |
|
||||||
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858, |
|
||||||
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032, |
|
||||||
81503396, 82323963, 83117622 |
|
||||||
}; |
|
||||||
|
|
||||||
int32_t Dx7Note::osc_freq(int midinote, int mode, int coarse, int fine, int detune) { |
|
||||||
// TODO: pitch randomization
|
|
||||||
int32_t logfreq; |
|
||||||
if (mode == 0) { |
|
||||||
logfreq = tuning_state_->midinote_to_logfreq(midinote); |
|
||||||
|
|
||||||
// could use more precision, closer enough for now. those numbers comes from my DX7
|
|
||||||
double detuneRatio = 0.0209 * exp(-0.396 * (((float)logfreq)/(1<<24))) / 7; |
|
||||||
logfreq += detuneRatio * logfreq * (detune - 7); |
|
||||||
|
|
||||||
logfreq += coarsemul[coarse & 31]; |
|
||||||
if (fine) { |
|
||||||
// (1 << 24) / log(2)
|
|
||||||
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5); |
|
||||||
} |
|
||||||
|
|
||||||
// // This was measured at 7.213Hz per count at 9600Hz, but the exact
|
|
||||||
// // value is somewhat dependent on midinote. Close enough for now.
|
|
||||||
// //logfreq += 12606 * (detune -7);
|
|
||||||
} else { |
|
||||||
// ((1 << 24) * log(10) / log(2) * .01) << 3
|
|
||||||
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3; |
|
||||||
logfreq += detune > 7 ? 13457 * (detune - 7) : 0; |
|
||||||
} |
|
||||||
return logfreq; |
|
||||||
} |
|
||||||
|
|
||||||
const uint8_t velocity_data[64] = { |
|
||||||
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163, |
|
||||||
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202, |
|
||||||
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232, |
|
||||||
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251, |
|
||||||
252, 253, 254 |
|
||||||
}; |
|
||||||
|
|
||||||
// See "velocity" section of notes. Returns velocity delta in microsteps.
|
|
||||||
int ScaleVelocity(int velocity, int sensitivity) { |
|
||||||
int clamped_vel = max(0, min(127, velocity)); |
|
||||||
int vel_value = velocity_data[clamped_vel >> 1] - 239; |
|
||||||
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4; |
|
||||||
return scaled_vel; |
|
||||||
} |
|
||||||
|
|
||||||
int ScaleRate(int midinote, int sensitivity) { |
|
||||||
int x = min(31, max(0, midinote / 3 - 7)); |
|
||||||
int qratedelta = (sensitivity * x) >> 3; |
|
||||||
#ifdef SUPER_PRECISE |
|
||||||
int rem = x & 7; |
|
||||||
if (sensitivity == 3 && rem == 3) { |
|
||||||
qratedelta -= 1; |
|
||||||
} else if (sensitivity == 7 && rem > 0 && rem < 4) { |
|
||||||
qratedelta += 1; |
|
||||||
} |
|
||||||
#endif |
|
||||||
return qratedelta; |
|
||||||
} |
|
||||||
|
|
||||||
const uint8_t exp_scale_data[] = { |
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66, |
|
||||||
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250 |
|
||||||
}; |
|
||||||
|
|
||||||
int ScaleCurve(int group, int depth, int curve) { |
|
||||||
int scale; |
|
||||||
if (curve == 0 || curve == 3) { |
|
||||||
// linear
|
|
||||||
scale = (group * depth * 329) >> 12; |
|
||||||
} else { |
|
||||||
// exponential
|
|
||||||
int n_scale_data = sizeof(exp_scale_data); |
|
||||||
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)]; |
|
||||||
scale = (raw_exp * depth * 329) >> 15; |
|
||||||
} |
|
||||||
if (curve < 2) { |
|
||||||
scale = -scale; |
|
||||||
} |
|
||||||
return scale; |
|
||||||
} |
|
||||||
|
|
||||||
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth, |
|
||||||
int left_curve, int right_curve) { |
|
||||||
int offset = midinote - break_pt - 17; |
|
||||||
if (offset >= 0) { |
|
||||||
return ScaleCurve((offset+1) / 3, right_depth, right_curve); |
|
||||||
} else { |
|
||||||
return ScaleCurve(-(offset-1) / 3, left_depth, left_curve); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static const uint8_t pitchmodsenstab[] = { |
|
||||||
0, 10, 20, 33, 55, 92, 153, 255 |
|
||||||
}; |
|
||||||
|
|
||||||
// 0, 66, 109, 255
|
|
||||||
static const uint32_t ampmodsenstab[] = { |
|
||||||
0, 4342338, 7171437, 16777216 |
|
||||||
}; |
|
||||||
|
|
||||||
Dx7Note::Dx7Note(std::shared_ptr<TuningState> ts) : tuning_state_(ts) { |
|
||||||
for(int op=0;op<6;op++) { |
|
||||||
params_[op].phase = 0; |
|
||||||
params_[op].gain_out = 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { |
|
||||||
int rates[4]; |
|
||||||
int levels[4]; |
|
||||||
playingMidiNote = midinote; |
|
||||||
for (int op = 0; op < 6; op++) { |
|
||||||
int off = op * 21; |
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
rates[i] = patch[off + i]; |
|
||||||
levels[i] = patch[off + 4 + i]; |
|
||||||
} |
|
||||||
int outlevel = patch[off + 16]; |
|
||||||
outlevel = Env::scaleoutlevel(outlevel); |
|
||||||
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], |
|
||||||
patch[off + 10], patch[off + 11], patch[off + 12]); |
|
||||||
outlevel += level_scaling; |
|
||||||
outlevel = min(127, outlevel); |
|
||||||
outlevel = outlevel << 5; |
|
||||||
outlevel += ScaleVelocity(velocity, patch[off + 15]); |
|
||||||
outlevel = max(0, outlevel); |
|
||||||
int rate_scaling = ScaleRate(midinote, patch[off + 13]); |
|
||||||
env_[op].init(rates, levels, outlevel, rate_scaling); |
|
||||||
|
|
||||||
int mode = patch[off + 17]; |
|
||||||
int coarse = patch[off + 18]; |
|
||||||
int fine = patch[off + 19]; |
|
||||||
int detune = patch[off + 20]; |
|
||||||
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune); |
|
||||||
opMode[op] = mode; |
|
||||||
basepitch_[op] = freq; |
|
||||||
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; |
|
||||||
} |
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
rates[i] = patch[126 + i]; |
|
||||||
levels[i] = patch[130 + i]; |
|
||||||
} |
|
||||||
pitchenv_.set(rates, levels); |
|
||||||
algorithm_ = patch[134]; |
|
||||||
int feedback = patch[135]; |
|
||||||
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16; |
|
||||||
pitchmoddepth_ = (patch[139] * 165) >> 6; |
|
||||||
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; |
|
||||||
ampmoddepth_ = (patch[140] * 165) >> 6; |
|
||||||
|
|
||||||
// MPE default valeus
|
|
||||||
mpePitchBend = 8192; |
|
||||||
mpeTimbre = 0; |
|
||||||
mpePressure = 0; |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) { |
|
||||||
// ==== PITCH ====
|
|
||||||
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
|
|
||||||
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); |
|
||||||
int32_t pmod_1 = (((int64_t) pmd) * (int64_t) senslfo) >> 39; |
|
||||||
pmod_1 = abs(pmod_1); |
|
||||||
int32_t pmod_2 = (int32_t)(((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14); |
|
||||||
pmod_2 = abs(pmod_2); |
|
||||||
int32_t pitch_mod = max(pmod_1, pmod_2); |
|
||||||
pitch_mod = pitchenv_.getsample() + (pitch_mod * (senslfo < 0 ? -1 : 1)); |
|
||||||
|
|
||||||
// ---- PITCH BEND ----
|
|
||||||
int pitchbend = ctrls->values_[kControllerPitch]; |
|
||||||
int32_t pb = (pitchbend - 0x2000); |
|
||||||
if (pb != 0) { |
|
||||||
if (ctrls->values_[kControllerPitchStep] == 0) { |
|
||||||
if( pb >= 0 ) |
|
||||||
pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRangeUp]) / 12.0; |
|
||||||
else |
|
||||||
pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRangeDn]) / 12.0; |
|
||||||
} else { |
|
||||||
int stp = 12 / ctrls->values_[kControllerPitchStep]; |
|
||||||
pb = pb * stp / 8191; |
|
||||||
pb = (pb * (8191 / stp)) << 11; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if( ctrls->mpeEnabled ) |
|
||||||
{ |
|
||||||
int d = ((float)( (mpePitchBend-0x2000) << 11 )) * ctrls->mpePitchBendRange / 12.0;
|
|
||||||
// std::cout << mpePitchBend << " " << 0x2000 << " " << d << std::endl;
|
|
||||||
pb += d; |
|
||||||
} |
|
||||||
|
|
||||||
if( ! tuning_state_->is_standard_tuning() && pb != 0 ) |
|
||||||
{ |
|
||||||
// If we have a scale we want PB to be in scale space so we sort of need to
|
|
||||||
// unwind the combinations above and re-interpolate
|
|
||||||
|
|
||||||
float notesTuned = ( pb >> 11 ) * 12.0 / 8192; // How many steps you tuned
|
|
||||||
int floorNote = std::floor(notesTuned); |
|
||||||
float frac = notesTuned - floorNote; |
|
||||||
float targetLog = tuning_state_->midinote_to_logfreq(playingMidiNote + floorNote) * ( 1.0 - frac ) + |
|
||||||
tuning_state_->midinote_to_logfreq(playingMidiNote + floorNote + 1) * frac; // the interpolated log freq
|
|
||||||
float newpb = targetLog - tuning_state_->midinote_to_logfreq(playingMidiNote); // and the resulting bend
|
|
||||||
pb = newpb; |
|
||||||
} |
|
||||||
|
|
||||||
int32_t pitch_base = pb + ctrls->masterTune; |
|
||||||
pitch_mod += pitch_base; |
|
||||||
|
|
||||||
// ==== AMP MOD ====
|
|
||||||
lfo_val = (1<<24) - lfo_val; |
|
||||||
uint32_t amod_1 = (uint32_t)(((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8); // Q24 :D
|
|
||||||
amod_1 = (uint32_t)(((int64_t) amod_1 * (int64_t) lfo_val) >> 24); |
|
||||||
uint32_t amod_2 = (uint32_t)(((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7); // Q?? :|
|
|
||||||
uint32_t amd_mod = max(amod_1, amod_2); |
|
||||||
|
|
||||||
// ==== EG AMP MOD ====
|
|
||||||
uint32_t amod_3 = (ctrls->eg_mod+1) << 17; |
|
||||||
amd_mod = max((1<<24) - amod_3, amd_mod); |
|
||||||
|
|
||||||
// ==== OP RENDER ====
|
|
||||||
for (int op = 0; op < 6; op++) { |
|
||||||
if ( ctrls->opSwitch[op] == '0' ) { |
|
||||||
env_[op].getsample(); // advance the envelop even if it is not playing
|
|
||||||
params_[op].level_in = 0; |
|
||||||
} else { |
|
||||||
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
|
|
||||||
|
|
||||||
if ( opMode[op] ) |
|
||||||
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_base); |
|
||||||
else |
|
||||||
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_mod); |
|
||||||
|
|
||||||
int32_t level = env_[op].getsample(); |
|
||||||
if (ampmodsens_[op] != 0) { |
|
||||||
uint32_t sensamp = (uint32_t)(((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24); |
|
||||||
|
|
||||||
// TODO: mehhh.. this needs some real tuning.
|
|
||||||
uint32_t pt = exp(((float)sensamp)/262144 * 0.07 + 12.2); |
|
||||||
uint32_t ldiff = (uint32_t)(((uint64_t)level) * (((uint64_t)pt<<4)) >> 28); |
|
||||||
level -= ldiff; |
|
||||||
} |
|
||||||
params_[op].level_in = level; |
|
||||||
} |
|
||||||
} |
|
||||||
ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_); |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::keyup() { |
|
||||||
for (int op = 0; op < 6; op++) { |
|
||||||
env_[op].keydown(false); |
|
||||||
} |
|
||||||
pitchenv_.keydown(false); |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) { |
|
||||||
int rates[4]; |
|
||||||
int levels[4]; |
|
||||||
playingMidiNote = midinote; |
|
||||||
for (int op = 0; op < 6; op++) { |
|
||||||
int off = op * 21; |
|
||||||
int mode = patch[off + 17]; |
|
||||||
int coarse = patch[off + 18]; |
|
||||||
int fine = patch[off + 19]; |
|
||||||
int detune = patch[off + 20]; |
|
||||||
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune); |
|
||||||
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; |
|
||||||
opMode[op] = mode; |
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
rates[i] = patch[off + i]; |
|
||||||
levels[i] = patch[off + 4 + i]; |
|
||||||
} |
|
||||||
int outlevel = patch[off + 16]; |
|
||||||
outlevel = Env::scaleoutlevel(outlevel); |
|
||||||
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], |
|
||||||
patch[off + 10], patch[off + 11], patch[off + 12]); |
|
||||||
outlevel += level_scaling; |
|
||||||
outlevel = min(127, outlevel); |
|
||||||
outlevel = outlevel << 5; |
|
||||||
outlevel += ScaleVelocity(velocity, patch[off + 15]); |
|
||||||
outlevel = max(0, outlevel); |
|
||||||
int rate_scaling = ScaleRate(midinote, patch[off + 13]); |
|
||||||
env_[op].update(rates, levels, outlevel, rate_scaling); |
|
||||||
} |
|
||||||
algorithm_ = patch[134]; |
|
||||||
int feedback = patch[135]; |
|
||||||
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16; |
|
||||||
pitchmoddepth_ = (patch[139] * 165) >> 6; |
|
||||||
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; |
|
||||||
ampmoddepth_ = (patch[140] * 165) >> 6; |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::peekVoiceStatus(VoiceStatus &status) { |
|
||||||
for(int i=0;i<6;i++) { |
|
||||||
status.amp[i] = Exp2::lookup(params_[i].level_in - (14 * (1 << 24))); |
|
||||||
env_[i].getPosition(&status.ampStep[i]); |
|
||||||
} |
|
||||||
pitchenv_.getPosition(&status.pitchStep); |
|
||||||
} |
|
||||||
|
|
||||||
/**
|
|
||||||
* Used in monophonic mode to transfer voice state from different notes |
|
||||||
*/ |
|
||||||
void Dx7Note::transferState(Dx7Note &src) { |
|
||||||
for (int i=0;i<6;i++) { |
|
||||||
env_[i].transfer(src.env_[i]); |
|
||||||
params_[i].gain_out = src.params_[i].gain_out; |
|
||||||
params_[i].phase = src.params_[i].phase; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::transferSignal(Dx7Note &src) { |
|
||||||
for (int i=0;i<6;i++) { |
|
||||||
params_[i].gain_out = src.params_[i].gain_out; |
|
||||||
params_[i].phase = src.params_[i].phase; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Dx7Note::oscSync() { |
|
||||||
for (int i=0;i<6;i++) { |
|
||||||
params_[i].gain_out = 0; |
|
||||||
params_[i].phase = 0; |
|
||||||
} |
|
||||||
} |
|
@ -1,90 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2016-2017 Pascal Gauthier. |
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef SYNTH_DX7NOTE_H_ |
|
||||||
#define SYNTH_DX7NOTE_H_ |
|
||||||
|
|
||||||
// This is the logic to put together a note from the MIDI description
|
|
||||||
// and run the low-level modules.
|
|
||||||
|
|
||||||
// It will continue to evolve a bit, as note-stealing logic, scaling,
|
|
||||||
// and real-time control of parameters live here.
|
|
||||||
|
|
||||||
#include "env.h" |
|
||||||
#include "pitchenv.h" |
|
||||||
#include "fm_core.h" |
|
||||||
#include "tuning.h" |
|
||||||
#include <memory> |
|
||||||
|
|
||||||
struct VoiceStatus { |
|
||||||
uint32_t amp[6]; |
|
||||||
char ampStep[6]; |
|
||||||
char pitchStep; |
|
||||||
}; |
|
||||||
|
|
||||||
class Dx7Note { |
|
||||||
public: |
|
||||||
Dx7Note(std::shared_ptr<TuningState> ts); |
|
||||||
void init(const uint8_t patch[156], int midinote, int velocity); |
|
||||||
|
|
||||||
// Note: this _adds_ to the buffer. Interesting question whether it's
|
|
||||||
// worth it...
|
|
||||||
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, |
|
||||||
const Controllers *ctrls); |
|
||||||
|
|
||||||
void keyup(); |
|
||||||
|
|
||||||
// TODO: some way of indicating end-of-note. Maybe should be a return
|
|
||||||
// value from the compute method? (Having a count return from keyup
|
|
||||||
// is also tempting, but if there's a dynamic parameter change after
|
|
||||||
// keyup, that won't work.
|
|
||||||
|
|
||||||
// PG:add the update
|
|
||||||
void update(const uint8_t patch[156], int midinote, int velocity); |
|
||||||
void peekVoiceStatus(VoiceStatus &status); |
|
||||||
void transferState(Dx7Note& src); |
|
||||||
void transferSignal(Dx7Note &src); |
|
||||||
void oscSync(); |
|
||||||
|
|
||||||
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune); |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> tuning_state_; |
|
||||||
|
|
||||||
int mpePitchBend = 8192; |
|
||||||
int mpePressure = 0; |
|
||||||
int mpeTimbre = 0; |
|
||||||
|
|
||||||
private: |
|
||||||
Env env_[6]; |
|
||||||
FmOpParams params_[6]; |
|
||||||
PitchEnv pitchenv_; |
|
||||||
int32_t basepitch_[6]; |
|
||||||
int32_t fb_buf_[2]; |
|
||||||
int32_t fb_shift_; |
|
||||||
int32_t ampmodsens_[6]; |
|
||||||
int32_t opMode[6]; |
|
||||||
|
|
||||||
uint8_t playingMidiNote; // We need this for scale aware pitch bend
|
|
||||||
|
|
||||||
int ampmoddepth_; |
|
||||||
int algorithm_; |
|
||||||
int pitchmoddepth_; |
|
||||||
int pitchmodsens_; |
|
||||||
|
|
||||||
}; |
|
||||||
|
|
||||||
#endif // SYNTH_DX7NOTE_H_
|
|
@ -1,192 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2017 Pascal Gauthier. |
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "env.h" |
|
||||||
|
|
||||||
#include "../Dexed.h" |
|
||||||
//using namespace std;
|
|
||||||
|
|
||||||
uint32_t Env::sr_multiplier = (1<<24); |
|
||||||
|
|
||||||
const int levellut[] = { |
|
||||||
0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46 |
|
||||||
}; |
|
||||||
|
|
||||||
#ifdef ACCURATE_ENVELOPE |
|
||||||
const int statics[] = { |
|
||||||
1764000, 1764000, 1411200, 1411200, 1190700, 1014300, 992250, |
|
||||||
882000, 705600, 705600, 584325, 507150, 502740, 441000, 418950, |
|
||||||
352800, 308700, 286650, 253575, 220500, 220500, 176400, 145530, |
|
||||||
145530, 125685, 110250, 110250, 88200, 88200, 74970, 61740, |
|
||||||
61740, 55125, 48510, 44100, 37485, 31311, 30870, 27562, 27562, |
|
||||||
22050, 18522, 17640, 15435, 14112, 13230, 11025, 9261, 9261, 7717, |
|
||||||
6615, 6615, 5512, 5512, 4410, 3969, 3969, 3439, 2866, 2690, 2249, |
|
||||||
1984, 1896, 1808, 1411, 1367, 1234, 1146, 926, 837, 837, 705, |
|
||||||
573, 573, 529, 441, 441 |
|
||||||
// and so on, I stopped measuring after R=76 (needs to be double-checked anyway)
|
|
||||||
}; |
|
||||||
#endif |
|
||||||
|
|
||||||
void Env::init_sr(double sampleRate) { |
|
||||||
sr_multiplier = (44100.0 / sampleRate) * (1<<24); |
|
||||||
} |
|
||||||
|
|
||||||
void Env::init(const int r[4], const int l[4], int ol, int rate_scaling) { |
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
rates_[i] = r[i]; |
|
||||||
levels_[i] = l[i]; |
|
||||||
} |
|
||||||
outlevel_ = ol; |
|
||||||
rate_scaling_ = rate_scaling; |
|
||||||
level_ = 0; |
|
||||||
down_ = true; |
|
||||||
advance(0); |
|
||||||
} |
|
||||||
|
|
||||||
int32_t Env::getsample() { |
|
||||||
#ifdef ACCURATE_ENVELOPE |
|
||||||
if (staticcount_) { |
|
||||||
staticcount_ -= N; |
|
||||||
if (staticcount_ <= 0) { |
|
||||||
staticcount_ = 0; |
|
||||||
advance(ix_ + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
if (ix_ < 3 || ((ix_ < 4) && !down_)) { |
|
||||||
if (staticcount_) { |
|
||||||
; |
|
||||||
} |
|
||||||
else if (rising_) { |
|
||||||
const int jumptarget = 1716; |
|
||||||
if (level_ < (jumptarget << 16)) { |
|
||||||
level_ = jumptarget << 16; |
|
||||||
} |
|
||||||
level_ += (((17 << 24) - level_) >> 24) * inc_; |
|
||||||
// TODO: should probably be more accurate when inc is large
|
|
||||||
if (level_ >= targetlevel_) { |
|
||||||
level_ = targetlevel_; |
|
||||||
advance(ix_ + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
else { // !rising
|
|
||||||
level_ -= inc_; |
|
||||||
if (level_ <= targetlevel_) { |
|
||||||
level_ = targetlevel_; |
|
||||||
advance(ix_ + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
// TODO: this would be a good place to set level to 0 when under threshold
|
|
||||||
return level_; |
|
||||||
} |
|
||||||
|
|
||||||
void Env::keydown(bool d) { |
|
||||||
if (down_ != d) { |
|
||||||
down_ = d; |
|
||||||
advance(d ? 0 : 3); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int Env::scaleoutlevel(int outlevel) { |
|
||||||
return outlevel >= 20 ? 28 + outlevel : levellut[outlevel]; |
|
||||||
} |
|
||||||
|
|
||||||
void Env::advance(int newix) { |
|
||||||
ix_ = newix; |
|
||||||
if (ix_ < 4) { |
|
||||||
int newlevel = levels_[ix_]; |
|
||||||
int actuallevel = scaleoutlevel(newlevel) >> 1; |
|
||||||
actuallevel = (actuallevel << 6) + outlevel_ - 4256; |
|
||||||
actuallevel = actuallevel < 16 ? 16 : actuallevel; |
|
||||||
// level here is same as Java impl
|
|
||||||
targetlevel_ = actuallevel << 16; |
|
||||||
rising_ = (targetlevel_ > level_); |
|
||||||
|
|
||||||
// rate
|
|
||||||
int qrate = (rates_[ix_] * 41) >> 6; |
|
||||||
qrate += rate_scaling_; |
|
||||||
qrate = min(qrate, 63); |
|
||||||
|
|
||||||
#ifdef ACCURATE_ENVELOPE |
|
||||||
if (targetlevel_ == level_ || (ix_ == 0 && newlevel == 0)) { |
|
||||||
// approximate number of samples at 44.100 kHz to achieve the time
|
|
||||||
// empirically gathered using 2 TF1s, could probably use some double-checking
|
|
||||||
// and cleanup, but it's pretty close for now.
|
|
||||||
int staticrate = rates_[ix_]; |
|
||||||
staticrate += rate_scaling_; // needs to be checked, as well, but seems correct
|
|
||||||
staticrate = min(staticrate, 99); |
|
||||||
staticcount_ = staticrate < 77 ? statics[staticrate] : 20 * (99 - staticrate); |
|
||||||
if (staticrate < 77 && (ix_ == 0 && newlevel == 0)) { |
|
||||||
staticcount_ /= 20; // attack is scaled faster
|
|
||||||
} |
|
||||||
staticcount_ = (int)(((int64_t)staticcount_ * (int64_t)sr_multiplier) >> 24); |
|
||||||
} |
|
||||||
else { |
|
||||||
staticcount_ = 0; |
|
||||||
} |
|
||||||
#endif |
|
||||||
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2)); |
|
||||||
// meh, this should be fixed elsewhere
|
|
||||||
inc_ = (int)(((int64_t)inc_ * (int64_t)sr_multiplier) >> 24); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Env::update(const int r[4], const int l[4], int ol, int rate_scaling) { |
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
rates_[i] = r[i]; |
|
||||||
levels_[i] = l[i]; |
|
||||||
} |
|
||||||
outlevel_ = ol; |
|
||||||
rate_scaling_ = rate_scaling; |
|
||||||
if ( down_ ) { |
|
||||||
// for now we simply reset ourselves at level 3
|
|
||||||
int newlevel = levels_[2]; |
|
||||||
int actuallevel = scaleoutlevel(newlevel) >> 1; |
|
||||||
actuallevel = (actuallevel << 6) - 4256; |
|
||||||
actuallevel = actuallevel < 16 ? 16 : actuallevel; |
|
||||||
targetlevel_ = actuallevel << 16; |
|
||||||
advance(2); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Env::getPosition(char *step) { |
|
||||||
*step = ix_; |
|
||||||
} |
|
||||||
|
|
||||||
void Env::transfer(Env &src) { |
|
||||||
for(int i=0;i<4;i++) { |
|
||||||
rates_[i] = src.rates_[i]; |
|
||||||
levels_[i] = src.levels_[i]; |
|
||||||
} |
|
||||||
outlevel_ = src.outlevel_; |
|
||||||
rate_scaling_ = src.rate_scaling_; |
|
||||||
level_ = src.level_; |
|
||||||
targetlevel_ = src.targetlevel_; |
|
||||||
rising_= src.rising_; |
|
||||||
ix_ = src.ix_; |
|
||||||
down_ = src.down_; |
|
||||||
#ifdef ACCURATE_ENVELOPE |
|
||||||
staticcount_ = src.staticcount_; |
|
||||||
#endif |
|
||||||
inc_ = src.inc_; |
|
||||||
} |
|
||||||
|
|
@ -1,82 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2017 Pascal Gauthier.
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef __ENV_H |
|
||||||
#define __ENV_H |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
|
|
||||||
// DX7 envelope generation
|
|
||||||
|
|
||||||
#define ACCURATE_ENVELOPE |
|
||||||
|
|
||||||
class Env { |
|
||||||
public: |
|
||||||
|
|
||||||
// The rates and levels arrays are calibrated to match the Dx7 parameters
|
|
||||||
// (ie, value 0..99). The outlevel parameter is calibrated in microsteps
|
|
||||||
// (ie units of approx .023 dB), with 99 * 32 = nominal full scale. The
|
|
||||||
// rate_scaling parameter is in qRate units (ie 0..63).
|
|
||||||
void init(const int rates[4], const int levels[4], int outlevel, |
|
||||||
int rate_scaling); |
|
||||||
|
|
||||||
void update(const int rates[4], const int levels[4], int outlevel, |
|
||||||
int rate_scaling); |
|
||||||
// Result is in Q24/doubling log format. Also, result is subsampled
|
|
||||||
// for every N samples.
|
|
||||||
// A couple more things need to happen for this to be used as a gain
|
|
||||||
// value. First, the # of outputs scaling needs to be applied. Also,
|
|
||||||
// modulation.
|
|
||||||
// Then, of course, log to linear.
|
|
||||||
int32_t getsample(); |
|
||||||
|
|
||||||
void keydown(bool down); |
|
||||||
static int scaleoutlevel(int outlevel); |
|
||||||
void getPosition(char *step); |
|
||||||
|
|
||||||
static void init_sr(double sample_rate); |
|
||||||
void transfer(Env &src); |
|
||||||
|
|
||||||
private: |
|
||||||
|
|
||||||
// PG: This code is normalized to 44100, need to put a multiplier
|
|
||||||
// if we are not using 44100.
|
|
||||||
static uint32_t sr_multiplier; |
|
||||||
|
|
||||||
int rates_[4]; |
|
||||||
int levels_[4]; |
|
||||||
int outlevel_; |
|
||||||
int rate_scaling_; |
|
||||||
// Level is stored so that 2^24 is one doubling, ie 16 more bits than
|
|
||||||
// the DX7 itself (fraction is stored in level rather than separate
|
|
||||||
// counter)
|
|
||||||
int32_t level_; |
|
||||||
int targetlevel_; |
|
||||||
bool rising_; |
|
||||||
int ix_; |
|
||||||
int inc_; |
|
||||||
#ifdef ACCURATE_ENVELOPE |
|
||||||
int staticcount_; |
|
||||||
#endif |
|
||||||
|
|
||||||
bool down_; |
|
||||||
|
|
||||||
void advance(int newix); |
|
||||||
}; |
|
||||||
|
|
||||||
#endif // __ENV_H
|
|
||||||
|
|
@ -1,72 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES |
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "exp2.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
|
|
||||||
#ifdef _MSC_VER |
|
||||||
#define exp2(arg) pow(2.0, arg) |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int32_t exp2tab[EXP2_N_SAMPLES << 1]; |
|
||||||
|
|
||||||
void Exp2::init() { |
|
||||||
double inc = exp2(1.0 / EXP2_N_SAMPLES); |
|
||||||
double y = 1 << 30; |
|
||||||
for (int i = 0; i < EXP2_N_SAMPLES; i++) { |
|
||||||
exp2tab[(i << 1) + 1] = (int32_t)floor(y + 0.5); |
|
||||||
y *= inc; |
|
||||||
} |
|
||||||
for (int i = 0; i < EXP2_N_SAMPLES - 1; i++) { |
|
||||||
exp2tab[i << 1] = exp2tab[(i << 1) + 3] - exp2tab[(i << 1) + 1]; |
|
||||||
} |
|
||||||
exp2tab[(EXP2_N_SAMPLES << 1) - 2] = (1U << 31) - exp2tab[(EXP2_N_SAMPLES << 1) - 1]; |
|
||||||
} |
|
||||||
|
|
||||||
int32_t tanhtab[TANH_N_SAMPLES << 1]; |
|
||||||
|
|
||||||
static double dtanh(double y) { |
|
||||||
return 1 - y * y; |
|
||||||
} |
|
||||||
|
|
||||||
void Tanh::init() { |
|
||||||
double step = 4.0 / TANH_N_SAMPLES; |
|
||||||
double y = 0; |
|
||||||
for (int i = 0; i < TANH_N_SAMPLES; i++) { |
|
||||||
tanhtab[(i << 1) + 1] = (1 << 24) * y + 0.5; |
|
||||||
//printf("%d\n", tanhtab[(i << 1) + 1]);
|
|
||||||
// Use a basic 4th order Runge-Kutte to compute tanh from its
|
|
||||||
// differential equation.
|
|
||||||
double k1 = dtanh(y); |
|
||||||
double k2 = dtanh(y + 0.5 * step * k1); |
|
||||||
double k3 = dtanh(y + 0.5 * step * k2); |
|
||||||
double k4 = dtanh(y + step * k3); |
|
||||||
double dy = (step / 6) * (k1 + k4 + 2 * (k2 + k3)); |
|
||||||
y += dy; |
|
||||||
} |
|
||||||
for (int i = 0; i < TANH_N_SAMPLES - 1; i++) { |
|
||||||
tanhtab[i << 1] = tanhtab[(i << 1) + 3] - tanhtab[(i << 1) + 1]; |
|
||||||
} |
|
||||||
int32_t lasty = (1 << 24) * y + 0.5; |
|
||||||
tanhtab[(TANH_N_SAMPLES << 1) - 2] = lasty - tanhtab[(TANH_N_SAMPLES << 1) - 1]; |
|
||||||
} |
|
@ -1,80 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
class Exp2 { |
|
||||||
public: |
|
||||||
Exp2(); |
|
||||||
|
|
||||||
static void init(); |
|
||||||
|
|
||||||
// Q24 in, Q24 out
|
|
||||||
static int32_t lookup(int32_t x); |
|
||||||
}; |
|
||||||
|
|
||||||
#define EXP2_LG_N_SAMPLES 10 |
|
||||||
#define EXP2_N_SAMPLES (1 << EXP2_LG_N_SAMPLES) |
|
||||||
|
|
||||||
#define EXP2_INLINE |
|
||||||
|
|
||||||
extern int32_t exp2tab[EXP2_N_SAMPLES << 1]; |
|
||||||
|
|
||||||
#ifdef EXP2_INLINE |
|
||||||
inline |
|
||||||
int32_t Exp2::lookup(int32_t x) { |
|
||||||
const int SHIFT = 24 - EXP2_LG_N_SAMPLES; |
|
||||||
int lowbits = x & ((1 << SHIFT) - 1); |
|
||||||
int x_int = (x >> (SHIFT - 1)) & ((EXP2_N_SAMPLES - 1) << 1); |
|
||||||
int dy = exp2tab[x_int]; |
|
||||||
int y0 = exp2tab[x_int + 1]; |
|
||||||
|
|
||||||
int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); |
|
||||||
return y >> (6 - (x >> 24)); |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
class Tanh { |
|
||||||
public: |
|
||||||
static void init(); |
|
||||||
|
|
||||||
// Q24 in, Q24 out
|
|
||||||
static int32_t lookup(int32_t x); |
|
||||||
}; |
|
||||||
|
|
||||||
#define TANH_LG_N_SAMPLES 10 |
|
||||||
#define TANH_N_SAMPLES (1 << TANH_LG_N_SAMPLES) |
|
||||||
|
|
||||||
extern int32_t tanhtab[TANH_N_SAMPLES << 1]; |
|
||||||
|
|
||||||
inline |
|
||||||
int32_t Tanh::lookup(int32_t x) { |
|
||||||
int32_t signum = x >> 31; |
|
||||||
x ^= signum; |
|
||||||
if (x >= (4 << 24)) { |
|
||||||
if (x >= (17 << 23)) { |
|
||||||
return signum ^ (1 << 24); |
|
||||||
} |
|
||||||
int32_t sx = ((int64_t)-48408812 * (int64_t)x) >> 24; |
|
||||||
return signum ^ ((1 << 24) - 2 * Exp2::lookup(sx)); |
|
||||||
} else { |
|
||||||
const int SHIFT = 26 - TANH_LG_N_SAMPLES; |
|
||||||
int lowbits = x & ((1 << SHIFT) - 1); |
|
||||||
int x_int = (x >> (SHIFT - 1)) & ((TANH_N_SAMPLES - 1) << 1); |
|
||||||
int dy = tanhtab[x_int]; |
|
||||||
int y0 = tanhtab[x_int + 1]; |
|
||||||
int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); |
|
||||||
return y ^ signum; |
|
||||||
} |
|
||||||
} |
|
@ -1,135 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifdef VERBOSE |
|
||||||
#include <iostream> |
|
||||||
#endif |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "exp2.h" |
|
||||||
#include "fm_op_kernel.h" |
|
||||||
#include "fm_core.h" |
|
||||||
|
|
||||||
|
|
||||||
//using namespace std;
|
|
||||||
|
|
||||||
const FmAlgorithm FmCore::algorithms[32] = { |
|
||||||
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
|
|
||||||
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
|
|
||||||
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
|
|
||||||
{ { 0xc1, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
|
|
||||||
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
|
|
||||||
{ { 0xc1, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
|
|
||||||
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
|
|
||||||
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
|
|
||||||
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
|
|
||||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
|
|
||||||
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
|
|
||||||
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
|
|
||||||
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
|
|
||||||
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
|
|
||||||
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
|
|
||||||
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
|
|
||||||
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
|
|
||||||
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
|
|
||||||
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
|
|
||||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
|
|
||||||
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
|
|
||||||
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
|
|
||||||
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
|
|
||||||
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
|
|
||||||
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
|
|
||||||
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
|
|
||||||
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
|
|
||||||
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
|
|
||||||
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
|
|
||||||
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
|
|
||||||
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
|
|
||||||
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
|
|
||||||
}; |
|
||||||
|
|
||||||
int n_out(const FmAlgorithm &alg) { |
|
||||||
int count = 0; |
|
||||||
for (int i = 0; i < 6; i++) { |
|
||||||
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++; |
|
||||||
} |
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
void FmCore::dump() { |
|
||||||
#ifdef VERBOSE |
|
||||||
for (int i = 0; i < 32; i++) { |
|
||||||
cout << (i + 1) << ":"; |
|
||||||
const FmAlgorithm &alg = algorithms[i]; |
|
||||||
for (int j = 0; j < 6; j++) { |
|
||||||
int flags = alg.ops[j]; |
|
||||||
cout << " "; |
|
||||||
if (flags & FB_IN) cout << "["; |
|
||||||
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->"; |
|
||||||
cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0"); |
|
||||||
if (flags & OUT_BUS_ADD) cout << "+"; |
|
||||||
//cout << alg.ops[j].in << "->" << alg.ops[j].out;
|
|
||||||
if (flags & FB_OUT) cout << "]"; |
|
||||||
} |
|
||||||
cout << " " << n_out(alg); |
|
||||||
cout << endl; |
|
||||||
} |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
void FmCore::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift) { |
|
||||||
const int kLevelThresh = 1120; |
|
||||||
const FmAlgorithm alg = algorithms[algorithm]; |
|
||||||
bool has_contents[3] = { true, false, false }; |
|
||||||
for (int op = 0; op < 6; op++) { |
|
||||||
int flags = alg.ops[op]; |
|
||||||
bool add = (flags & OUT_BUS_ADD) != 0; |
|
||||||
FmOpParams ¶m = params[op]; |
|
||||||
int inbus = (flags >> 4) & 3; |
|
||||||
int outbus = flags & 3; |
|
||||||
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); |
|
||||||
int32_t gain1 = param.gain_out; |
|
||||||
int32_t gain2 = Exp2::lookup(param.level_in - (14 * (1 << 24))); |
|
||||||
param.gain_out = gain2; |
|
||||||
|
|
||||||
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) { |
|
||||||
if (!has_contents[outbus]) { |
|
||||||
add = false; |
|
||||||
} |
|
||||||
if (inbus == 0 || !has_contents[inbus]) { |
|
||||||
// todo: more than one op in a feedback loop
|
|
||||||
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) { |
|
||||||
// cout << op << " fb " << inbus << outbus << add << endl;
|
|
||||||
FmOpKernel::compute_fb(outptr, param.phase, param.freq, |
|
||||||
gain1, gain2, |
|
||||||
fb_buf, feedback_shift, add); |
|
||||||
} else { |
|
||||||
// cout << op << " pure " << inbus << outbus << add << endl;
|
|
||||||
FmOpKernel::compute_pure(outptr, param.phase, param.freq, |
|
||||||
gain1, gain2, add); |
|
||||||
} |
|
||||||
} else { |
|
||||||
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
|
|
||||||
FmOpKernel::compute(outptr, buf_[inbus - 1].get(), |
|
||||||
param.phase, param.freq, gain1, gain2, add); |
|
||||||
} |
|
||||||
has_contents[outbus] = true; |
|
||||||
} else if (!add) { |
|
||||||
has_contents[outbus] = false; |
|
||||||
} |
|
||||||
param.phase += param.freq << LG_N; |
|
||||||
} |
|
||||||
} |
|
@ -1,57 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef __FM_CORE_H |
|
||||||
#define __FM_CORE_H |
|
||||||
|
|
||||||
#include "aligned_buf.h" |
|
||||||
#include "fm_op_kernel.h" |
|
||||||
#include "synth.h" |
|
||||||
#include "controllers.h" |
|
||||||
|
|
||||||
|
|
||||||
class FmOperatorInfo { |
|
||||||
public: |
|
||||||
int in; |
|
||||||
int out; |
|
||||||
}; |
|
||||||
|
|
||||||
enum FmOperatorFlags { |
|
||||||
OUT_BUS_ONE = 1 << 0, |
|
||||||
OUT_BUS_TWO = 1 << 1, |
|
||||||
OUT_BUS_ADD = 1 << 2, |
|
||||||
IN_BUS_ONE = 1 << 4, |
|
||||||
IN_BUS_TWO = 1 << 5, |
|
||||||
FB_IN = 1 << 6, |
|
||||||
FB_OUT = 1 << 7 |
|
||||||
}; |
|
||||||
|
|
||||||
class FmAlgorithm { |
|
||||||
public: |
|
||||||
int ops[6]; |
|
||||||
}; |
|
||||||
|
|
||||||
class FmCore { |
|
||||||
public: |
|
||||||
virtual ~FmCore() {}; |
|
||||||
static void dump(); |
|
||||||
virtual void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_gain); |
|
||||||
protected: |
|
||||||
AlignedBuf<int32_t, N>buf_[2]; |
|
||||||
const static FmAlgorithm algorithms[32]; |
|
||||||
}; |
|
||||||
|
|
||||||
#endif // __FM_CORE_H
|
|
@ -1,283 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
#include <cstdlib> |
|
||||||
|
|
||||||
#ifdef HAVE_NEON |
|
||||||
#include <cpu-features.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "sin.h" |
|
||||||
#include "fm_op_kernel.h" |
|
||||||
|
|
||||||
#ifdef HAVE_NEONx |
|
||||||
static bool hasNeon() { |
|
||||||
return true; |
|
||||||
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; |
|
||||||
} |
|
||||||
|
|
||||||
extern "C" |
|
||||||
void neon_fm_kernel(const int *in, const int *busin, int *out, int count, |
|
||||||
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); |
|
||||||
|
|
||||||
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; |
|
||||||
|
|
||||||
#else |
|
||||||
static bool hasNeon() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
void FmOpKernel::compute(int32_t *output, const int32_t *input, |
|
||||||
int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
if (hasNeon()) { |
|
||||||
#ifdef HAVE_NEON |
|
||||||
neon_fm_kernel(input, add ? output : zeros, output, N, |
|
||||||
phase0, freq, gain, dgain); |
|
||||||
#endif |
|
||||||
} else { |
|
||||||
if (add) { |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
int32_t y = Sin::lookup(phase + input[i]); |
|
||||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
|
||||||
output[i] += y1; |
|
||||||
phase += freq; |
|
||||||
} |
|
||||||
} else { |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
int32_t y = Sin::lookup(phase + input[i]); |
|
||||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
|
||||||
output[i] = y1; |
|
||||||
phase += freq; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
if (hasNeon()) { |
|
||||||
#ifdef HAVE_NEON |
|
||||||
neon_fm_kernel(zeros, add ? output : zeros, output, N, |
|
||||||
phase0, freq, gain, dgain); |
|
||||||
#endif |
|
||||||
} else { |
|
||||||
if (add) { |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
int32_t y = Sin::lookup(phase); |
|
||||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
|
||||||
output[i] += y1; |
|
||||||
phase += freq; |
|
||||||
} |
|
||||||
} else { |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
int32_t y = Sin::lookup(phase); |
|
||||||
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
|
|
||||||
output[i] = y1; |
|
||||||
phase += freq; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#define noDOUBLE_ACCURACY |
|
||||||
#define HIGH_ACCURACY |
|
||||||
|
|
||||||
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, |
|
||||||
int32_t *fb_buf, int fb_shift, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
int32_t y0 = fb_buf[0]; |
|
||||||
int32_t y = fb_buf[1]; |
|
||||||
if (add) { |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
|
||||||
y0 = y; |
|
||||||
y = Sin::lookup(phase + scaled_fb); |
|
||||||
y = ((int64_t)y * (int64_t)gain) >> 24; |
|
||||||
output[i] += y; |
|
||||||
phase += freq; |
|
||||||
} |
|
||||||
} else { |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
|
||||||
y0 = y; |
|
||||||
y = Sin::lookup(phase + scaled_fb); |
|
||||||
y = ((int64_t)y * (int64_t)gain) >> 24; |
|
||||||
output[i] = y; |
|
||||||
phase += freq; |
|
||||||
} |
|
||||||
} |
|
||||||
fb_buf[0] = y0; |
|
||||||
fb_buf[1] = y; |
|
||||||
} |
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Experimental sine wave generators below
|
|
||||||
#if 0 |
|
||||||
// Results: accuracy 64.3 mean, 170 worst case
|
|
||||||
// high accuracy: 5.0 mean, 49 worst case
|
|
||||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
#ifdef HIGH_ACCURACY |
|
||||||
int32_t u = Sin::compute10(phase << 6); |
|
||||||
u = ((int64_t)u * gain) >> 30; |
|
||||||
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
|
|
||||||
v = ((int64_t)v * gain) >> 30; |
|
||||||
int32_t s = Sin::compute10(freq << 6); |
|
||||||
int32_t c = Sin::compute10((freq << 6) + (1 << 28)); |
|
||||||
#else |
|
||||||
int32_t u = Sin::compute(phase); |
|
||||||
u = ((int64_t)u * gain) >> 24; |
|
||||||
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
|
|
||||||
v = ((int64_t)v * gain) >> 24; |
|
||||||
int32_t s = Sin::compute(freq) << 6; |
|
||||||
int32_t c = Sin::compute(freq + (1 << 22)) << 6; |
|
||||||
#endif |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
output[i] = u; |
|
||||||
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; |
|
||||||
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; |
|
||||||
v = t; |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#if 0 |
|
||||||
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
|
|
||||||
// for freq < 0.25, 275.2 mean, 716 worst
|
|
||||||
// high accuracy: 57.4 mean, 7559 worst
|
|
||||||
// freq < 0.25: 17.9 mean, 78 worst
|
|
||||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
#ifdef HIGH_ACCURACY |
|
||||||
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); |
|
||||||
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); |
|
||||||
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); |
|
||||||
#else |
|
||||||
int32_t u = Sin::compute(phase); |
|
||||||
u = ((int64_t)u * gain) >> 24; |
|
||||||
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
|
||||||
v = ((int64_t)v * gain) >> 24; |
|
||||||
int32_t a = Sin::compute(freq >> 1) << 1; |
|
||||||
#endif |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
output[i] = u; |
|
||||||
v -= ((int64_t)a * (int64_t)u) >> 24; |
|
||||||
u += ((int64_t)a * (int64_t)v) >> 24; |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#if 0 |
|
||||||
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
|
|
||||||
// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0)
|
|
||||||
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
|
|
||||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
#ifdef DOUBLE_ACCURACY |
|
||||||
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); |
|
||||||
double a_d = sin(freq * (M_PI / (1 << 24))); |
|
||||||
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * |
|
||||||
(M_PI / (1 << 23))) + 0.5); |
|
||||||
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); |
|
||||||
#else |
|
||||||
#ifdef HIGH_ACCURACY |
|
||||||
int32_t u = Sin::compute10(phase << 6); |
|
||||||
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); |
|
||||||
int32_t a = Sin::compute10(freq << 5); |
|
||||||
v = ((int64_t)v * (int64_t)a) >> 29; |
|
||||||
int32_t aa = ((int64_t)a * (int64_t)a) >> 29; |
|
||||||
#else |
|
||||||
int32_t u = Sin::compute(phase) << 6; |
|
||||||
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
|
||||||
int32_t a = Sin::compute(freq >> 1); |
|
||||||
v = ((int64_t)v * (int64_t)a) >> 17; |
|
||||||
int32_t aa = ((int64_t)a * (int64_t)a) >> 17; |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
|
|
||||||
if (aa < 0) aa = (1 << 31) - 1; |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
|
||||||
v -= ((int64_t)aa * (int64_t)u) >> 29; |
|
||||||
u += v; |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#if 0 |
|
||||||
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
|
|
||||||
// high accuracy 2.9 mean, 143 worst
|
|
||||||
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add) { |
|
||||||
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
|
||||||
int32_t gain = gain1; |
|
||||||
int32_t phase = phase0; |
|
||||||
#ifdef HIGH_ACCURACY |
|
||||||
int32_t u = Sin::compute10(phase << 6); |
|
||||||
int32_t lastu = Sin::compute10((phase - freq) << 6); |
|
||||||
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; |
|
||||||
#else |
|
||||||
int32_t u = Sin::compute(phase) << 6; |
|
||||||
int32_t lastu = Sin::compute(phase - freq) << 6; |
|
||||||
int32_t a = Sin::compute(freq + (1 << 22)) << 7; |
|
||||||
#endif |
|
||||||
if (a < 0 && freq < 256) a = (1 << 31) - 1; |
|
||||||
if (a > 0 && freq > 0x7fff00) a = -(1 << 31); |
|
||||||
for (int i = 0; i < N; i++) { |
|
||||||
gain += dgain; |
|
||||||
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
|
||||||
//output[i] = u;
|
|
||||||
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; |
|
||||||
lastu = u; |
|
||||||
u = newu; |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
@ -1,47 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef __FM_OP_KERNEL_H |
|
||||||
#define __FM_OP_KERNEL_H |
|
||||||
|
|
||||||
struct FmOpParams { |
|
||||||
int32_t level_in; // value to be computed (from level to gain[0])
|
|
||||||
int32_t gain_out; // computed value (gain[1] to gain[0])
|
|
||||||
int32_t freq; |
|
||||||
int32_t phase; |
|
||||||
}; |
|
||||||
|
|
||||||
class FmOpKernel { |
|
||||||
public: |
|
||||||
// gain1 and gain2 represent linear step: gain for sample i is
|
|
||||||
// gain1 + (1 + i) / 64 * (gain2 - gain1)
|
|
||||||
|
|
||||||
// This is the basic FM operator. No feedback.
|
|
||||||
static void compute(int32_t *output, const int32_t *input, |
|
||||||
int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add); |
|
||||||
|
|
||||||
// This is a sine generator, no feedback.
|
|
||||||
static void compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, bool add); |
|
||||||
|
|
||||||
// One op with feedback, no add.
|
|
||||||
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
|
||||||
int32_t gain1, int32_t gain2, |
|
||||||
int32_t *fb_buf, int fb_gain, bool add); |
|
||||||
}; |
|
||||||
|
|
||||||
#endif |
|
@ -1,55 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
// Resolve frequency signal (1.0 in Q24 format = 1 octave) to phase delta.
|
|
||||||
|
|
||||||
// The LUT is just a global, and we'll need the init function to be called before
|
|
||||||
// use.
|
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
#include "freqlut.h" |
|
||||||
|
|
||||||
#define LG_N_SAMPLES 10 |
|
||||||
#define N_SAMPLES (1 << LG_N_SAMPLES) |
|
||||||
#define SAMPLE_SHIFT (24 - LG_N_SAMPLES) |
|
||||||
|
|
||||||
#define MAX_LOGFREQ_INT 20 |
|
||||||
|
|
||||||
int32_t lut[N_SAMPLES + 1]; |
|
||||||
|
|
||||||
void Freqlut::init(double sample_rate) { |
|
||||||
double y = (1LL << (24 + MAX_LOGFREQ_INT)) / sample_rate; |
|
||||||
double inc = pow(2, 1.0 / N_SAMPLES); |
|
||||||
for (int i = 0; i < N_SAMPLES + 1; i++) { |
|
||||||
lut[i] = (int32_t)floor(y + 0.5); |
|
||||||
y *= inc; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Note: if logfreq is more than 20.0, the results will be inaccurate. However,
|
|
||||||
// that will be many times the Nyquist rate.
|
|
||||||
int32_t Freqlut::lookup(int32_t logfreq) { |
|
||||||
int ix = (logfreq & 0xffffff) >> SAMPLE_SHIFT; |
|
||||||
|
|
||||||
int32_t y0 = lut[ix]; |
|
||||||
int32_t y1 = lut[ix + 1]; |
|
||||||
int lowbits = logfreq & ((1 << SAMPLE_SHIFT) - 1); |
|
||||||
int32_t y = y0 + ((((int64_t)(y1 - y0) * (int64_t)lowbits)) >> SAMPLE_SHIFT); |
|
||||||
int hibits = logfreq >> 24; |
|
||||||
return y >> (MAX_LOGFREQ_INT - hibits); |
|
||||||
} |
|
@ -1,21 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
class Freqlut { |
|
||||||
public: |
|
||||||
static void init(double sample_rate); |
|
||||||
static int32_t lookup(int32_t logfreq); |
|
||||||
}; |
|
@ -1,97 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
// Low frequency oscillator, compatible with DX7
|
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
|
|
||||||
#include "sin.h" |
|
||||||
#include "lfo.h" |
|
||||||
|
|
||||||
uint32_t Lfo::unit_; |
|
||||||
|
|
||||||
void Lfo::init(double sample_rate) { |
|
||||||
// constant is 1 << 32 / 15.5s / 11
|
|
||||||
Lfo::unit_ = (int32_t)(N * 25190424 / sample_rate + 0.5); |
|
||||||
} |
|
||||||
|
|
||||||
void Lfo::reset(const uint8_t params[6]) { |
|
||||||
int rate = params[0]; // 0..99
|
|
||||||
int sr = rate == 0 ? 1 : (165 * rate) >> 6; |
|
||||||
sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4)); |
|
||||||
delta_ = unit_ * sr; |
|
||||||
int a = 99 - params[1]; // LFO delay
|
|
||||||
if (a == 99) { |
|
||||||
delayinc_ = ~0u; |
|
||||||
delayinc2_ = ~0u; |
|
||||||
} else { |
|
||||||
a = (16 + (a & 15)) << (1 + (a >> 4)); |
|
||||||
delayinc_ = unit_ * a; |
|
||||||
a &= 0xff80; |
|
||||||
a = max(0x80, a); |
|
||||||
delayinc2_ = unit_ * a; |
|
||||||
} |
|
||||||
waveform_ = params[5]; |
|
||||||
sync_ = params[4] != 0; |
|
||||||
} |
|
||||||
|
|
||||||
int32_t Lfo::getsample() { |
|
||||||
phase_ += delta_; |
|
||||||
int32_t x; |
|
||||||
switch (waveform_) { |
|
||||||
case 0: // triangle
|
|
||||||
x = phase_ >> 7; |
|
||||||
x ^= -(phase_ >> 31); |
|
||||||
x &= (1 << 24) - 1; |
|
||||||
return x; |
|
||||||
case 1: // sawtooth down
|
|
||||||
return (~phase_ ^ (1U << 31)) >> 8; |
|
||||||
case 2: // sawtooth up
|
|
||||||
return (phase_ ^ (1U << 31)) >> 8; |
|
||||||
case 3: // square
|
|
||||||
return ((~phase_) >> 7) & (1 << 24); |
|
||||||
case 4: // sine
|
|
||||||
return (1 << 23) + (Sin::lookup(phase_ >> 8) >> 1); |
|
||||||
case 5: // s&h
|
|
||||||
if (phase_ < delta_) { |
|
||||||
randstate_ = (randstate_ * 179 + 17) & 0xff; |
|
||||||
} |
|
||||||
x = randstate_ ^ 0x80; |
|
||||||
return (x + 1) << 16; |
|
||||||
} |
|
||||||
return 1 << 23; |
|
||||||
} |
|
||||||
|
|
||||||
int32_t Lfo::getdelay() { |
|
||||||
uint32_t delta = delaystate_ < (1U << 31) ? delayinc_ : delayinc2_; |
|
||||||
uint64_t d = ((uint64_t)delaystate_) + delta; |
|
||||||
if (d > ~0u) { |
|
||||||
return 1 << 24; |
|
||||||
} |
|
||||||
delaystate_ = d; |
|
||||||
if (d < (1U << 31)) { |
|
||||||
return 0; |
|
||||||
} else { |
|
||||||
return (d >> 7) & ((1 << 24) - 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void Lfo::keydown() { |
|
||||||
if (sync_) { |
|
||||||
phase_ = (1U << 31) - 1; |
|
||||||
} |
|
||||||
delaystate_ = 0; |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
// Low frequency oscillator, compatible with DX7
|
|
||||||
|
|
||||||
class Lfo { |
|
||||||
public: |
|
||||||
static void init(double sample_rate); |
|
||||||
void reset(const uint8_t params[6]); |
|
||||||
|
|
||||||
// result is 0..1 in Q24
|
|
||||||
int32_t getsample(); |
|
||||||
|
|
||||||
// result is 0..1 in Q24
|
|
||||||
int32_t getdelay(); |
|
||||||
|
|
||||||
void keydown(); |
|
||||||
private: |
|
||||||
static uint32_t unit_; |
|
||||||
|
|
||||||
uint32_t phase_; // Q32
|
|
||||||
uint32_t delta_; |
|
||||||
uint8_t waveform_; |
|
||||||
uint8_t randstate_; |
|
||||||
bool sync_; |
|
||||||
|
|
||||||
uint32_t delaystate_; |
|
||||||
uint32_t delayinc_; |
|
||||||
uint32_t delayinc2_; |
|
||||||
}; |
|
@ -1,31 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef SYNTH_MODULE_H |
|
||||||
#define SYNTH_MODULE_H |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
class Module { |
|
||||||
public: |
|
||||||
static const int lg_n = 6; |
|
||||||
static const int n = 1 << lg_n; |
|
||||||
virtual void process(const int32_t **inbufs, const int32_t *control_in, |
|
||||||
const int32_t *control_last, int32_t **outbufs) = 0; |
|
||||||
}; |
|
||||||
|
|
||||||
#endif // SYNTH_MODULE_H
|
|
||||||
|
|
@ -1,95 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "pitchenv.h" |
|
||||||
|
|
||||||
int PitchEnv::unit_; |
|
||||||
|
|
||||||
void PitchEnv::init(double sample_rate) { |
|
||||||
unit_ = N * (1 << 24) / (21.3 * sample_rate) + 0.5; |
|
||||||
} |
|
||||||
|
|
||||||
const uint8_t pitchenv_rate[] = { |
|
||||||
1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, |
|
||||||
12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, |
|
||||||
25, 26, 27, 28, 30, 31, 33, 34, 36, 37, 38, 39, 41, 42, 44, 46, 47, |
|
||||||
49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 79, 82, |
|
||||||
85, 88, 91, 94, 98, 102, 106, 110, 115, 120, 125, 130, 135, 141, 147, |
|
||||||
153, 159, 165, 171, 178, 185, 193, 202, 211, 232, 243, 254, 255 |
|
||||||
}; |
|
||||||
|
|
||||||
const int8_t pitchenv_tab[] = { |
|
||||||
-128, -116, -104, -95, -85, -76, -68, -61, -56, -52, -49, -46, -43, |
|
||||||
-41, -39, -37, -35, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, |
|
||||||
-23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, |
|
||||||
-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
|
||||||
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, |
|
||||||
28, 29, 30, 31, 32, 33, 34, 35, 38, 40, 43, 46, 49, 53, 58, 65, 73, |
|
||||||
82, 92, 103, 115, 127 |
|
||||||
}; |
|
||||||
|
|
||||||
void PitchEnv::set(const int r[4], const int l[4]) { |
|
||||||
for (int i = 0; i < 4; i++) { |
|
||||||
rates_[i] = r[i]; |
|
||||||
levels_[i] = l[i]; |
|
||||||
} |
|
||||||
level_ = pitchenv_tab[l[3]] << 19; |
|
||||||
down_ = true; |
|
||||||
advance(0); |
|
||||||
} |
|
||||||
|
|
||||||
int32_t PitchEnv::getsample() { |
|
||||||
if (ix_ < 3 || ((ix_ < 4) && !down_)) { |
|
||||||
if (rising_) { |
|
||||||
level_ += inc_; |
|
||||||
if (level_ >= targetlevel_) { |
|
||||||
level_ = targetlevel_; |
|
||||||
advance(ix_ + 1); |
|
||||||
} |
|
||||||
} else { // !rising
|
|
||||||
level_ -= inc_; |
|
||||||
if (level_ <= targetlevel_) { |
|
||||||
level_ = targetlevel_; |
|
||||||
advance(ix_ + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return level_; |
|
||||||
} |
|
||||||
|
|
||||||
void PitchEnv::keydown(bool d) { |
|
||||||
if (down_ != d) { |
|
||||||
down_ = d; |
|
||||||
advance(d ? 0 : 3); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void PitchEnv::advance(int newix) { |
|
||||||
ix_ = newix; |
|
||||||
if (ix_ < 4) { |
|
||||||
int newlevel = levels_[ix_]; |
|
||||||
targetlevel_ = pitchenv_tab[newlevel] << 19; |
|
||||||
rising_ = (targetlevel_ > level_); |
|
||||||
inc_ = pitchenv_rate[rates_[ix_]] * unit_; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void PitchEnv::getPosition(char *step) { |
|
||||||
*step = ix_; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
@ -1,53 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2013 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef __PITCHENV_H |
|
||||||
#define __PITCHENV_H |
|
||||||
|
|
||||||
// Computation of the DX7 pitch envelope
|
|
||||||
|
|
||||||
class PitchEnv { |
|
||||||
public: |
|
||||||
static void init(double sample_rate); |
|
||||||
|
|
||||||
// The rates and levels arrays are calibrated to match the Dx7 parameters
|
|
||||||
// (ie, value 0..99).
|
|
||||||
void set(const int rates[4], const int levels[4]); |
|
||||||
|
|
||||||
// Result is in Q24/octave
|
|
||||||
int32_t getsample(); |
|
||||||
void keydown(bool down); |
|
||||||
void getPosition(char *step); |
|
||||||
private: |
|
||||||
static int unit_; |
|
||||||
int rates_[4]; |
|
||||||
int levels_[4]; |
|
||||||
int32_t level_; |
|
||||||
int targetlevel_; |
|
||||||
bool rising_; |
|
||||||
int ix_; |
|
||||||
int inc_; |
|
||||||
|
|
||||||
bool down_; |
|
||||||
|
|
||||||
void advance(int newix); |
|
||||||
}; |
|
||||||
|
|
||||||
extern const uint8_t pitchenv_rate[]; |
|
||||||
extern const int8_t pitchenv_tab[]; |
|
||||||
|
|
||||||
#endif // __PITCHENV_H
|
|
||||||
|
|
@ -1,143 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES |
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include "sin.h" |
|
||||||
|
|
||||||
#define R (1 << 29) |
|
||||||
#define M_PI 3.1415926 |
|
||||||
|
|
||||||
#ifdef SIN_DELTA |
|
||||||
int32_t sintab[SIN_N_SAMPLES << 1]; |
|
||||||
#else |
|
||||||
int32_t sintab[SIN_N_SAMPLES + 1]; |
|
||||||
#endif |
|
||||||
|
|
||||||
void Sin::init() { |
|
||||||
double dphase = 2 * M_PI / SIN_N_SAMPLES; |
|
||||||
int32_t c = (int32_t)floor(cos(dphase) * (1 << 30) + 0.5); |
|
||||||
int32_t s = (int32_t)floor(sin(dphase) * (1 << 30) + 0.5); |
|
||||||
int32_t u = 1 << 30; |
|
||||||
int32_t v = 0; |
|
||||||
for (int i = 0; i < SIN_N_SAMPLES / 2; i++) { |
|
||||||
#ifdef SIN_DELTA |
|
||||||
sintab[(i << 1) + 1] = (v + 32) >> 6; |
|
||||||
sintab[((i + SIN_N_SAMPLES / 2) << 1) + 1] = -((v + 32) >> 6); |
|
||||||
#else |
|
||||||
sintab[i] = (v + 32) >> 6; |
|
||||||
sintab[i + SIN_N_SAMPLES / 2] = -((v + 32) >> 6); |
|
||||||
#endif |
|
||||||
int32_t t = ((int64_t)u * (int64_t)s + (int64_t)v * (int64_t)c + R) >> 30; |
|
||||||
u = ((int64_t)u * (int64_t)c - (int64_t)v * (int64_t)s + R) >> 30; |
|
||||||
v = t; |
|
||||||
} |
|
||||||
#ifdef SIN_DELTA |
|
||||||
for (int i = 0; i < SIN_N_SAMPLES - 1; i++) { |
|
||||||
sintab[i << 1] = sintab[(i << 1) + 3] - sintab[(i << 1) + 1]; |
|
||||||
} |
|
||||||
sintab[(SIN_N_SAMPLES << 1) - 2] = -sintab[(SIN_N_SAMPLES << 1) - 1]; |
|
||||||
#else |
|
||||||
sintab[SIN_N_SAMPLES] = 0; |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
#ifndef SIN_INLINE |
|
||||||
int32_t Sin::lookup(int32_t phase) { |
|
||||||
const int SHIFT = 24 - SIN_LG_N_SAMPLES; |
|
||||||
int lowbits = phase & ((1 << SHIFT) - 1); |
|
||||||
#ifdef SIN_DELTA |
|
||||||
int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1); |
|
||||||
int dy = sintab[phase_int]; |
|
||||||
int y0 = sintab[phase_int + 1]; |
|
||||||
|
|
||||||
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); |
|
||||||
#else |
|
||||||
int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1); |
|
||||||
int y0 = sintab[phase_int]; |
|
||||||
int y1 = sintab[phase_int + 1]; |
|
||||||
|
|
||||||
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT); |
|
||||||
#endif |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
#if 0 |
|
||||||
// The following is an implementation designed not to use any lookup tables,
|
|
||||||
// based on the following implementation by Basile Graf:
|
|
||||||
// http://www.rossbencina.com/static/code/sinusoids/even_polynomial_sin_approximation.txt
|
|
||||||
|
|
||||||
#define C0 (1 << 24) |
|
||||||
#define C1 (331121857 >> 2) |
|
||||||
#define C2 (1084885537 >> 4) |
|
||||||
#define C3 (1310449902 >> 6) |
|
||||||
|
|
||||||
int32_t Sin::compute(int32_t phase) { |
|
||||||
int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22); |
|
||||||
int32_t x2 = ((int64_t)x * (int64_t)x) >> 22; |
|
||||||
int32_t x4 = ((int64_t)x2 * (int64_t)x2) >> 24; |
|
||||||
int32_t x6 = ((int64_t)x2 * (int64_t)x4) >> 24; |
|
||||||
int32_t y = C0 - |
|
||||||
(((int64_t)C1 * (int64_t)x2) >> 24) + |
|
||||||
(((int64_t)C2 * (int64_t)x4) >> 24) - |
|
||||||
(((int64_t)C3 * (int64_t)x6) >> 24); |
|
||||||
y ^= -((phase >> 23) & 1); |
|
||||||
return y; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#if 1 |
|
||||||
// coefficients are Chebyshev polynomial, computed by compute_cos_poly.py
|
|
||||||
#define C8_0 16777216 |
|
||||||
#define C8_2 -331168742 |
|
||||||
#define C8_4 1089453524 |
|
||||||
#define C8_6 -1430910663 |
|
||||||
#define C8_8 950108533 |
|
||||||
|
|
||||||
int32_t Sin::compute(int32_t phase) { |
|
||||||
int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22); |
|
||||||
int32_t x2 = ((int64_t)x * (int64_t)x) >> 16; |
|
||||||
int32_t y = (((((((((((((int64_t)C8_8 |
|
||||||
* (int64_t)x2) >> 32) + C8_6) |
|
||||||
* (int64_t)x2) >> 32) + C8_4) |
|
||||||
* (int64_t)x2) >> 32) + C8_2) |
|
||||||
* (int64_t)x2) >> 32) + C8_0); |
|
||||||
y ^= -((phase >> 23) & 1); |
|
||||||
return y; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#define C10_0 (1 << 30) |
|
||||||
#define C10_2 -1324675874 // scaled * 4
|
|
||||||
#define C10_4 1089501821 |
|
||||||
#define C10_6 -1433689867 |
|
||||||
#define C10_8 1009356886 |
|
||||||
#define C10_10 -421101352 |
|
||||||
int32_t Sin::compute10(int32_t phase) { |
|
||||||
int32_t x = (phase & ((1 << 29) - 1)) - (1 << 28); |
|
||||||
int32_t x2 = ((int64_t)x * (int64_t)x) >> 26; |
|
||||||
int32_t y = ((((((((((((((((int64_t)C10_10 |
|
||||||
* (int64_t)x2) >> 34) + C10_8) |
|
||||||
* (int64_t)x2) >> 34) + C10_6) |
|
||||||
* (int64_t)x2) >> 34) + C10_4) |
|
||||||
* (int64_t)x2) >> 32) + C10_2) |
|
||||||
* (int64_t)x2) >> 30) + C10_0); |
|
||||||
y ^= -((phase >> 29) & 1); |
|
||||||
return y; |
|
||||||
} |
|
@ -1,62 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
class Sin { |
|
||||||
public: |
|
||||||
Sin(); |
|
||||||
|
|
||||||
static void init(); |
|
||||||
static int32_t lookup(int32_t phase); |
|
||||||
static int32_t compute(int32_t phase); |
|
||||||
|
|
||||||
// A more accurate sine, both input and output Q30
|
|
||||||
static int32_t compute10(int32_t phase); |
|
||||||
}; |
|
||||||
|
|
||||||
#define SIN_LG_N_SAMPLES 10 |
|
||||||
#define SIN_N_SAMPLES (1 << SIN_LG_N_SAMPLES) |
|
||||||
|
|
||||||
#define SIN_INLINE |
|
||||||
|
|
||||||
// Use twice as much RAM for the LUT but avoid a little computation
|
|
||||||
#define SIN_DELTA |
|
||||||
|
|
||||||
#ifdef SIN_DELTA |
|
||||||
extern int32_t sintab[SIN_N_SAMPLES << 1]; |
|
||||||
#else |
|
||||||
extern int32_t sintab[SIN_N_SAMPLES + 1]; |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef SIN_INLINE |
|
||||||
inline |
|
||||||
int32_t Sin::lookup(int32_t phase) { |
|
||||||
const int SHIFT = 24 - SIN_LG_N_SAMPLES; |
|
||||||
int lowbits = phase & ((1 << SHIFT) - 1); |
|
||||||
#ifdef SIN_DELTA |
|
||||||
int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1); |
|
||||||
int dy = sintab[phase_int]; |
|
||||||
int y0 = sintab[phase_int + 1]; |
|
||||||
|
|
||||||
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); |
|
||||||
#else |
|
||||||
int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1); |
|
||||||
int y0 = sintab[phase_int]; |
|
||||||
int y1 = sintab[phase_int + 1]; |
|
||||||
|
|
||||||
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT); |
|
||||||
#endif |
|
||||||
} |
|
||||||
#endif |
|
@ -1,71 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc. |
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef __SYNTH_H |
|
||||||
#define __SYNTH_H |
|
||||||
|
|
||||||
// This IS not be present on MSVC.
|
|
||||||
// See http://stackoverflow.com/questions/126279/c99-stdint-h-header-and-ms-visual-studio
|
|
||||||
#include <stdint.h> |
|
||||||
#ifdef _MSC_VER |
|
||||||
typedef __int32 int32_t; |
|
||||||
typedef unsigned __int32 uint32_t; |
|
||||||
typedef __int16 SInt16; |
|
||||||
#endif |
|
||||||
|
|
||||||
const static int LG_N = 6; |
|
||||||
const static int N = (1 << LG_N); |
|
||||||
|
|
||||||
#if defined(__APPLE__) |
|
||||||
#include <libkern/OSAtomic.h> |
|
||||||
#define SynthMemoryBarrier() OSMemoryBarrier() |
|
||||||
#elif defined(__GNUC__) |
|
||||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) |
|
||||||
#define SynthMemoryBarrier() __sync_synchronize() |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
// #undef SynthMemoryBarrier()
|
|
||||||
|
|
||||||
#ifndef SynthMemoryBarrier |
|
||||||
// need to understand why this must be defined
|
|
||||||
// #warning Memory barrier is not enabled
|
|
||||||
#define SynthMemoryBarrier() |
|
||||||
#endif |
|
||||||
|
|
||||||
template<typename T> |
|
||||||
inline static T min(const T& a, const T& b) { |
|
||||||
return a < b ? a : b; |
|
||||||
} |
|
||||||
|
|
||||||
template<typename T> |
|
||||||
inline static T max(const T& a, const T& b) { |
|
||||||
return a > b ? a : b; |
|
||||||
} |
|
||||||
|
|
||||||
void dexed_trace(const char *source, const char *fmt, ...); |
|
||||||
|
|
||||||
#define QER(n,b) ( ((float)n)/(1<<b) ) |
|
||||||
//#ifndef TRACE
|
|
||||||
// #ifdef _MSC_VER
|
|
||||||
// #define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
|
|
||||||
// #else
|
|
||||||
// #define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
|
|
||||||
// #endif
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
#endif // __SYNTH_H
|
|
@ -1,79 +0,0 @@ |
|||||||
#include "tuning.h" |
|
||||||
|
|
||||||
#include <iostream> |
|
||||||
#include <iomanip> |
|
||||||
#include <fstream> |
|
||||||
#include <cstdlib> |
|
||||||
#include <math.h> |
|
||||||
#include <sstream> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
|
|
||||||
struct StandardTuning : public TuningState { |
|
||||||
StandardTuning() { |
|
||||||
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
|
|
||||||
const int step = (1 << 24) / 12; |
|
||||||
for( int mn = 0; mn < 128; ++mn ) |
|
||||||
{ |
|
||||||
auto res = base + step * mn; |
|
||||||
current_logfreq_table_[mn] = res; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
virtual int32_t midinote_to_logfreq(int midinote) override { |
|
||||||
return current_logfreq_table_[midinote]; |
|
||||||
} |
|
||||||
|
|
||||||
int current_logfreq_table_[128]; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
struct SCLAndKBMTuningState : public TuningState { |
|
||||||
virtual bool is_standard_tuning() override { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
virtual int32_t midinote_to_logfreq(int midinote) override { |
|
||||||
const int base = 50857777; |
|
||||||
const int step = ( 1 << 24 ); |
|
||||||
return tuning.logScaledFrequencyForMidiNote( midinote ) * step + base; |
|
||||||
} |
|
||||||
|
|
||||||
virtual int scale_length() { return tuning.scale.count; } |
|
||||||
virtual std::string display_tuning_str() { return "SCL KBM Tuning"; } |
|
||||||
|
|
||||||
virtual Tunings::Tuning &getTuning() override { return tuning; } |
|
||||||
|
|
||||||
Tunings::Tuning tuning; |
|
||||||
|
|
||||||
}; |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> createStandardTuning() |
|
||||||
{ |
|
||||||
return std::make_shared<StandardTuning>(); |
|
||||||
} |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> createTuningFromSCLData( const std::string &scl ) |
|
||||||
{ |
|
||||||
auto s = Tunings::parseSCLData(scl); |
|
||||||
auto res = std::make_shared<SCLAndKBMTuningState>(); |
|
||||||
res->tuning = Tunings::Tuning( s ); |
|
||||||
return res; |
|
||||||
} |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> createTuningFromKBMData( const std::string &kbm ) |
|
||||||
{ |
|
||||||
auto k = Tunings::parseKBMData(kbm); |
|
||||||
auto res = std::make_shared<SCLAndKBMTuningState>(); |
|
||||||
res->tuning = Tunings::Tuning( k ); |
|
||||||
return res; |
|
||||||
} |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> createTuningFromSCLAndKBMData( const std::string &sclData, const std::string &kbmData ) |
|
||||||
{ |
|
||||||
auto s = Tunings::parseSCLData(sclData); |
|
||||||
auto k = Tunings::parseKBMData(kbmData); |
|
||||||
auto res = std::make_shared<SCLAndKBMTuningState>(); |
|
||||||
res->tuning = Tunings::Tuning( s, k ); |
|
||||||
return res; |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
#ifndef __SYNTH_TUNING_H |
|
||||||
#define __SYNTH_TUNING_H |
|
||||||
|
|
||||||
#include "synth.h" |
|
||||||
#include <memory> |
|
||||||
#include <string> |
|
||||||
#include "Tunings.h" |
|
||||||
|
|
||||||
class TuningState { |
|
||||||
public: |
|
||||||
virtual ~TuningState() { } |
|
||||||
|
|
||||||
virtual int32_t midinote_to_logfreq(int midinote) = 0; |
|
||||||
virtual bool is_standard_tuning() { return true; } |
|
||||||
virtual int scale_length() { return 12; } |
|
||||||
virtual std::string display_tuning_str() { return "Standard Tuning"; } |
|
||||||
|
|
||||||
virtual Tunings::Tuning &getTuning() { |
|
||||||
static Tunings::Tuning t; |
|
||||||
return t; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> createStandardTuning(); |
|
||||||
|
|
||||||
std::shared_ptr<TuningState> createTuningFromSCLData( const std::string &sclData ); |
|
||||||
std::shared_ptr<TuningState> createTuningFromKBMData( const std::string &kbmData ); |
|
||||||
std::shared_ptr<TuningState> createTuningFromSCLAndKBMData( const std::string &sclData, const std::string &kbmData ); |
|
||||||
|
|
||||||
#endif |
|
Loading…
Reference in new issue