pull/4/head
Holger Wirtz 6 years ago
parent c1b5edced8
commit ab4a7de34d
  1. 30
      EngineMkI.cpp
  2. 2
      EngineMkI.h
  3. 3
      EngineOpl.cpp
  4. 4
      EngineOpl.h
  5. 36
      MicroDexed.ino
  6. 41
      controllers.h
  7. 36
      dexed.cpp
  8. 6
      dexed.h
  9. 931
      dexed.orig
  10. 52
      dx7note.cpp
  11. 4
      dx7note.h
  12. 188
      env.cc
  13. 10
      env.h
  14. 5
      fm_core.cpp
  15. 2
      fm_core.h
  16. 283
      fm_op_kernel.cc
  17. 6
      fm_op_kernel.cpp
  18. 1
      lfo.cpp
  19. 8
      synth.h
  20. 11
      trace.h

@ -21,6 +21,7 @@
*
*/
#include <Arduino.h>
#include "EngineMkI.h"
#define _USE_MATH_DEFINES
#include <cmath>
@ -29,22 +30,21 @@
#include "sin.h"
#include "exp2.h"
#undef min
#define min(a,b) ((a)<(b)?(a):(b))
#ifdef DEBUG
#include "time.h"
//#define MKIDEBUG
#endif
#ifdef _WIN32
#if _MSC_VER < 1800
double log2(double n) {
return log(n) / log(2.0);
}
double round(double n) {
return n < 0.0 ? ceil(n - 0.5) : floor(n + 0.5);
}
__declspec(align(16)) const int zeros[_N_] = {0};
#endif
__declspec(align(16)) const int zeros[N] = {0};
#else
const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0};
#endif
@ -77,6 +77,7 @@ static inline uint16_t sinLog(uint16_t phi) {
return sinLogTable[index ^ SINLOG_TABLEFILTER] | NEGATIVE_BIT;
}
}
EngineMkI::EngineMkI() {
float bitReso = SINLOG_TABLESIZE;
@ -95,32 +96,36 @@ EngineMkI::EngineMkI() {
char buffer[4096];
int pos = 0;
TRACE("****************************************");
for(int i=0;i<SINLOG_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinLogTable[i]);
if ( pos > 90 ) {
TRACE("SINLOGTABLE: %s" ,buffer);
buffer[0] = 0;
pos = 0;
}
}
TRACE("SINLOGTABLE: %s", buffer);
buffer[0] = 0;
pos = 0;
TRACE("----------------------------------------");
for(int i=0;i<SINEXP_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinExpTable[i]);
if ( pos > 90 ) {
TRACE("SINEXTTABLE: %s" ,buffer);
buffer[0] = 0;
pos = 0;
}
}
TRACE("SINEXTTABLE: %s", buffer);
TRACE("****************************************");
#endif
}
inline int32_t mkiSin(int32_t phase, uint16_t env) {
uint16_t expVal = sinLog(phase >> (22 - SINLOG_BITDEPTH)) + (env);
#ifdef MKIDEBUG
int16_t expValShow = expVal;
#endif
//int16_t expValShow = expVal;
const bool isSigned = expVal & NEGATIVE_BIT;
expVal &= ~NEGATIVE_BIT;
@ -135,6 +140,7 @@ inline int32_t mkiSin(int32_t phase, uint16_t env) {
if ( expValShow < 0 ) {
expValShow = (expValShow + 0x7FFF) * -1;
}
//TRACE(",%d,%d,%d,%d,%d,%d", phase >> (22 - SINLOG_BITDEPTH), env, expValShow, ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER, resultB4, result);
}
#endif
@ -287,7 +293,7 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01,
fb_buf[1] = y;
}
void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) {
void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int kLevelThresh = ENV_MAX-100;
FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
@ -322,14 +328,14 @@ void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32
switch ( algorithm ) {
// three operator feedback, process exception for ALGO 4
case 3 :
compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2),16));
compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16));
params[1].phase += params[1].freq << LG_N; // hack, we already processed op-5 - op-4
params[2].phase += params[2].freq << LG_N; // yuk yuk
op += 2; // ignore the 2 other operators
break;
// two operator feedback, process exception for ALGO 6
case 5 :
compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2),16));
compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16));
params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5
op++; // ignore next operator;
break;

@ -29,7 +29,7 @@ class EngineMkI : public FmCore {
public:
EngineMkI();
void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift);
void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift);
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2,
bool add);

@ -168,8 +168,7 @@ void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
}
void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift) {
void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,int32_t *fb_buf, int32_t feedback_shift) {
const int kLevelThresh = 507; // really ????
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };

@ -20,7 +20,7 @@
#ifndef ENGINEOPL_H_INCLUDED
#define ENGINEOPL_H_INCLUDED
#include "Arduino.h"
#include "synth.h"
#include "aligned_buf.h"
#include "fm_op_kernel.h"
@ -31,7 +31,7 @@
class EngineOpl : public FmCore {
public:
virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift);
int32_t *fb_buf, int32_t feedback_shift);
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq,

@ -5,7 +5,7 @@
#define RATE 128
#define TEENSY 1
#define TEST_MIDI
#define TEST_MIDI 1
#ifdef TEENSY
#include <Audio.h>
@ -30,6 +30,8 @@ Dexed* dexed = new Dexed(RATE);
void setup()
{
Serial.begin(115200);
//while (!Serial) ; // wait for Arduino Serial Monitor
delay(200);
Serial.println(F("MicroDexed"));
MIDI.begin(MIDI_CHANNEL_OMNI);
@ -40,7 +42,7 @@ void setup()
AudioMemory(8);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sgtl5000_1.volume(0.2);
// Initialize processor and memory measurements
//AudioProcessorUsageMaxReset();
@ -54,9 +56,14 @@ void setup()
}
#endif
dexed->activate();
#ifdef TEST_MIDI
dexed->ProcessMidiMessage(0x90, 64, 100);
#endif
Serial.println(dexed->getEngineType(), DEC);
Serial.println("Go");
}
void loop()
@ -68,18 +75,35 @@ void loop()
#endif
// process midi->audio
while (MIDI.read())
if (MIDI.read())
{
dexed->ProcessMidiMessage(MIDI.getType(), MIDI.getData1(), MIDI.getData2());
}
/* uint8_t i = 0;
Serial.println("Before:");
for (i = 0; i < 128; i++)
{
Serial.print(audio_buffer[i]);
Serial.print(",");
}
Serial.println();*/
dexed->GetSamples(audio_buffer);
dexed->GetSamples(RATE, audio_buffer);
/* Serial.println("After:");
for (i = 0; i < 128; i++)
{
Serial.print(audio_buffer[i]);
Serial.print(",");
}
Serial.println();*/
#ifdef TEENSY
// play the current buffer
while (!queue1.available());
while (!queue1.available())
Serial.println("Block");
queue1.playBuffer();
#endif
delay(500);
}

@ -20,7 +20,7 @@
#include "synth.h"
#include <stdio.h>
#include <string.h>
//#include <algorithm.h>
#include "trace.h"
#ifdef _WIN32
#define snprintf _snprintf
@ -48,46 +48,42 @@ struct FmMod {
void setRange(uint8_t r) {
range = r < 0 && r > 127 ? 0 : r;
TRACE("range=%d",range);
}
void setTarget(uint8_t assign) {
TRACE("Target: %d", assign);
assign=assign < 0 && assign > 7 ? 0 : assign;
if(assign&1) // AMP
pitch=true;
if(assign&2) // PITCH
amp=true;
if(assign&4) // EG
eg=true;
pitch=assign&1; // AMP
amp=assign&2; // PITCH
eg=assign&4; // EG
TRACE("pitch[%d] amp[%d] eg[%d]", pitch,amp,eg);
}
};
class Controllers {
void applyMod(int cc, FmMod &mod) {
int total = float(cc) * 0.01 * float(mod.range);
if(mod.amp==true)
{
float range = 0.01 * mod.range;
uint8_t total = (float)cc * range;
TRACE("amp[%d]|pitch[%d]|eg[%d]",mod.amp,mod.pitch,mod.eg);
TRACE("range=%f mod.range=%d total=%d cc=%d",range,mod.range,total,cc);
if(mod.amp)
amp_mod = max(amp_mod, total);
}
if(mod.pitch==true)
{
if(mod.pitch)
pitch_mod = max(pitch_mod, total);
}
if(mod.eg==true)
{
if(mod.eg)
eg_mod = max(eg_mod, total);
}
}
public:
int32_t values_[3];
int amp_mod;
int pitch_mod;
int eg_mod;
uint8_t amp_mod;
uint8_t pitch_mod;
uint8_t eg_mod;
uint8_t aftertouch_cc;
uint8_t breath_cc;
@ -119,7 +115,6 @@ public:
if ( ! ((wheel.eg || foot.eg) || (breath.eg || at.eg)) )
eg_mod = 127;
}
FmCore *core;

@ -31,7 +31,7 @@
#include <limits.h>
#include <math.h>
Dexed::Dexed(double num_samples)
Dexed::Dexed(uint16_t num_samples)
{
uint8_t i;
@ -78,7 +78,7 @@ Dexed::Dexed(double num_samples)
memset(&voiceStatus, 0, sizeof(VoiceStatus));
setEngineType(DEXED_ENGINE_MODERN);
setEngineType(DEXED_ENGINE_MARKI);
}
Dexed::~Dexed()
@ -337,21 +337,9 @@ void Dexed::deactivate(void)
}
*/
/*void Dexed::run (uint8_t* midi_data)
{
static int16_t buffer;
ProcessMidiMessage(midi_data);
// render audio from the last frame until the timestamp of this event
GetSamples(&buffer);
//fx.process(&buffer,_rate);
}*/
void Dexed::GetSamples(int16_t* buffer)
void Dexed::GetSamples(uint32_t n_samples, int16_t* buffer)
{
uint32_t i = 0;
uint32_t i;
if (refreshVoice) {
for (i = 0; i < max_notes; i++) {
@ -362,8 +350,7 @@ void Dexed::GetSamples(int16_t* buffer)
refreshVoice = false;
}
// remaining buffer is still to be processed
for (; i < _rate; i += _N_) {
for (i = 0; i < n_samples; i += _N_) {
AlignedBuf<int32_t, _N_> audiobuf;
float sumbuf[_N_];
@ -376,9 +363,12 @@ void Dexed::GetSamples(int16_t* buffer)
int32_t lfodelay = lfo.getdelay();
for (uint8_t note = 0; note < max_notes; ++note) {
if (voices[note].live) {
voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers);
for (uint32_t j = 0; j < _N_; ++j) {
int32_t val = audiobuf.get()[j];
val = val >> 4;
int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9;
@ -390,11 +380,12 @@ void Dexed::GetSamples(int16_t* buffer)
}
}
}
for (uint32_t j = 0; j < _N_; ++j)
buffer[i + j] = sumbuf[j];
for (uint32_t j = 0; j < _N_; ++j) {
buffer[j + (i * _N_)] = sumbuf[j];
}
}
// mark unused voice as not live
if (++_k_rate_counter % 32 && !monoMode)
{
uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers
@ -410,7 +401,7 @@ void Dexed::GetSamples(int16_t* buffer)
for (uint8_t op = 0; op < 6; op++)
{
uint8_t op_bit = pow(2, op);
uint8_t op_bit = static_cast<uint8_t>(pow(2, op));
if ((op_carrier & op_bit) > 0)
{
@ -442,7 +433,6 @@ bool Dexed::ProcessMidiMessage(uint8_t cmd, uint8_t data1, uint8_t data2)
return (false);
break;
case 0x90 :
// TRACE("MIDI keydown event: %d %d",buf[1],buf[2]);
keydown(data1, data2);
return (false);
break;

@ -72,7 +72,7 @@ class DexedVoice
class Dexed
{
public:
Dexed(double rate);
Dexed(uint16_t rate);
~Dexed();
void run(uint8_t* midi_data);
void activate(void);
@ -82,7 +82,7 @@ class Dexed
bool isMonoMode(void);
void setMonoMode(bool mode);
void set_params(void);
void GetSamples(int16_t* buffer);
void GetSamples(uint32_t n_samples, int16_t* buffer);
bool ProcessMidiMessage(uint8_t cmd, uint8_t data1, uint8_t data2);
Controllers controllers;
@ -110,8 +110,6 @@ class Dexed
EngineOpl* engineOpl;
float* outbuf_;
uint32_t bufsize_;
float extra_buf_[_N_];
uint32_t extra_buf_size_;
private:
uint16_t _rate;

@ -0,0 +1,931 @@
/**
*
* Copyright (c) 2016-2017 Holger Wirtz <dcoredump@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "synth.h"
#include "dexed.h"
#include "EngineMkI.h"
#include "EngineOpl.h"
#include "fm_core.h"
#include "exp2.h"
#include "sin.h"
#include "freqlut.h"
#include "controllers.h"
#include <unistd.h>
#include <limits.h>
Dexed::Dexed(double rate) : lvtk::Synth<DexedVoice, Dexed>(p_n_ports, p_midi_in)
{
uint8_t i;
TRACE("--------------------------------------------------------------------------------");
TRACE("Hi");
Exp2::init();
Tanh::init();
Sin::init();
Freqlut::init(rate);
Lfo::init(rate);
PitchEnv::init(rate);
Env::init_sr(rate);
fx.init(rate);
engineMkI=new EngineMkI;
engineOpl=new EngineOpl;
engineMsfa=new FmCore;
/*
if(!(engineMkI=new (std::nothrow) EngineMkI))
TRACE("Cannot not create engine EngineMkI");
if(!(engineOpl=new (std::nothrow) EngineOpl))
{
delete(engineMkI);
TRACE("Cannot not create engine EngineOpl");
}
if(!(engineMsfa=new (std::nothrow) FmCore))
{
delete(engineMkI);
delete(engineOpl);
TRACE("Cannot create engine FmCore");
}
*/
for(i=0; i<MAX_ACTIVE_NOTES; i++) {
// if(!(voices[i].dx7_note = new (std::nothrow) Dx7Note))
// TRACE("Cannot create DX7Note [%d]",i);
voices[i].dx7_note = new Dx7Note;
voices[i].keydown = false;
voices[i].sustained = false;
voices[i].live = false;
}
for(i=0;i<sizeof(data);++i)
{
data_float[i]=static_cast<float>(data[i]);
TRACE("%d->%f",i,data_float[i]);
}
max_notes=16;
currentNote = 0;
controllers.values_[kControllerPitch] = 0x2000;
controllers.values_[kControllerPitchRange] = 0;
controllers.values_[kControllerPitchStep] = 0;
controllers.modwheel_cc = 0;
controllers.foot_cc = 0;
controllers.breath_cc = 0;
controllers.aftertouch_cc = 0;
controllers.masterTune=0;
controllers.opSwitch=0x3f; // enable all operators
//controllers.opSwitch=0x00;
bufsize_=256;
outbuf_=new float[bufsize_];
//if(!(outbuf_=new (std::nothrow) float[bufsize_]))
// TRACE("Cannot create outbuf_ buffer");
lfo.reset(data+137);
setMonoMode(false);
sustain = false;
extra_buf_size_ = 0;
memset(&voiceStatus, 0, sizeof(VoiceStatus));
setEngineType(DEXED_ENGINE_MODERN);
//add_voices(new DexedVoice(rate));
add_audio_outputs(p_audio_out);
TRACE("Bye");
}
Dexed::~Dexed()
{
TRACE("Hi");
delete [] outbuf_;
currentNote = -1;
for (uint8_t note = 0; note < MAX_ACTIVE_NOTES; ++note)
delete voices[note].dx7_note;
delete(engineMsfa);
delete(engineOpl);
delete(engineMkI);
TRACE("Bye");
TRACE("--------------------------------------------------------------------------------");
}
void Dexed::activate(void)
{
TRACE("Hi");
Plugin::activate();
panic();
controllers.values_[kControllerPitchRange] = data[155];
controllers.values_[kControllerPitchStep] = data[156];
TRACE("Bye");
}
void Dexed::deactivate(void)
{
TRACE("Hi");
Plugin::deactivate();
TRACE("Bye");
}
void Dexed::set_params(void)
{
//TRACE("Hi");
_param_change_counter=0;
bool polymono=bool(*p(p_polymono));
uint8_t engine=uint8_t(*p(p_engine));
float f_gain=*p(p_output);
float f_cutoff=*p(p_cutoff);
float f_reso=*p(p_resonance);
// Dexed-Unisono
if(isMonoMode()!=polymono)
setMonoMode(polymono);
// Dexed-Engine
if(controllers.core==NULL || getEngineType()!=engine)
{
setEngineType(engine);
refreshVoice=true;
}
// Dexed-Filter
if(fx.uiCutoff!=f_cutoff)
{
fx.uiCutoff=f_cutoff;
refreshVoice=true;
}
if(fx.uiReso!=f_reso)
{
fx.uiReso=f_reso;
refreshVoice=true;
}
if(fx.uiGain!=f_gain)
{
fx.uiGain=f_gain;
refreshVoice=true;
}
// OP6
onParam(0,*p(p_op6_eg_rate_1));
onParam(1,*p(p_op6_eg_rate_2));
onParam(2,*p(p_op6_eg_rate_3));
onParam(3,*p(p_op6_eg_rate_4));
onParam(4,*p(p_op6_eg_level_1));
onParam(5,*p(p_op6_eg_level_2));
onParam(6,*p(p_op6_eg_level_3));
onParam(7,*p(p_op6_eg_level_4));
onParam(8,*p(p_op6_kbd_lev_scl_brk_pt));
onParam(9,*p(p_op6_kbd_lev_scl_lft_depth));
onParam(10,*p(p_op6_kbd_lev_scl_rht_depth));
onParam(11,*p(p_op6_kbd_lev_scl_lft_curve));
onParam(12,*p(p_op6_kbd_lev_scl_rht_curve));
onParam(13,*p(p_op6_kbd_rate_scaling));
onParam(14,*p(p_op6_amp_mod_sensitivity));
onParam(15,*p(p_op6_key_vel_sensitivity));
onParam(16,*p(p_op6_operator_output_level));
onParam(17,*p(p_op6_osc_mode));
onParam(18,*p(p_op6_osc_freq_coarse));
onParam(19,*p(p_op6_osc_freq_fine));
onParam(20,*p(p_op6_osc_detune)+7);
// OP5
onParam(21,*p(p_op5_eg_rate_1));
onParam(22,*p(p_op5_eg_rate_2));
onParam(23,*p(p_op5_eg_rate_3));
onParam(24,*p(p_op5_eg_rate_4));
onParam(25,*p(p_op5_eg_level_1));
onParam(26,*p(p_op5_eg_level_2));
onParam(27,*p(p_op5_eg_level_3));
onParam(28,*p(p_op5_eg_level_4));
onParam(29,*p(p_op5_kbd_lev_scl_brk_pt));
onParam(30,*p(p_op5_kbd_lev_scl_lft_depth));
onParam(31,*p(p_op5_kbd_lev_scl_rht_depth));
onParam(32,*p(p_op5_kbd_lev_scl_lft_curve));
onParam(33,*p(p_op5_kbd_lev_scl_rht_curve));
onParam(34,*p(p_op5_kbd_rate_scaling));
onParam(35,*p(p_op5_amp_mod_sensitivity));
onParam(36,*p(p_op5_key_vel_sensitivity));
onParam(37,*p(p_op5_operator_output_level));
onParam(38,*p(p_op5_osc_mode));
onParam(39,*p(p_op5_osc_freq_coarse));
onParam(40,*p(p_op5_osc_freq_fine));
onParam(41,*p(p_op5_osc_detune)+7);
// OP4
onParam(42,*p(p_op4_eg_rate_1));
onParam(43,*p(p_op4_eg_rate_2));
onParam(44,*p(p_op4_eg_rate_3));
onParam(45,*p(p_op4_eg_rate_4));
onParam(46,*p(p_op4_eg_level_1));
onParam(47,*p(p_op4_eg_level_2));
onParam(48,*p(p_op4_eg_level_3));
onParam(49,*p(p_op4_eg_level_4));
onParam(50,*p(p_op4_kbd_lev_scl_brk_pt));
onParam(51,*p(p_op4_kbd_lev_scl_lft_depth));
onParam(52,*p(p_op4_kbd_lev_scl_rht_depth));
onParam(53,*p(p_op4_kbd_lev_scl_lft_curve));
onParam(54,*p(p_op4_kbd_lev_scl_rht_curve));
onParam(55,*p(p_op4_kbd_rate_scaling));
onParam(56,*p(p_op4_amp_mod_sensitivity));
onParam(57,*p(p_op4_key_vel_sensitivity));
onParam(58,*p(p_op4_operator_output_level));
onParam(59,*p(p_op4_osc_mode));
onParam(60,*p(p_op4_osc_freq_coarse));
onParam(61,*p(p_op4_osc_freq_fine));
onParam(62,*p(p_op4_osc_detune)+7);
// OP3
onParam(63,*p(p_op3_eg_rate_1));
onParam(64,*p(p_op3_eg_rate_2));
onParam(65,*p(p_op3_eg_rate_3));
onParam(66,*p(p_op3_eg_rate_4));
onParam(67,*p(p_op3_eg_level_1));
onParam(68,*p(p_op3_eg_level_2));
onParam(69,*p(p_op3_eg_level_3));
onParam(70,*p(p_op3_eg_level_4));
onParam(71,*p(p_op3_kbd_lev_scl_brk_pt));
onParam(72,*p(p_op3_kbd_lev_scl_lft_depth));
onParam(73,*p(p_op3_kbd_lev_scl_rht_depth));
onParam(74,*p(p_op3_kbd_lev_scl_lft_curve));
onParam(75,*p(p_op3_kbd_lev_scl_rht_curve));
onParam(76,*p(p_op3_kbd_rate_scaling));
onParam(77,*p(p_op3_amp_mod_sensitivity));
onParam(78,*p(p_op3_key_vel_sensitivity));
onParam(79,*p(p_op3_operator_output_level));
onParam(80,*p(p_op3_osc_mode));
onParam(81,*p(p_op3_osc_freq_coarse));
onParam(82,*p(p_op3_osc_freq_fine));
onParam(83,*p(p_op3_osc_detune)+7);
// OP2
onParam(84,*p(p_op2_eg_rate_1));
onParam(85,*p(p_op2_eg_rate_2));
onParam(86,*p(p_op2_eg_rate_3));
onParam(87,*p(p_op2_eg_rate_4));
onParam(88,*p(p_op2_eg_level_1));
onParam(89,*p(p_op2_eg_level_2));
onParam(90,*p(p_op2_eg_level_3));
onParam(91,*p(p_op2_eg_level_4));
onParam(92,*p(p_op2_kbd_lev_scl_brk_pt));
onParam(93,*p(p_op2_kbd_lev_scl_lft_depth));
onParam(94,*p(p_op2_kbd_lev_scl_rht_depth));
onParam(95,*p(p_op2_kbd_lev_scl_lft_curve));
onParam(96,*p(p_op2_kbd_lev_scl_rht_curve));
onParam(97,*p(p_op2_kbd_rate_scaling));
onParam(98,*p(p_op2_amp_mod_sensitivity));
onParam(99,*p(p_op2_key_vel_sensitivity));
onParam(100,*p(p_op2_operator_output_level));
onParam(101,*p(p_op2_osc_mode));
onParam(102,*p(p_op2_osc_freq_coarse));
onParam(103,*p(p_op2_osc_freq_fine));
onParam(104,*p(p_op2_osc_detune)+7);
// OP1
onParam(105,*p(p_op1_eg_rate_1));
onParam(106,*p(p_op1_eg_rate_2));
onParam(107,*p(p_op1_eg_rate_3));
onParam(108,*p(p_op1_eg_rate_4));
onParam(109,*p(p_op1_eg_level_1));
onParam(110,*p(p_op1_eg_level_2));
onParam(111,*p(p_op1_eg_level_3));
onParam(112,*p(p_op1_eg_level_4));
onParam(113,*p(p_op1_kbd_lev_scl_brk_pt));
onParam(114,*p(p_op1_kbd_lev_scl_lft_depth));
onParam(115,*p(p_op1_kbd_lev_scl_rht_depth));
onParam(116,*p(p_op1_kbd_lev_scl_lft_curve));
onParam(117,*p(p_op1_kbd_lev_scl_rht_curve));
onParam(118,*p(p_op1_kbd_rate_scaling));
onParam(119,*p(p_op1_amp_mod_sensitivity));
onParam(120,*p(p_op1_key_vel_sensitivity));
onParam(121,*p(p_op1_operator_output_level));
onParam(122,*p(p_op1_osc_mode));
onParam(123,*p(p_op1_osc_freq_coarse));
onParam(124,*p(p_op1_osc_freq_fine));
onParam(125,*p(p_op1_osc_detune)+7);
// Global for all OPs
onParam(126,*p(p_pitch_eg_rate_1));
onParam(127,*p(p_pitch_eg_rate_2));
onParam(128,*p(p_pitch_eg_rate_3));
onParam(129,*p(p_pitch_eg_rate_4));
onParam(130,*p(p_pitch_eg_level_1));
onParam(131,*p(p_pitch_eg_level_2));
onParam(132,*p(p_pitch_eg_level_3));
onParam(133,*p(p_pitch_eg_level_4));
onParam(134,*p(p_algorithm_num)-1);
onParam(135,*p(p_feedback));
onParam(136,*p(p_oscillator_sync));
onParam(137,*p(p_lfo_speed));
onParam(138,*p(p_lfo_delay));
onParam(139,*p(p_lfo_pitch_mod_depth));
onParam(140,*p(p_lfo_amp_mod_depth));
onParam(141,*p(p_lfo_sync));
onParam(142,*p(p_lfo_waveform));
onParam(143,*p(p_pitch_mod_sensitivity));
onParam(144,*p(p_transpose));
// 10 bytes (145-154) are the name of the patch
// Controllers (added at the end of the data[])
onParam(155,*p(p_pitch_bend_range));
onParam(156,*p(p_pitch_bend_step));
onParam(157,*p(p_mod_wheel_range));
onParam(158,*p(p_mod_wheel_assign));
onParam(159,*p(p_foot_ctrl_range));
onParam(160,*p(p_foot_ctrl_assign));
onParam(161,*p(p_breath_ctrl_range));
onParam(162,*p(p_breath_ctrl_assign));
onParam(163,*p(p_aftertouch_range));
onParam(164,*p(p_aftertouch_assign));
onParam(165,*p(p_master_tune));
onParam(166,*p(p_op1_enable));
onParam(167,*p(p_op2_enable));
onParam(168,*p(p_op3_enable));
onParam(169,*p(p_op4_enable));
onParam(170,*p(p_op5_enable));
onParam(171,*p(p_op6_enable));
onParam(172,*p(p_number_of_voices));
if(_param_change_counter>PARAM_CHANGE_LEVEL)
{
panic();
controllers.refresh();
}
//TRACE("Bye");
}
// override the run() method
void Dexed::run (uint32_t sample_count)
{
const LV2_Atom_Sequence* seq = p<LV2_Atom_Sequence> (p_midi_in);
float* output = p(p_audio_out);
uint32_t last_frame = 0, num_this_time = 0;
bool drop_next_events=false;
Plugin::run(sample_count);
if(++_k_rate_counter%16)
set_params(); // pre_process: copy actual voice params
for (LV2_Atom_Event* ev = lv2_atom_sequence_begin (&seq->body);
!lv2_atom_sequence_is_end(&seq->body, seq->atom.size, ev);
ev = lv2_atom_sequence_next (ev))
{
num_this_time = ev->time.frames - last_frame;
// If it's midi, send it to the engine
if (ev->body.type == m_midi_type)
{
drop_next_events|=ProcessMidiMessage((uint8_t*) LV2_ATOM_BODY (&ev->body),ev->body.size);
if(drop_next_events==true)
continue;
}
// render audio from the last frame until the timestamp of this event
GetSamples (num_this_time, outbuf_);
// i is the index of the engine's buf, which always starts at 0 (i think)
// j is the index of the plugin's float output buffer which will be the timestamp
// of the last processed atom event.
for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j)
output[j]=outbuf_[i];
last_frame = ev->time.frames;
}
// render remaining samples if any left
if (last_frame < sample_count)
{
// do the same thing as above except from last frame until the end of
// the processing cycles last sample. at this point, all events have
// already been handled.
num_this_time = sample_count - last_frame;
GetSamples (num_this_time, outbuf_);
for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j)
output[j] = outbuf_[i];
}
fx.process(output, sample_count);
}
void Dexed::GetSamples(uint32_t n_samples, float* buffer)
{
uint32_t i;
if(refreshVoice) {
for(i=0;i < max_notes;i++) {
if ( voices[i].live )
voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity);
}
lfo.reset(data+137);
refreshVoice = false;
}
// flush first events
for (i=0; i < n_samples && i < extra_buf_size_; i++) {
buffer[i] = extra_buf_[i];
}
// remaining buffer is still to be processed
if (extra_buf_size_ > n_samples) {
for (uint32_t j = 0; j < extra_buf_size_ - n_samples; j++) {
extra_buf_[j] = extra_buf_[j + n_samples];
}
extra_buf_size_ -= n_samples;
}
else
{
for (; i < n_samples; i += N) {
AlignedBuf<int32_t, N> audiobuf;
float sumbuf[N];
for (uint32_t j = 0; j < N; ++j) {
audiobuf.get()[j] = 0;
sumbuf[j] = 0.0;
}
int32_t lfovalue = lfo.getsample();
int32_t lfodelay = lfo.getdelay();
for (uint8_t note = 0; note < max_notes; ++note) {
if (voices[note].live) {
voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers);
for (uint32_t j=0; j < N; ++j) {
int32_t val = audiobuf.get()[j];
val = val >> 4;
int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9;
float f = static_cast<float>(clip_val>>1)/0x8000;
if(f>1) f=1;
if(f<-1) f=-1;
sumbuf[j]+=f;
audiobuf.get()[j]=0;
}
}
}
uint32_t jmax = n_samples - i;
for (uint32_t j = 0; j < N; ++j) {
if (j < jmax)
buffer[i + j] = sumbuf[j];
else
extra_buf_[j - jmax] = sumbuf[j];
}
}
extra_buf_size_ = i - n_samples;
}
if(++_k_rate_counter%32 && !monoMode)
{
uint8_t op_carrier=controllers.core->get_carrier_operators(data[134]); // look for carriers
for(i=0;i < max_notes;i++)
{
if(voices[i].live==true)
{
uint8_t op_amp=0;
uint8_t op_carrier_num=0;
voices[i].dx7_note->peekVoiceStatus(voiceStatus);
for(uint8_t op=0;op<6;op++)
{
uint8_t op_bit=static_cast<uint8_t>(pow(2,op));
if((op_carrier&op_bit)>0)
{
// this voice is a carrier!
op_carrier_num++;
//TRACE("Voice[%2d] OP [%d] amp=%ld,amp_step=%d,pitch_step=%d",i,op,voiceStatus.amp[op],voiceStatus.ampStep[op],voiceStatus.pitchStep);
if(voiceStatus.amp[op]<=1069 && voiceStatus.ampStep[op]==4) // this voice produces no audio output
op_amp++;
}
}
if(op_amp==op_carrier_num)
{
// all carrier-operators are silent -> disable the voice
voices[i].live=false;
voices[i].sustained=false;
voices[i].keydown=false;
TRACE("Shutted down Voice[%2d]",i);
}
}
// TRACE("Voice[%2d] live=%d keydown=%d",i,voices[i].live,voices[i].keydown);
}
}
}
bool Dexed::ProcessMidiMessage(const uint8_t *buf, uint32_t buf_size) {
TRACE("Hi");
uint8_t cmd = buf[0];
switch(cmd & 0xf0) {
case 0x80 :
TRACE("MIDI keyup event: %d",buf[1]);
keyup(buf[1]);
return(false);
break;
case 0x90 :
TRACE("MIDI keydown event: %d %d",buf[1],buf[2]);
keydown(buf[1], buf[2]);
return(false);
break;
case 0xb0 : {
uint8_t ctrl = buf[1];
uint8_t value = buf[2];
switch(ctrl) {
case 1:
TRACE("MIDI modwheel event: %d %d",ctrl,value);
controllers.modwheel_cc = value;
controllers.refresh();
break;
case 2:
TRACE("MIDI breath event: %d %d",ctrl,value);
controllers.breath_cc = value;
controllers.refresh();
break;
case 4:
TRACE("MIDI footsw event: %d %d",ctrl,value);
controllers.foot_cc = value;
controllers.refresh();
break;
case 64:
TRACE("MIDI sustain event: %d %d",ctrl,value);
sustain = value > 63;
if (!sustain) {
for (uint8_t note = 0; note < max_notes; note++) {
if (voices[note].sustained && !voices[note].keydown) {
voices[note].dx7_note->keyup();
voices[note].sustained = false;
}
}
}
break;
case 120:
TRACE("MIDI all-sound-off: %d %d",ctrl,value);
panic();
return(true);
break;
case 123:
TRACE("MIDI all-notes-off: %d %d",ctrl,value);
notes_off();
return(true);
break;
}
break;
}
// case 0xc0 :
// setCurrentProgram(buf[1]);
// break;
// channel aftertouch
case 0xd0 :
TRACE("MIDI aftertouch 0xd0 event: %d %d",buf[1]);
controllers.aftertouch_cc = buf[1];
controllers.refresh();
break;
// pitchbend
case 0xe0 :
TRACE("MIDI pitchbend 0xe0 event: %d %d",buf[1],buf[2]);
controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7);
break;
default:
TRACE("MIDI event unknown: cmd=%d, val1=%d, val2=%d",buf[0],buf[1],buf[2]);
break;
}
TRACE("Bye");
return(false);
}
void Dexed::keydown(uint8_t pitch, uint8_t velo) {
TRACE("Hi");
TRACE("pitch=%d, velo=%d\n",pitch,velo);
if ( velo == 0 ) {
keyup(pitch);
return;
}
pitch += data[144] - 24;
uint8_t note = currentNote;
uint8_t keydown_counter=0;
for (uint8_t i=0; i<max_notes; i++) {
if (!voices[note].keydown) {
currentNote = (note + 1) % max_notes;
voices[note].midi_note = pitch;
voices[note].velocity = velo;
voices[note].sustained = sustain;
voices[note].keydown = true;
voices[note].dx7_note->init(data, pitch, velo);
if ( data[136] )
voices[note].dx7_note->oscSync();
break;
}
else
keydown_counter++;
note = (note + 1) % max_notes;
}
if(keydown_counter==0)
lfo.keydown();
if ( monoMode ) {
for(uint8_t i=0; i<max_notes; i++) {
if ( voices[i].live ) {
// all keys are up, only transfer signal
if ( ! voices[i].keydown ) {
voices[i].live = false;
voices[note].dx7_note->transferSignal(*voices[i].dx7_note);
break;
}
if ( voices[i].midi_note < pitch ) {
voices[i].live = false;
voices[note].dx7_note->transferState(*voices[i].dx7_note);
break;
}
return;
}
}
}
voices[note].live = true;
TRACE("Bye");
}
void Dexed::keyup(uint8_t pitch) {
TRACE("Hi");
TRACE("pitch=%d\n",pitch);
pitch += data[144] - 24;
uint8_t note;
for (note=0; note<max_notes; ++note) {
if ( voices[note].midi_note == pitch && voices[note].keydown ) {
voices[note].keydown = false;
break;
}
}
// note not found ?
if ( note >= max_notes ) {
TRACE("note-off not found???");
return;
}
if ( monoMode ) {
int8_t highNote = -1;
int8_t target = 0;
for (int8_t i=0; i<max_notes;i++) {
if ( voices[i].keydown && voices[i].midi_note > highNote ) {
target = i;
highNote = voices[i].midi_note;
}
}
if ( highNote != -1 && voices[note].live ) {
voices[note].live = false;
voices[target].live = true;
voices[target].dx7_note->transferState(*voices[note].dx7_note);
}
}
if ( sustain ) {
voices[note].sustained = true;
} else {
voices[note].dx7_note->keyup();
}
TRACE("Bye");
}
void Dexed::onParam(uint8_t param_num,float param_val)
{
int32_t tune;
if(param_val!=data_float[param_num])
{
TRACE("Parameter %d change from %f to %f",param_num, data_float[param_num], param_val);
#ifdef DEBUG
uint8_t tmp=data[param_num];
#endif
_param_change_counter++;
if(param_num==144 || param_num==134 || param_num==172)
panic();
refreshVoice=true;
data[param_num]=static_cast<uint8_t>(param_val);
data_float[param_num]=param_val;
switch(param_num)
{
case 155:
controllers.values_[kControllerPitchRange]=data[param_num];
break;
case 156:
controllers.values_[kControllerPitchStep]=data[param_num];
break;
case 157:
controllers.wheel.setRange(data[param_num]);
controllers.wheel.setTarget(data[param_num+1]);
controllers.refresh();
break;
case 158:
controllers.wheel.setRange(data[param_num-1]);
controllers.wheel.setTarget(data[param_num]);
controllers.refresh();
break;
case 159:
controllers.foot.setRange(data[param_num]);
controllers.foot.setTarget(data[param_num+1]);
controllers.refresh();
break;
case 160:
controllers.foot.setRange(data[param_num-1]);
controllers.foot.setTarget(data[param_num]);
controllers.refresh();
break;
case 161:
controllers.breath.setRange(data[param_num]);
controllers.breath.setTarget(data[param_num+1]);
controllers.refresh();
break;
case 162:
controllers.breath.setRange(data[param_num-1]);
controllers.breath.setTarget(data[param_num]);
controllers.refresh();
break;
case 163:
controllers.at.setRange(data[param_num]);
controllers.at.setTarget(data[param_num+1]);
controllers.refresh();
break;
case 164:
controllers.at.setRange(data[param_num-1]);
controllers.at.setTarget(data[param_num]);
controllers.refresh();
break;
case 165:
tune=param_val*0x4000;
controllers.masterTune=(tune<<11)*(1.0/12);
break;
case 166:
case 167:
case 168:
case 169:
case 170:
case 171:
controllers.opSwitch=(data[166]<<5)|(data[167]<<4)|(data[168]<<3)|(data[169]<<2)|(data[170]<<1)|data[171];
break;
case 172:
max_notes=data[param_num];
break;
}
TRACE("Done: Parameter %d changed from %d to %d",param_num, tmp, data[param_num]);
}
}
uint8_t Dexed::getEngineType() {
return engineType;
}
void Dexed::setEngineType(uint8_t tp) {
TRACE("settings engine %d", tp);
if(engineType==tp && controllers.core!=NULL)
return;
switch (tp) {
case DEXED_ENGINE_MARKI:
TRACE("DEXED_ENGINE_MARKI:%d",DEXED_ENGINE_MARKI);
controllers.core = engineMkI;
break;
case DEXED_ENGINE_OPL:
TRACE("DEXED_ENGINE_OPL:%d",DEXED_ENGINE_OPL);
controllers.core = engineOpl;
break;
default:
TRACE("DEXED_ENGINE_MODERN:%d",DEXED_ENGINE_MODERN);
controllers.core = engineMsfa;
tp=DEXED_ENGINE_MODERN;
break;
}
engineType = tp;
panic();
controllers.refresh();
}
bool Dexed::isMonoMode(void) {
return monoMode;
}
void Dexed::setMonoMode(bool mode) {
if(monoMode==mode)
return;
monoMode = mode;
}
void Dexed::panic(void) {
for(uint8_t i=0;i<MAX_ACTIVE_NOTES;i++)
{
if(voices[i].live == true) {
voices[i].keydown = false;
voices[i].live = false;
voices[i].sustained = false;
if ( voices[i].dx7_note != NULL ) {
voices[i].dx7_note->oscSync();
}
}
}
}
void Dexed::notes_off(void) {
for(uint8_t i=0;i<MAX_ACTIVE_NOTES;i++) {
if(voices[i].live==true&&voices[i].keydown==true) {
voices[i].keydown=false;
}
}
}
//==============================================================================
DexedVoice::DexedVoice(double rate) : m_key(lvtk::INVALID_KEY), m_rate(rate)
{
TRACE("Hi");
TRACE("Bye");
}
DexedVoice::~DexedVoice()
{
TRACE("Hi");
TRACE("Bye");
}
void DexedVoice::on(unsigned char key, unsigned char velocity)
{
TRACE("Hi");
m_key = key;
TRACE("Bye");
}
void DexedVoice::off(unsigned char velocity)
{
TRACE("Hi");
m_key = lvtk::INVALID_KEY;
TRACE("Bye");
}
unsigned char DexedVoice::get_key(void) const
{
TRACE("Hi");
return m_key;
TRACE("Bye");
}
static int _ = Dexed::register_class(p_uri);

@ -44,14 +44,20 @@ int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
int32_t logfreq;
if (mode == 0) {
logfreq = 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);
// // 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;
@ -138,7 +144,7 @@ Dx7Note::Dx7Note() {
}
}
void Dx7Note::init(const uint8_t patch[167], int midinote, int velocity) {
void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
@ -164,6 +170,7 @@ void Dx7Note::init(const uint8_t patch[167], int midinote, int velocity) {
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];
}
@ -186,30 +193,31 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co
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 = ((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14;
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) {
pb = ((float) (pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0;
pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRange]) / 12.0;
} else {
int stp = 12 / ctrls->values_[kControllerPitchStep];
pb = pb * stp / 8191;
pb = (pb * (8191 / stp)) << 11;
}
}
pitch_mod += pb;
pitch_mod += ctrls->masterTune;
int32_t pitch_base = pb + ctrls->masterTune;
pitch_mod += pitch_base;
// ==== AMP MOD ====
uint32_t amod_1 = ((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8; // Q24 :D
amod_1 = ((int64_t) amod_1 * (int64_t) lfo_val) >> 24;
uint32_t amod_2 = ((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7; // Q?? :|
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 ====
@ -218,21 +226,25 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co
// ==== OP RENDER ====
for (int op = 0; op < 6; op++) {
//if ( ctrls->opSwitch[op] == '0' ) {
if (!(ctrls->opSwitch & (1<<op))) {
// if ( ctrls->opSwitch[op] == '0' ) {
if (!(ctrls->opSwitch & (1<<op))) {
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)));
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_mod);
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 = ((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24;
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 = ((uint64_t)level) * (((uint64_t)pt<<4)) >> 28;
uint32_t ldiff = (uint32_t)(((uint64_t)level) * (((uint64_t)pt<<4)) >> 28);
level -= ldiff;
}
params_[op].level_in = level;
@ -259,10 +271,11 @@ void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) {
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];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
@ -316,4 +329,3 @@ void Dx7Note::oscSync() {
params_[i].phase = 0;
}
}

@ -37,8 +37,7 @@ struct VoiceStatus {
class Dx7Note {
public:
Dx7Note();
//void init(const uint8_t patch[156], int midinote, int velocity, int fb_depth);
void init(const uint8_t patch[160], int midinote, int velocity);
void init(const uint8_t patch[156], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
@ -67,6 +66,7 @@ private:
int32_t fb_buf_[2];
int32_t fb_shift_;
int32_t ampmodsens_[6];
int32_t opMode[6];
int ampmoddepth_;
int algorithm_;

188
env.cc

@ -0,0 +1,188 @@
/*
* 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"
//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 (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 if (staticcount_) {
;
}
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_) {
// 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);
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 ourselve 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_;
}

10
env.h

@ -1,4 +1,5 @@
/*
* Copyright 2017 Pascal Gauthier.
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,6 +22,8 @@
// DX7 envelope generation
#define ACCURATE_ENVELOPE
class Env {
public:
@ -33,7 +36,6 @@ class Env {
void update(const int rates[4], const int levels[4], int32_t 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
@ -50,10 +52,11 @@ class Env {
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_;
@ -66,6 +69,9 @@ class Env {
bool rising_;
int ix_;
int inc_;
#ifdef ACCURATE_ENVELOPE
int staticcount_;
#endif
bool down_;

@ -23,6 +23,7 @@
#include "fm_op_kernel.h"
#include "fm_core.h"
//using namespace std;
const FmAlgorithm FmCore::algorithms[32] = {
@ -77,6 +78,8 @@ uint8_t FmCore::get_carrier_operators(uint8_t algorithm)
{
if((alg.ops[i]&OUT_BUS_ADD)==OUT_BUS_ADD)
op_out|=1<<i;
// TRACE("OP[%d]:",6-i);
// TRACE("OUT_BUS_ONE=%d OUT_BUS_TWO=%d OUT_BUS_ADD=%d IN_BUS_ONE=%d IN_BUS_TWO=%d FB_IN=%d FB_OUT=%d",alg.ops[i]&OUT_BUS_ONE?1:0,alg.ops[i]&OUT_BUS_TWO?1:0,alg.ops[i]&OUT_BUS_ADD?1:0,alg.ops[i]&IN_BUS_ONE?1:0,alg.ops[i]&IN_BUS_TWO?1:0,alg.ops[i]&FB_IN?1:0,alg.ops[i]&FB_OUT?1:0);
}
return op_out;
@ -103,7 +106,7 @@ void FmCore::dump() {
#endif
}
void FmCore::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift) {
void FmCore::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) {
const int kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };

@ -49,7 +49,7 @@ public:
virtual ~FmCore() {};
static void dump();
uint8_t get_carrier_operators(uint8_t algorithm);
virtual void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_gain);
virtual void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_gain);
protected:
AlignedBuf<int32_t, _N_>buf_[2];
const static FmAlgorithm algorithms[32];

@ -0,0 +1,283 @@
/*
* 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

@ -52,7 +52,7 @@ void FmOpKernel::compute(int32_t *output, const int32_t *input,
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
neon_fm_kernel(input, add ? output : zeros, output, N,
neon_fm_kernel(input, add ? output : zeros, output, _N_,
phase0, freq, gain, dgain);
#endif
} else {
@ -172,7 +172,7 @@ void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t s = Sin::compute(freq) << 6;
int32_t c = Sin::compute(freq + (1 << 22)) << 6;
#endif
for (int i = 0; i < N; i++) {
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;
@ -270,7 +270,7 @@ void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
#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++) {
for (int i = 0; i < _N_; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
//output[i] = u;

@ -95,4 +95,3 @@ void Lfo::keydown() {
}
delaystate_ = 0;
}

@ -47,7 +47,6 @@ typedef __int16 SInt16;
#define SynthMemoryBarrier()
#endif
/*
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
@ -57,13 +56,6 @@ template<typename T>
inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}
*/
#undef max
#define max(a,b) ((a)>(b)?(a):(b))
#undef min
#define min(a,b) ((a)<(b)?(a):(b))
#define QER(n,b) ( ((float)n)/(1<<b) )

@ -0,0 +1,11 @@
#ifndef TRACE_H_INCLUDED
#define TRACE_H_INCLUDED
#ifdef DEBUG
extern void _trace(const char *source, const char *fmt, ...);
#define TRACE(fmt, ...) _trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#else
#define TRACE(fmt, ...)
#endif
#endif
Loading…
Cancel
Save