Algo 4-6 Feedback support

pull/1/head
asb2m10 10 years ago
parent 05cd3aeba9
commit c228e50f10
  1. BIN
      Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate
  2. 12
      README.md
  3. 9
      Source/PluginParam.cpp
  4. 5
      Source/PluginProcessor.cpp
  5. 14
      Source/msfa/dx7note.cc
  6. 9
      Source/msfa/env.cc
  7. 7
      Source/msfa/env.h
  8. 29
      Source/msfa/fm_core.cc
  9. 6
      Source/msfa/fm_core.h
  10. 90
      Source/msfa/fm_op_kernel.cc
  11. 12
      Source/msfa/fm_op_kernel.h
  12. 10
      Source/msfa/synth.h

@ -37,8 +37,11 @@ new version here but you see it in the change log, it's because this version is
Changelog
---------
#### Version 0.7.0 (current sprint)
* DX Engine customizable bitrate engine
* DX Engine AM implementation
* Preliminary Algo 4 & 6 feedback support
* DX Engine 'Dirty DX' emulation
* DX Engine LFO amplitude - still buggy
* Fixed stucked notes when programs where changed
* Fixed engine envelop wrong timing if it was not 44100Khz - still buggy
#### Version 0.6.1
* Mouse over + LFO type fix + pitch eg values
@ -71,7 +74,7 @@ you get something "not quite there". Yamaha did a lot of hacks to be able to squ
musical and expressive. It is those 'hacks' that we need to recreate to be able to find that original
DX sound.
Dexed comes with 3 engine resolution.
Dexed comes with 3 engine resolutions :
* Modern : this is the original 24-bit music-synthesizer-for-android implementation.
* Mark I : this is a pale implementation of the limitation of a Yamaha DX7 Mark I with the 12-bit (with the 4-bit attenuator hack) DAC.
* OPL Series : this is a experimental implementation of Yamaha 4-ops that used the YM2151 chip. These chips were supposed to be even more limited to the DX7 but gave a very interesting distinctive sound.
@ -145,6 +148,5 @@ TODO - Dexed
TODO - msfa
-----------
* The sample rate should not change the response of the envelopes
* Portamento implementation
* Algo 4 & 6 feedback
* Feedback tuning

@ -456,11 +456,8 @@ void DexedAudioProcessor::setCurrentProgram(int index) {
return;
}
for (int i = 0; i < MAX_ACTIVE_NOTES; i++) {
if (voices[i].keydown == false && voices[i].live == true) {
voices[i].live = false;
}
}
panic();
index = index > 31 ? 31 : index;
unpackProgram(index);
lfo.reset(data + 137);
@ -473,6 +470,8 @@ void DexedAudioProcessor::setCurrentProgram(int index) {
return;
}
editor->global.setParamMessage("");
panic();
}
const String DexedAudioProcessor::getProgramName(int index) {

@ -26,6 +26,7 @@
#include "msfa/freqlut.h"
#include "msfa/sin.h"
#include "msfa/exp2.h"
#include "msfa/env.h"
#include "msfa/pitchenv.h"
#include "msfa/aligned_buf.h"
#include "msfa/fm_op_kernel.h"
@ -87,6 +88,7 @@ void DexedAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
Freqlut::init(sampleRate);
Lfo::init(sampleRate);
PitchEnv::init(sampleRate);
Env::init_sr(sampleRate);
fx.init(sampleRate);
for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) {
@ -364,6 +366,7 @@ void DexedAudioProcessor::keyup(uint8_t pitch) {
void DexedAudioProcessor::panic() {
for(int i=0;i<MAX_ACTIVE_NOTES;i++) {
voices[i].keydown = false;
voices[i].live = false;
}
keyboardState.reset();
}
@ -438,7 +441,7 @@ void DexedAudioProcessor::setEngineResolution(int rs) {
controllers.dacBitFilter = 0xFFFFF000; // semi 14 bit
break;
case DEXED_RESO_OPL:
controllers.sinBitFilter = 0xFFFF8000; // 9 bit
controllers.sinBitFilter = 0xFFFF0000; // 9 bit
controllers.dacBitFilter = 0xFFFF0000;
break;
}

@ -25,14 +25,6 @@ using namespace std;
#include "controllers.h"
#include "dx7note.h"
void dexed_trace(const char *source, const char *fmt, ...);
#ifdef _MSC_VER
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
#else
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#endif
int32_t midinote_to_logfreq(int midinote) {
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int step = (1 << 24) / 12;
@ -134,9 +126,8 @@ static const uint8_t pitchmodsenstab[] = {
0, 10, 20, 33, 55, 92, 153, 255
};
// PG: we need to find the real values
static const uint8_t ampmodsenstab[] = {
0, 33, 153, 255
0, 66, 109, 255
};
void Dx7Note::init(const char patch[156], int midinote, int velocity) {
@ -178,8 +169,6 @@ void Dx7Note::init(const char patch[156], int midinote, int velocity) {
params_[op].phase = 0;
params_[op].gain[1] = 0;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
TRACE("operator set: %d %d", op, ampmodsens_[op]);
}
for (int i = 0; i < 4; i++) {
rates[i] = patch[126 + i];
@ -253,6 +242,7 @@ void Dx7Note::update(const char patch[156], int midinote) {
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];
}
algorithm_ = patch[134];
int feedback = patch[135];

@ -21,6 +21,12 @@
using namespace std;
uint32_t Env::sr_multiplier = (1<<24);
void Env::init_sr(double sampleRate) {
sr_multiplier = (44100 / sampleRate) * (1<<23);
}
void Env::init(const int r[4], const int l[4], int32_t ol, int rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
@ -98,6 +104,9 @@ void Env::advance(int newix) {
qrate += rate_scaling_;
qrate = min(qrate, 63);
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2));
// meh, this should be fixed elsewhere
inc_ = ((int64_t)inc_ * (int64_t)sr_multiplier) >> 23;
}
}

@ -43,7 +43,14 @@ class Env {
void setparam(int param, int value);
static int scaleoutlevel(int outlevel);
void getPosition(char *step);
static void init_sr(double sample_rate);
private:
// 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_;

@ -23,7 +23,7 @@
#include "fm_core.h"
using namespace std;
//using namespace std;
struct FmOperatorInfo {
int in;
@ -48,9 +48,9 @@ const FmAlgorithm algorithms[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
{ { 0x41, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
{ { 0xe1, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
{ { 0x41, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
{ { 0xd1, 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
@ -127,12 +127,31 @@ void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm,
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// todo: more than one op in a feedback loop
// PG: this is my 'dirty' implementation of FB for 2 and 3 operators...
// still needs some tuning...
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl;
switch ( (flags >> 6) - 0xc ) {
// one operator feedback, normal process
case 0 :
//cout << "\t" << op << " fb " << inbus << outbus << add << endl;
FmOpKernel::compute_fb(outptr, param.phase, param.freq,
gain1, gain2,
fb_buf, feedback_shift, add, controllers);
break;
// two operator feedback, process exception for ALGO 6
case 1 :
FmOpKernel::compute_fb2(outptr, params, fb_buf, feedback_shift, controllers);
params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5
op++; // ignore next operator;
break;
// three operator feedback, process exception for ALGO 4
case 2 :
FmOpKernel::compute_fb3(outptr, params, fb_buf, feedback_shift, controllers);
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;
}
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
FmOpKernel::compute_pure(outptr, param.phase, param.freq,

@ -22,12 +22,6 @@
#include "synth.h"
#include "controllers.h"
struct FmOpParams {
int32_t gain[2];
int32_t freq;
int32_t phase;
};
class FmCore {
public:
static void dump();

@ -159,6 +159,96 @@ void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
fb_buf[1] = y;
}
// exclusively used for ALGO 6 with feedback
void FmOpKernel::compute_fb2(int32_t *output, FmOpParams *parms, int32_t *fb_buf, int fb_shift, const Controllers *cont) {
int32_t dgain[2];
int32_t gain[2];
int32_t phase[2];
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
phase[0] = parms[0].phase;
phase[1] = parms[1].phase;
dgain[0] = (parms[0].gain[1] - parms[0].gain[0] + (N >> 1)) >> LG_N;
dgain[1] = (parms[1].gain[1] - parms[1].gain[1] + (N >> 1)) >> LG_N;
gain[0] = parms[0].gain[0];
gain[1] = parms[1].gain[1];
for (int i = 0; i < N; i++) {
// op 0
gain[0] += dgain[0];
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase[0] + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
phase[0] += parms[0].freq;
// op 1
gain[1] += dgain[1];
//scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase[1] + scaled_fb + y);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase[1] += parms[1].freq;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
// exclusively used for ALGO 4 with feedback
void FmOpKernel::compute_fb3(int32_t *output, FmOpParams *parms, int32_t *fb_buf, int fb_shift, const Controllers *conts) {
int32_t dgain[3];
int32_t gain[3];
int32_t phase[3];
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
phase[0] = parms[0].phase;
phase[1] = parms[1].phase;
phase[2] = parms[2].phase;
dgain[0] = (parms[0].gain[1] - parms[0].gain[0] + (N >> 1)) >> LG_N;
dgain[1] = (parms[1].gain[1] - parms[1].gain[0] + (N >> 1)) >> LG_N;
dgain[2] = (parms[2].gain[1] - parms[2].gain[0] + (N >> 1)) >> LG_N;
gain[0] = parms[0].gain[0];
gain[1] = parms[1].gain[0];
gain[2] = parms[2].gain[0];
for (int i = 0; i < N; i++) {
// op 0
gain[0] += dgain[0];
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase[0] + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
phase[0] += parms[0].freq;
// op 1
gain[1] += dgain[1];
scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase[1] + scaled_fb + y);
y = ((int64_t)y * (int64_t)gain) >> 24;
phase[1] += parms[1].freq;
// op 2
gain[2] += dgain[2];
scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase[2] + scaled_fb + y);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase[2] += parms[2].freq;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

@ -19,6 +19,12 @@
#include "controllers.h"
struct FmOpParams {
int32_t gain[2];
int32_t freq;
int32_t phase;
};
class FmOpKernel {
public:
// gain1 and gain2 represent linear step: gain for sample i is
@ -37,6 +43,12 @@ class FmOpKernel {
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, const Controllers *controllers);
static void compute_fb2(int32_t *output, FmOpParams *params, int32_t *fb_buf, int fb_shift,
const Controllers *controllers);
static void compute_fb3(int32_t *output, FmOpParams *params, int32_t *fb_buf, int fb_shift,
const Controllers *controllers);
};
#endif

@ -57,4 +57,14 @@ inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}
void dexed_trace(const char *source, const char *fmt, ...);
#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 // __SYNTH_H

Loading…
Cancel
Save