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. 9
      Source/msfa/env.h
  8. 35
      Source/msfa/fm_core.cc
  9. 6
      Source/msfa/fm_core.h
  10. 92
      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 Changelog
--------- ---------
#### Version 0.7.0 (current sprint) #### Version 0.7.0 (current sprint)
* DX Engine customizable bitrate engine * Preliminary Algo 4 & 6 feedback support
* DX Engine AM implementation * 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 #### Version 0.6.1
* Mouse over + LFO type fix + pitch eg values * 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 musical and expressive. It is those 'hacks' that we need to recreate to be able to find that original
DX sound. 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. * 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. * 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. * 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 TODO - msfa
----------- -----------
* The sample rate should not change the response of the envelopes
* Portamento implementation * Portamento implementation
* Algo 4 & 6 feedback * Feedback tuning

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

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

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

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

@ -42,8 +42,15 @@ class Env {
void keydown(bool down); void keydown(bool down);
void setparam(int param, int value); void setparam(int param, int value);
static int scaleoutlevel(int outlevel); static int scaleoutlevel(int outlevel);
void getPosition(char *step); void getPosition(char *step);
static void init_sr(double sample_rate);
private: 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 rates_[4];
int levels_[4]; int levels_[4];
int outlevel_; int outlevel_;

@ -23,7 +23,7 @@
#include "fm_core.h" #include "fm_core.h"
using namespace std; //using namespace std;
struct FmOperatorInfo { struct FmOperatorInfo {
int in; int in;
@ -48,9 +48,9 @@ const FmAlgorithm algorithms[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1 { { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2 { { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3 { { 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 { { 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 { { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8 { { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9 { { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
@ -127,12 +127,31 @@ void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm,
add = false; add = false;
} }
if (inbus == 0 || !has_contents[inbus]) { 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) { if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl; switch ( (flags >> 6) - 0xc ) {
FmOpKernel::compute_fb(outptr, param.phase, param.freq, // one operator feedback, normal process
gain1, gain2, case 0 :
fb_buf, feedback_shift, add, controllers); //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 { } else {
// cout << op << " pure " << inbus << outbus << add << endl; // cout << op << " pure " << inbus << outbus << add << endl;
FmOpKernel::compute_pure(outptr, param.phase, param.freq, FmOpKernel::compute_pure(outptr, param.phase, param.freq,

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

@ -1,6 +1,6 @@
/* /*
* Copyright 2012 Google Inc. * Copyright 2012 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
@ -159,6 +159,96 @@ void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
fb_buf[1] = y; 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" #include "controllers.h"
struct FmOpParams {
int32_t gain[2];
int32_t freq;
int32_t phase;
};
class FmOpKernel { class FmOpKernel {
public: public:
// gain1 and gain2 represent linear step: gain for sample i is // 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, static void compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_gain, bool add, const Controllers *controllers); 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 #endif

@ -57,4 +57,14 @@ inline static T max(const T& a, const T& b) {
return a > b ? a : 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 #endif // __SYNTH_H

Loading…
Cancel
Save