Monophonic Mode support

pull/1/head
asb2m10 10 years ago
parent df045032ce
commit 97aeda47b7
  1. 2
      Builds/MacOSX/Dexed.xcodeproj/project.pbxproj
  2. BIN
      Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate
  3. 2
      Dexed.jucer
  4. 42
      README.md
  5. 26
      Source/DXComponents.cpp
  6. 24
      Source/DXComponents.h
  7. 24
      Source/DXLookNFeel.cpp
  8. 24
      Source/DXLookNFeel.h
  9. 24
      Source/Dexed.h
  10. 355
      Source/EngineMkI.cpp
  11. 45
      Source/EngineMkI.h
  12. 143
      Source/EngineOpl.cpp
  13. 14
      Source/EngineOpl.h
  14. 31
      Source/PluginData.cpp
  15. 24
      Source/PluginData.h
  16. 34
      Source/PluginEditor.cpp
  17. 25
      Source/PluginEditor.h
  18. 24
      Source/PluginFx.cpp
  19. 24
      Source/PluginFx.h
  20. 24
      Source/PluginParam.cpp
  21. 24
      Source/PluginParam.h
  22. 105
      Source/PluginProcessor.cpp
  23. 29
      Source/PluginProcessor.h
  24. 52
      Source/SysexComm.cpp
  25. 41
      Source/SysexComm.h
  26. 34
      Source/msfa/dx7note.cc
  27. 1
      Source/msfa/dx7note.h
  28. 23
      Source/msfa/env.cc
  29. 1
      Source/msfa/env.h
  30. 11
      Source/msfa/fm_core.cc
  31. 2
      Source/msfa/fm_core.h
  32. 6
      Source/msfa/fm_op_kernel.h

@ -892,8 +892,8 @@
0652CE27AE4971C99654E3BF = {isa = PBXGroup; children = ( 0652CE27AE4971C99654E3BF = {isa = PBXGroup; children = (
8BADEB7BF1A65E83A7A1736D, 8BADEB7BF1A65E83A7A1736D,
DC75DFCDFCDB425927B11EC0, DC75DFCDFCDB425927B11EC0,
C222D35AC4FC5C2A3BB0F8B6,
8BEBEDCAD409EE3A5D383B6D, 8BEBEDCAD409EE3A5D383B6D,
C222D35AC4FC5C2A3BB0F8B6,
61F792AFE04C15F413A4F766, 61F792AFE04C15F413A4F766,
7A58027CF8C6967B02370E01, ); name = dsp; sourceTree = "<group>"; }; 7A58027CF8C6967B02370E01, ); name = dsp; sourceTree = "<group>"; };
427DD4218ED26D69B0149A90 = {isa = PBXGroup; children = ( 427DD4218ED26D69B0149A90 = {isa = PBXGroup; children = (

@ -19,8 +19,8 @@
<GROUP id="{1B64F271-8E56-F19A-FF63-094159E5EF7B}" name="dsp"> <GROUP id="{1B64F271-8E56-F19A-FF63-094159E5EF7B}" name="dsp">
<FILE id="bvvgdq" name="PluginFx.cpp" compile="1" resource="0" file="Source/PluginFx.cpp"/> <FILE id="bvvgdq" name="PluginFx.cpp" compile="1" resource="0" file="Source/PluginFx.cpp"/>
<FILE id="E4gace" name="PluginFx.h" compile="0" resource="0" file="Source/PluginFx.h"/> <FILE id="E4gace" name="PluginFx.h" compile="0" resource="0" file="Source/PluginFx.h"/>
<FILE id="Fyf1ag" name="EngineMkI.h" compile="0" resource="0" file="Source/EngineMkI.h"/>
<FILE id="gZfpcq" name="EngineMkI.cpp" compile="1" resource="0" file="Source/EngineMkI.cpp"/> <FILE id="gZfpcq" name="EngineMkI.cpp" compile="1" resource="0" file="Source/EngineMkI.cpp"/>
<FILE id="Fyf1ag" name="EngineMkI.h" compile="0" resource="0" file="Source/EngineMkI.h"/>
<FILE id="gY0vZ2" name="EngineOpl.cpp" compile="1" resource="0" file="Source/EngineOpl.cpp"/> <FILE id="gY0vZ2" name="EngineOpl.cpp" compile="1" resource="0" file="Source/EngineOpl.cpp"/>
<FILE id="TF7JMc" name="EngineOpl.h" compile="0" resource="0" file="Source/EngineOpl.h"/> <FILE id="TF7JMc" name="EngineOpl.h" compile="0" resource="0" file="Source/EngineOpl.h"/>
</GROUP> </GROUP>

@ -10,12 +10,12 @@ with 'float' value parameters, different waveform à la TX81z would be great but
goes beyond the DX7 should and will be a fork of this project. This is to keep the compatibility with goes beyond the DX7 should and will be a fork of this project. This is to keep the compatibility with
the original machine. the original machine.
Dexed is licensed on the GPL v2. The msfa component (acronym for music synthesizer for android, see msfa Dexed is licensed on the GPL v3. The msfa component (acronym for music synthesizer for android, see msfa
in the source folder) stays on the Apache 2.0 license to able to collaborate between projects. in the source folder) stays on the Apache 2.0 license to able to collaborate between projects.
Features Features
-------- --------
* Multi platform (OS X, Windows or Linux) and multi format (VST, AU and others that I don't use); by using JUCE * Multi platform (OS X, Windows or Linux) and multi format (VST, *soon* AU and others that I don't use); by using JUCE
* The sound engine [music-synthesizer-for-android](https://code.google.com/p/music-synthesizer-for-android) is closely modeled on the original DX7 characteristics * The sound engine [music-synthesizer-for-android](https://code.google.com/p/music-synthesizer-for-android) is closely modeled on the original DX7 characteristics
* 144 DAW automatable DX7 parameters available from one single panel * 144 DAW automatable DX7 parameters available from one single panel
* Fully supports DX7 input and output Sysex messages; including controller change. This means that you can use this with a native DX7/TX7 as a patch editor and sysex manager * Fully supports DX7 input and output Sysex messages; including controller change. This means that you can use this with a native DX7/TX7 as a patch editor and sysex manager
@ -29,18 +29,20 @@ in normal operation it shouldn't crash and the VST state saving works. If you do
new version here but you see it in the change log, it's because this version is in development new version here but you see it in the change log, it's because this version is in development
(current sprint). Only officials (tested) builds are listed here. (current sprint). Only officials (tested) builds are listed here.
* Version 0.7.0 OMG !
* Version 0.6.1 [vst win32/x64](http://le-son666.com/software/dexed/dexed-0.6.1-win.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.6.1-osx.vst.zip) * Version 0.6.1 [vst win32/x64](http://le-son666.com/software/dexed/dexed-0.6.1-win.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.6.1-osx.vst.zip)
Changelog Changelog
--------- ---------
#### Version 0.7.0 (current sprint) #### Version 0.7.0 (current sprint)
* Preliminary Algo 4 & 6 feedback support * Preliminary Algo 4 & 6 feedback support
* DX Engine 'Dirty DX' emulation, including based on OPL chips * DX Engine 'Dirty DX' emulation, including one based on OPL chips
* DX Engine LFO amplitude. This still needs some tunings * DX Engine LFO amplitude. This still needs some tunings :(
* Fixed stucked notes when programs where changed * Monophonic mode
* Added the 'INIT' button to re-initialize a program * Added the 'INIT' button to re-initialize a program
* Fixed engine envelop wrong timing if it was not 44100Khz * Fixed stucked notes when programs where changed
* Filter only .syx files when using the Dexed_cart.zip file * Fixed engine envelopes wrong timing if it was not 44100Khz
* Fixed only .syx are shown when we are using the Dexed_cart.zip cartridges collection
#### Version 0.6.1 #### Version 0.6.1
* Mouse over + LFO type fix + pitch eg values * Mouse over + LFO type fix + pitch eg values
@ -61,22 +63,17 @@ sysex content to where you have installed Dexed (VST plugins dir). Then rename t
file is changed. Directories in the zip file will be transformed into submenu when you hit the file is changed. Directories in the zip file will be transformed into submenu when you hit the
[CART] button. Watch out; Windows hides the .zip extension by default ! [CART] button. Watch out; Windows hides the .zip extension by default !
Engine resolutions Engine Type
------------------ -----------
Dexed can be configured to try to use the original math limitation of a DX synthesizer. And when I say Dexed can be configured to try to use the original math limitation of a DX synthesizer. This does not
math limitation, I'm not only talking about the DAC, it is also about the sin LUT lookup table, multiply only apply to the DAC, it also involves the bit resolution of the sine waves and the way that the
resolution and original DX sampling rate. This is a work in progress and this might take time to be able amplitude is applied to each operator. Since all of this is experimental, multiple engines will be
to perfect. available to be able to compare them easily.
If you look at the original DX7 and implementation a DX engine with 10-bit sin lookup and 12 mul possibility,
you get something "not quite there". Yamaha did a lot of hacks to be able to squeeze this into something
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 resolutions : Dexed comes with 3 engine types :
* 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 : It is the music-synthesizer-for-android implementation reduced to 8-bit. It is used to be able to see the difference between the OPL series that also uses 8-bit but with sums of logs to avoid multiplications. It will be upgraded to 10-bit/12-bit later.
* 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 an experimental implementation of the [reversed engineered OPL family chips](http://sourceforge.net/projects/peepeeplayer). 8-bit. Keep in mind that the envelopes stills needs tuning.
Using as a DX7 editor Using as a DX7 editor
--------------------- ---------------------
@ -104,7 +101,6 @@ you edit.
### Troubleshooting ### Troubleshooting
* If you play on your DX7 keyboard, the |DX7 In| light should be flashing. Use this to test the midi in communication. * If you play on your DX7 keyboard, the |DX7 In| light should be flashing. Use this to test the midi in communication.
* If you click/play on the Dexed virtual keyboard, it will send the corresponding midi out note to the DX7 port; if configured. Use this to test the midi out communication.
* If the data sent is corrupted (wrong checksum, DX7 crash), it might be the midi interface implementation. Default Windows USB midi driver are known to send corrupt sysex data. If it is the case, use a third party device (like the midiman uno) that have his own USB driver. * If the data sent is corrupted (wrong checksum, DX7 crash), it might be the midi interface implementation. Default Windows USB midi driver are known to send corrupt sysex data. If it is the case, use a third party device (like the midiman uno) that have his own USB driver.
* If you are unable to open the interface (error message after the using the [PARM] dialog), it might be because the midi driver doesn't support multiple clients (common on Windows). Be sure that there are no other applications that are using the same midi interface. * If you are unable to open the interface (error message after the using the [PARM] dialog), it might be because the midi driver doesn't support multiple clients (common on Windows). Be sure that there are no other applications that are using the same midi interface.
@ -130,6 +126,7 @@ Credits & thanks
---------------- ----------------
* DX Synth engine : Raph Levien and the [msfa](https://code.google.com/p/music-synthesizer-for-android) team * DX Synth engine : Raph Levien and the [msfa](https://code.google.com/p/music-synthesizer-for-android) team
* LP Filter : Filatov Vadim (2DaT); taken from the excellent [Obxd](https://obxd.wordpress.com) project * LP Filter : Filatov Vadim (2DaT); taken from the excellent [Obxd](https://obxd.wordpress.com) project
* PPPlay : Great [OPL3](http://sourceforge.net/projects/peepeeplayer) implementation, with documented code :D
* DX7 program compilation : Jean-Marc Desprez (author of [SynprezFM](http://www.synprez.com/SynprezFM)) * DX7 program compilation : Jean-Marc Desprez (author of [SynprezFM](http://www.synprez.com/SynprezFM))
* DX7 programs : Dave Benson, Frank Carvalho, Tim Conrardy, Jack Deckard, Chris Dodunski, Tim Garrett, Hitaye, Stephan Ibsen, Christian Jezreel, Narfman, Godric Wilkie * DX7 programs : Dave Benson, Frank Carvalho, Tim Conrardy, Jack Deckard, Chris Dodunski, Tim Garrett, Hitaye, Stephan Ibsen, Christian Jezreel, Narfman, Godric Wilkie
* markusthegeek direct implication for this project * markusthegeek direct implication for this project
@ -144,6 +141,7 @@ TODO - Dexed
------------ ------------
* Yamaha 4 operators (DX21/DX27/DX100) sysex import * Yamaha 4 operators (DX21/DX27/DX100) sysex import
* Various code cleanup * Various code cleanup
* AU Version
TODO - msfa TODO - msfa
----------- -----------

@ -6,20 +6,20 @@
* Copyright (C) 2002 Juan Linietsky <coding@reduz.com.ar> * Copyright (C) 2002 Juan Linietsky <coding@reduz.com.ar>
* Copyright (C) 2006 Mark-André Hopf <mhopf@mark13.org> * Copyright (C) 2006 Mark-André Hopf <mhopf@mark13.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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 "DXComponents.h" #include "DXComponents.h"
@ -425,7 +425,7 @@ void VuMeter::paint(Graphics &g) {
g.setColour (Colours::black); g.setColour (Colours::black);
g.fillRoundedRectangle (0.0f, 0.0f, (float) width, (float) height, 0); g.fillRoundedRectangle (0.0f, 0.0f, (float) width, (float) height, 0);
const int numBlocks = roundToInt (totalBlocks * v); const int numBlocks = roundToInt(totalBlocks * v);
const float h = (height - 6.0f) / (float) totalBlocks; const float h = (height - 6.0f) / (float) totalBlocks;
for (int i = 0; i < totalBlocks; ++i) { for (int i = 0; i < totalBlocks; ++i) {

@ -2,20 +2,20 @@
* *
* Copyright (c) 2014 Pascal Gauthier. * Copyright (c) 2014 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef DXCOMPONENTS_H_INCLUDED #ifndef DXCOMPONENTS_H_INCLUDED

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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.
* *
* 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 "DXLookNFeel.h" #include "DXLookNFeel.h"

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef DXLOOKNFEEL_H_INCLUDED #ifndef DXLOOKNFEEL_H_INCLUDED

@ -2,20 +2,20 @@
* *
* Copyright (c) 2014 Pascal Gauthier. * Copyright (c) 2014 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef DEXED_H_INCLUDED #ifndef DEXED_H_INCLUDED

@ -1,202 +1,123 @@
/* /**
============================================================================== *
* Copyright (c) 2014 Pascal Gauthier.
EngineMkI.cpp *
Created: 25 Aug 2014 12:08:00am * This program is free software; you can redistribute it and/or modify
Author: Pascal Gauthier * 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 "EngineMkI.h" #include "EngineMkI.h"
#include <math.h> #include <math.h>
#include <cstdlib> #include <cstdlib>
#include "sin.h" #include "msfa/sin.h"
#include "msfa/exp2.h"
void EngineMkI::compute(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers) {
const int kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (int op = 0; op < 6; op++) { void EngineMkI::compute(int32_t *output, const int32_t *input,
int flags = alg.ops[op]; int32_t phase0, int32_t freq,
bool add = (flags & OUT_BUS_ADD) != 0; int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) {
FmOpParams &param = params[op]; int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int inbus = (flags >> 4) & 3; int32_t gain = gain1;
int outbus = flags & 3; int32_t phase = phase0;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain[0];
int32_t gain2 = param.gain[1];
/*
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// PG: this is my 'dirty' implementation of FB for 2 and 3 operators...
// still needs some tuning...
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
switch ( algorithm ) {
// two operator feedback, process exception for ALGO 6
case 5 :
FmOpKernel::compute_fb2(outptr, params, fb_buf, feedback_shift);
param.phase += param.freq << LG_N;
params[1].phase += param.freq + 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 3 :
FmOpKernel::compute_fb3(outptr, params, fb_buf, feedback_shift);
param.phase += param.freq << LG_N;
params[1].phase += param.freq + params[1].freq << LG_N; // hack, we already processed op-5 - op-4
params[2].phase += param.freq + params[1].freq + params[2].freq << LG_N; // yuk yuk
op += 2; // ignore the 2 other operators
break;
default:
// one operator feedback, normal proces
//cout << "\t" << op << " fb " << inbus << outbus << add << endl;
FmOpKernel::compute_fb(outptr, param.phase, param.freq,gain1, gain2, fb_buf, feedback_shift, add);
param.phase += param.freq << LG_N;
break;
}
has_contents[outbus] = true;
continue;
} 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;
}
}
/*
void FmOpKernel::compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) {
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) { if (add) {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t y = Sin::lookup(phase + input[i]); int32_t y = Sin::lookup(phase + input[i]);
y &= controllers->sinBitFilter; y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1; output[i] += y1;
phase += freq; phase += freq;
} }
} else { } else {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t y = Sin::lookup(phase + input[i]); int32_t y = Sin::lookup(phase + input[i]);
y &= controllers->sinBitFilter; y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1; output[i] = y1;
phase += freq; phase += freq;
} }
} }
}
} }
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, void EngineMkI::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) { int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1; int32_t gain = gain1;
int32_t phase = phase0; 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) { if (add) {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t y = Sin::lookup(phase); int32_t y = Sin::lookup(phase);
y &= controllers->sinBitFilter; y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1; output[i] += y1;
phase += freq; phase += freq;
} }
} else { } else {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t y = Sin::lookup(phase); int32_t y = Sin::lookup(phase);
y &= controllers->sinBitFilter; y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1; output[i] = y1;
phase += freq; phase += freq;
} }
} }
}
} }
#define noDOUBLE_ACCURACY void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
#define HIGH_ACCURACY int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add, const Controllers *controllers) {
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain1, int32_t gain2, int32_t gain = gain1;
int32_t *fb_buf, int fb_shift, bool add, const Controllers *controllers) { int32_t phase = phase0;
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; int32_t y0 = fb_buf[0];
int32_t gain = gain1; int32_t y = fb_buf[1];
int32_t phase = phase0; if (add) {
int32_t y0 = fb_buf[0]; for (int i = 0; i < N; i++) {
int32_t y = fb_buf[1]; gain += dgain;
if (add) { int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
for (int i = 0; i < N; i++) { y0 = y;
gain += dgain; y = Sin::lookup(phase + scaled_fb);
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); y &= controllers->sinBitFilter;
y0 = y; y = ((int64_t)y * (int64_t)gain) >> 24;
y = Sin::lookup(phase + scaled_fb); output[i] += y;
y &= controllers->sinBitFilter; phase += freq;
y = ((int64_t)y * (int64_t)gain) >> 24; }
output[i] += y; } else {
phase += freq; for (int i = 0; i < N; i++) {
} gain += dgain;
} else { int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
for (int i = 0; i < N; i++) { y0 = y;
gain += dgain; y = Sin::lookup(phase + scaled_fb);
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); y &= controllers->sinBitFilter;
y0 = y; y = ((int64_t)y * (int64_t)gain) >> 24;
y = Sin::lookup(phase + scaled_fb); output[i] = y;
y &= controllers->sinBitFilter; phase += freq;
y = ((int64_t)y * (int64_t)gain) >> 24; }
output[i] = y;
phase += freq;
} }
} fb_buf[0] = y0;
fb_buf[0] = y0; fb_buf[1] = y;
fb_buf[1] = y;
} }
/*
// exclusively used for ALGO 6 with feedback // 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) { void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t *fb_buf, int fb_shift, const Controllers *cont) {
int32_t dgain[2]; int32_t dgain[2];
int32_t gain[2]; int32_t gain[2];
int32_t phase[2]; int32_t phase[2];
@ -235,7 +156,7 @@ void FmOpKernel::compute_fb2(int32_t *output, FmOpParams *parms, int32_t *fb_buf
} }
// exclusively used for ALGO 4 with feedback // 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) { void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t *fb_buf, int fb_shift, const Controllers *conts) {
int32_t dgain[3]; int32_t dgain[3];
int32_t gain[3]; int32_t gain[3];
int32_t phase[3]; int32_t phase[3];
@ -283,5 +204,79 @@ void FmOpKernel::compute_fb3(int32_t *output, FmOpParams *parms, int32_t *fb_buf
fb_buf[0] = y0; fb_buf[0] = y0;
fb_buf[1] = y; fb_buf[1] = y;
} }
*/ */
void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers) {
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 &param = 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]) {
// PG: this is my 'dirty' implementation of FB for 2 and 3 operators...
// still needs some tuning...
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
switch ( algorithm ) {
/* // two operator feedback, process exception for ALGO 6
case 5 :
EngineMkI::compute_fb2(outptr, params, fb_buf, feedback_shift);
param.phase += param.freq << LG_N;
params[1].phase += param.freq + 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 3 :
FmOpKernel::compute_fb3(outptr, params, fb_buf, feedback_shift);
param.phase += param.freq << LG_N;
params[1].phase += param.freq + params[1].freq << LG_N; // hack, we already processed op-5 - op-4
params[2].phase += param.freq + params[1].freq + params[2].freq << LG_N; // yuk yuk
op += 2; // ignore the 2 other operators
break;*/
default:
// one operator feedback, normal proces
//cout << "\t" << op << " fb " << inbus << outbus << add << endl;
compute_fb(outptr, param.phase, param.freq,gain1, gain2, fb_buf, feedback_shift, add, controllers);
param.phase += param.freq << LG_N;
break;
}
has_contents[outbus] = true;
continue;
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
compute_pure(outptr, param.phase, param.freq, gain1, gain2, add, controllers);
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add, controllers);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}
param.phase += param.freq << LG_N;
}
}

@ -1,12 +1,22 @@
/* /**
============================================================================== *
* Copyright (c) 2014 Pascal Gauthier.
EngineMkI.h *
Created: 25 Aug 2014 12:08:00am * This program is free software; you can redistribute it and/or modify
Author: Pascal Gauthier * 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
*
*/
#ifndef ENGINEMKI_H_INCLUDED #ifndef ENGINEMKI_H_INCLUDED
#define ENGINEMKI_H_INCLUDED #define ENGINEMKI_H_INCLUDED
@ -20,12 +30,21 @@
class EngineMkI : public FmCore { class EngineMkI : public FmCore {
public: public:
virtual void compute(int32_t *output, FmOpParams *params, int algorithm, virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers); int32_t *fb_buf, int feedback_shift, const Controllers *controllers);
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2,
bool add, const Controllers *controllers);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2,
bool add, const Controllers *controllers);
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); 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); void compute_fb3(int32_t *output, FmOpParams *params, int32_t *fb_buf, int fb_shift, const Controllers *controllers);
}; };

@ -1,31 +1,31 @@
/* /*
* Copyright (C) 2014 Pascal Gauthier * Copyright (C) 2014 Pascal Gauthier.
* Copyright (C) 2012 Steffen Ohrendorf <steffen.ohrendorf@gmx.de> * Copyright (C) 2012 Steffen Ohrendorf <steffen.ohrendorf@gmx.de>
* *
* This library is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU Lesser General Public * it under the terms of the GNU General Public License as published by
* License as published by the Free Software Foundation; either * the Free Software Foundation; either version 3 of the License, or
* version 3 of the License, or (at your option) any later version. * (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Lesser General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU General Public License
* License along with this library; if not, write to the Free Software * along with this program; if not, write to the Free Software Foundation,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* *
* Original Java Code: Copyright (C) 2008 Robson Cozendey <robson@cozendey.com> * Original Java Code: Copyright (C) 2008 Robson Cozendey <robson@cozendey.com>
* *
* Some code based on forum posts in: http://forums.submarine.org.uk/phpBB/viewforum.php?f=9, * Some code based on forum posts in: http://forums.submarine.org.uk/phpBB/viewforum.php?f=9,
* Copyright (C) 2010-2013 by carbon14 and opl3 * Copyright (C) 2010-2013 by carbon14 and opl3
*/ *
*/
#include "PluginProcessor.h"
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; #include "EngineOpl.h"
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0};
uint16_t SignBit = 0x8000; uint16_t SignBit = 0x8000;
@ -80,11 +80,10 @@ inline uint16_t sinLog( uint16_t phi ) {
case 0x0200: case 0x0200:
// rising quarter wave -ve Shape C // rising quarter wave -ve Shape C
return sinLogTable[index] | SignBit; return sinLogTable[index] | SignBit;
case 0x0300: default:
// falling quarter wave -ve Shape D // falling quarter wave -ve Shape D
return sinLogTable[index ^ 0xFF] | SignBit; return sinLogTable[index ^ 0xFF] | SignBit;
} }
return 0;
} }
// 16 env units are ~3dB and halve the output // 16 env units are ~3dB and halve the output
@ -95,7 +94,7 @@ inline uint16_t sinLog( uint16_t phi ) {
* @warning @a env will not be checked for correct values. * @warning @a env will not be checked for correct values.
*/ */
inline int16_t oplSin( uint16_t phase, uint16_t env ) { inline int16_t oplSin( uint16_t phase, uint16_t env ) {
uint16_t expVal = sinLog(phase) + (env << 3); uint16_t expVal = sinLog(phase) + (env << 3);
const bool isSigned = expVal & SignBit; const bool isSigned = expVal & SignBit;
expVal &= ~SignBit; expVal &= ~SignBit;
@ -113,61 +112,61 @@ inline int16_t oplSin( uint16_t phase, uint16_t env ) {
} }
} }
void EngineOpl::computeOpl(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) { void EngineOpl::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 dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1; int32_t gain = gain1;
int32_t phase = phase0; int32_t phase = phase0;
const int32_t *adder = add ? output : zeros; const int32_t *adder = add ? output : zeros;
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t y = oplSin( (phase+input[i]) >> 14, gain1); int32_t y = oplSin( (phase+input[i]) >> 14, gain);
output[i] = (y << 15) + adder[i]; output[i] = (y << 14) + adder[i];
phase += freq; phase += freq;
} }
} }
void EngineOpl::computeOpl_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) { void EngineOpl::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 dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1; int32_t gain = gain1;
int32_t phase = phase0; int32_t phase = phase0;
const int32_t *adder = add ? output : zeros; const int32_t *adder = add ? output : zeros;
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t y = oplSin( phase >> 14, gain1); int32_t y = oplSin( phase >> 14, gain);
output[i] = (y << 15) + adder[i]; output[i] = (y << 14) + adder[i];
phase += freq; phase += freq;
} }
} }
void EngineOpl::computeOpl_fb(int32_t *output, int32_t phase0, int32_t freq, void EngineOpl::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_shift, bool add) { int32_t *fb_buf, int fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1; int32_t gain = gain1;
int32_t phase = phase0; int32_t phase = phase0;
const int32_t *adder = add ? output : zeros; const int32_t *adder = add ? output : zeros;
int32_t y0 = fb_buf[0]; int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1]; int32_t y = fb_buf[1];
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
gain += dgain; gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y; y0 = y;
int32_t y = oplSin( (phase+scaled_fb) >> 14, gain1); int32_t y = oplSin( (phase+scaled_fb) >> 14, gain) << 14;
output[i] = (y << 15) + adder[i]; output[i] = y + adder[i];
phase += freq; phase += freq;
} }
fb_buf[0] = y0; fb_buf[0] = y0;
fb_buf[1] = y; fb_buf[1] = y;
} }
void EngineOpl::compute(int32_t *output, FmOpParams *params, int algorithm, void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers) { int32_t *fb_buf, int feedback_shift, const Controllers *controllers) {
const int kLevelThresh = 505; // really ???? const int kLevelThresh = 507; // really ????
const FmAlgorithm alg = algorithms[algorithm]; const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false }; bool has_contents[3] = { true, false, false };
for (int op = 0; op < 6; op++) { for (int op = 0; op < 6; op++) {
@ -177,8 +176,10 @@ void EngineOpl::compute(int32_t *output, FmOpParams *params, int algorithm,
int inbus = (flags >> 4) & 3; int inbus = (flags >> 4) & 3;
int outbus = flags & 3; int outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = 512-(param.level[0] >> 19); int32_t gain1 = param.gain_out == 0 ? 511 : param.gain_out;
int32_t gain2 = 512-(param.level[1] >> 19); int32_t gain2 = 512-(param.level_in >> 19);
param.gain_out = gain2;
if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) { if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) {
if (!has_contents[outbus]) { if (!has_contents[outbus]) {
add = false; add = false;
@ -187,17 +188,17 @@ void EngineOpl::compute(int32_t *output, FmOpParams *params, int algorithm,
// todo: more than one op in a feedback loop // todo: more than one op in a feedback loop
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) { if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl; // cout << op << " fb " << inbus << outbus << add << endl;
computeOpl_fb(outptr, param.phase, param.freq, compute_fb(outptr, param.phase, param.freq,
gain1, gain2, gain1, gain2,
fb_buf, feedback_shift, add); fb_buf, feedback_shift, add);
} else { } else {
// cout << op << " pure " << inbus << outbus << add << endl; // cout << op << " pure " << inbus << outbus << add << endl;
computeOpl_pure(outptr, param.phase, param.freq, compute_pure(outptr, param.phase, param.freq,
gain1, gain2, add); gain1, gain2, add);
} }
} else { } else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl; // cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
computeOpl(outptr, buf_[inbus - 1].get(), compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add); param.phase, param.freq, gain1, gain2, add);
} }
has_contents[outbus] = true; has_contents[outbus] = true;

@ -8,8 +8,8 @@
============================================================================== ==============================================================================
*/ */
#ifndef EXTRAKERNELS_H_INCLUDED #ifndef ENGINEOPL_H_INCLUDED
#define EXTRAKERNELS_H_INCLUDED #define ENGINEOPL_H_INCLUDED
#include "synth.h" #include "synth.h"
#include "aligned_buf.h" #include "aligned_buf.h"
@ -20,15 +20,15 @@
class EngineOpl : public FmCore { class EngineOpl : public FmCore {
public: public:
virtual void compute(int32_t *output, FmOpParams *params, int algorithm, virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers); int32_t *fb_buf, int feedback_shift, const Controllers *controllers);
void computeOpl(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add); void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void computeOpl_pure(int32_t *output, 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 computeOpl_fb(int32_t *output, int32_t phase0, int32_t freq, 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); int32_t *fb_buf, int fb_gain, bool add);
}; };
#endif // EXTRAKERNELS_H_INCLUDED #endif // ENGINEOPL_H_INCLUDED

@ -2,20 +2,20 @@
* *
* Copyright (c) 2014 Pascal Gauthier. * Copyright (c) 2014 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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 <time.h> #include <time.h>
@ -253,7 +253,7 @@ void DexedAudioProcessor::resetToInitVoice() {
99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7,
99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 99, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 99, 0, 1, 0, 7,
99, 99, 99, 99, 50, 50, 50, 50, 0, 0, 1, 35, 0, 0, 0, 1, 0, 3, 24, 99, 99, 99, 99, 50, 50, 50, 50, 0, 0, 1, 35, 0, 0, 0, 1, 0, 3, 24,
73, 78, 73, 84, 32, 86, 79, 73, 67, 69} ; 73, 78, 73, 84, 32, 86, 79, 73, 67, 69 };
for(int i=0;i<sizeof(init_voice);i++) { for(int i=0;i<sizeof(init_voice);i++) {
data[i] = init_voice[i]; data[i] = init_voice[i];
@ -277,6 +277,8 @@ void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) {
dexedState.setAttribute("reso", fx.uiReso); dexedState.setAttribute("reso", fx.uiReso);
dexedState.setAttribute("gain", fx.uiGain); dexedState.setAttribute("gain", fx.uiGain);
dexedState.setAttribute("currentProgram", currentProgram); dexedState.setAttribute("currentProgram", currentProgram);
dexedState.setAttribute("monoMode", monoMode);
dexedState.setAttribute("engineType", (int) engineType);
char sysex_blob[4104]; char sysex_blob[4104];
exportSysexCart((char *) &sysex_blob, (char *) sysex, 0); exportSysexCart((char *) &sysex_blob, (char *) sysex, 0);
@ -306,6 +308,9 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte
fx.uiGain = root->getDoubleAttribute("gain"); fx.uiGain = root->getDoubleAttribute("gain");
currentProgram = root->getIntAttribute("currentProgram"); currentProgram = root->getIntAttribute("currentProgram");
setEngineType(root->getIntAttribute("engineType", 0));
monoMode = root->getIntAttribute("monoMode", 0);
XmlElement *dexedBlob = root->getChildByName("dexedBlob"); XmlElement *dexedBlob = root->getChildByName("dexedBlob");
if ( dexedBlob == NULL ) { if ( dexedBlob == NULL ) {
TRACE("dexedBlob element not found"); TRACE("dexedBlob element not found");

@ -2,20 +2,20 @@
* *
* Copyright (c) 2014 Pascal Gauthier. * Copyright (c) 2014 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef PLUGINDATA_H_INCLUDED #ifndef PLUGINDATA_H_INCLUDED

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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 "PluginProcessor.h" #include "PluginProcessor.h"
@ -147,7 +147,7 @@ DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* owner
initButton->addListener(this); initButton->addListener(this);
initButton->setBounds(385, 6, 50, 18); initButton->setBounds(385, 6, 50, 18);
addAndMakeVisible(monoButton = new TextButton("MONO")); addAndMakeVisible(monoButton = new ToggleButton("MONO"));
monoButton->setButtonText("MONO"); monoButton->setButtonText("MONO");
monoButton->addListener(this); monoButton->addListener(this);
monoButton->setBounds(439, 6, 50, 18); monoButton->setBounds(439, 6, 50, 18);
@ -213,6 +213,8 @@ DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* owner
sendPopup.addItem(1, "Send program to DX7"); sendPopup.addItem(1, "Send program to DX7");
sendPopup.addItem(2, "Send cartridge to DX7"); sendPopup.addItem(2, "Send cartridge to DX7");
monoButton->setState(processor->isMonoMode() ? Button::ButtonState::buttonDown : Button::ButtonState::buttonNormal);
updateUI(); updateUI();
startTimer(100); startTimer(100);
} }
@ -358,6 +360,12 @@ void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) {
return; return;
} }
if (buttonThatWasClicked == monoButton ) {
processor->setMonoMode(!processor->isMonoMode());
monoButton->setState(processor->isMonoMode() ? Button::ButtonState::buttonDown : Button::ButtonState::buttonNormal);
return;
}
if (buttonThatWasClicked == aboutButton) { if (buttonThatWasClicked == aboutButton) {
AboutBox about(this); AboutBox about(this);
about.runModalLoop(); about.runModalLoop();

@ -2,20 +2,19 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public * You should have received a copy of the GNU General Public License
* License along with this program; if not, write to the Free * along with this program; if not, write to the Free Software Foundation,
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Boston, MA 02110-1301 USA.
*/ */
#ifndef PLUGINEDITOR_H_INCLUDED #ifndef PLUGINEDITOR_H_INCLUDED
@ -52,7 +51,7 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor,
ScopedPointer<TextButton> settingsButton; ScopedPointer<TextButton> settingsButton;
ScopedPointer<TextButton> sendButton; ScopedPointer<TextButton> sendButton;
ScopedPointer<TextButton> initButton; ScopedPointer<TextButton> initButton;
ScopedPointer<TextButton> monoButton; ScopedPointer<ToggleButton> monoButton;
ScopedPointer<Component> midiMonitor; ScopedPointer<Component> midiMonitor;
void storeProgram(); void storeProgram();

@ -6,20 +6,20 @@
* Filter taken from the Obxd project : * Filter taken from the Obxd project :
* https://github.com/2DaT/Obxd * https://github.com/2DaT/Obxd
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef PLUGINFX_H_INCLUDED #ifndef PLUGINFX_H_INCLUDED

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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 <time.h> #include <time.h>

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef PLUGINPARAM_H_INCLUDED #ifndef PLUGINPARAM_H_INCLUDED

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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 "PluginProcessor.h" #include "PluginProcessor.h"
@ -55,7 +55,6 @@ DexedAudioProcessor::DexedAudioProcessor() {
sendSysexChange = true; sendSysexChange = true;
normalizeDxVelocity = false; normalizeDxVelocity = false;
sysexComm.listener = this; sysexComm.listener = this;
keyboardState.addListener(&sysexComm);
engineType = -1; engineType = -1;
memset(&voiceStatus, 0, sizeof(VoiceStatus)); memset(&voiceStatus, 0, sizeof(VoiceStatus));
@ -148,9 +147,7 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi
refreshVoice = false; refreshVoice = false;
} }
// Now pass any incoming midi messages to our keyboard state object, and let it keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true);
// add messages to the buffer if the user is clicking on the on-screen keys
keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
MidiBuffer::Iterator it(midiMessages); MidiBuffer::Iterator it(midiMessages);
hasMidiMessage = it.getNextEvent(*nextMidi,midiEventPos); hasMidiMessage = it.getNextEvent(*nextMidi,midiEventPos);
@ -322,42 +319,86 @@ void DexedAudioProcessor::keydown(uint8_t pitch, uint8_t velo) {
return; return;
} }
pitch += (data[144] - 24); pitch += data[144] - 24;
if ( normalizeDxVelocity ) { if ( normalizeDxVelocity ) {
velo = ((float)velo) * 0.7874015; // 100/127 velo = ((float)velo) * 0.7874015; // 100/127
} }
int note = currentNote; int note = currentNote;
for (int i = 0; i < MAX_ACTIVE_NOTES; i++) { for (int i=0; i<MAX_ACTIVE_NOTES; i++) {
if (!voices[note].keydown) { if (!voices[note].keydown) {
currentNote = (note + 1) % MAX_ACTIVE_NOTES; currentNote = (note + 1) % MAX_ACTIVE_NOTES;
lfo.keydown(); // TODO: should only do this if # keys down was 0 lfo.keydown(); // TODO: should only do this if # keys down was 0
voices[note].midi_note = pitch; voices[note].midi_note = pitch;
voices[note].keydown = true;
voices[note].sustained = sustain; voices[note].sustained = sustain;
voices[note].live = true; voices[note].keydown = true;
voices[note].dx7_note->init(data, pitch, velo); voices[note].dx7_note->init(data, pitch, velo);
return; break;
} }
note = (note + 1) % MAX_ACTIVE_NOTES; note = (note + 1) % MAX_ACTIVE_NOTES;
} }
if ( monoMode ) {
for(int i=0; i<MAX_ACTIVE_NOTES; i++) {
if ( voices[i].live ) {
// all keys are up, don't transfert anything
if ( ! voices[i].keydown ) {
voices[i].live = false;
break;
}
if ( voices[i].midi_note < pitch ) {
voices[i].live = false;
voices[note].dx7_note->transfertState(*voices[i].dx7_note);
break;
}
return;
}
}
}
voices[note].live = true;
} }
void DexedAudioProcessor::keyup(uint8_t pitch) { void DexedAudioProcessor::keyup(uint8_t pitch) {
pitch += (data[144] - 24); pitch += data[144] - 24;
for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { int note;
if (voices[note].midi_note == pitch && voices[note].keydown) { for (note=0; note<MAX_ACTIVE_NOTES; ++note) {
if (sustain) { if ( voices[note].midi_note == pitch && voices[note].keydown ) {
voices[note].sustained = true;
} else {
voices[note].dx7_note->keyup();
}
voices[note].keydown = false; voices[note].keydown = false;
break;
} }
} }
// note not found ?
if ( note == 16 ) {
TRACE("note-off not found???");
return;
}
if ( monoMode ) {
int highNote = -1;
int target = 0;
for (int i=0; i<MAX_ACTIVE_NOTES;i++) {
if ( voices[i].keydown && voices[i].midi_note > highNote ) {
target = i;
highNote = voices[i].midi_note;
}
}
if ( highNote != -1 ) {
voices[note].live = false;
voices[target].live = true;
voices[target].dx7_note->transfertState(*voices[note].dx7_note);
}
}
if ( sustain ) {
voices[note].sustained = true;
} else {
voices[note].dx7_note->keyup();
}
} }
void DexedAudioProcessor::panic() { void DexedAudioProcessor::panic() {
@ -421,7 +462,6 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid
forceRefreshUI = true; forceRefreshUI = true;
} }
int DexedAudioProcessor::getEngineType() { int DexedAudioProcessor::getEngineType() {
return engineType; return engineType;
} }
@ -443,6 +483,11 @@ void DexedAudioProcessor::setEngineType(int tp) {
engineType = tp; engineType = tp;
} }
void DexedAudioProcessor::setMonoMode(bool mode) {
panic();
monoMode = mode;
}
// ==================================================================== // ====================================================================
bool DexedAudioProcessor::peekVoiceStatus() { bool DexedAudioProcessor::peekVoiceStatus() {
if ( currentNote == -1 ) if ( currentNote == -1 )

@ -2,20 +2,20 @@
* *
* Copyright (c) 2013 Pascal Gauthier. * Copyright (c) 2013 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef PLUGINPROCESSOR_H_INCLUDED #ifndef PLUGINPROCESSOR_H_INCLUDED
@ -62,6 +62,7 @@ class DexedAudioProcessor : public AudioProcessor, public AsyncUpdater, public
Lfo lfo; Lfo lfo;
bool sustain; bool sustain;
bool monoMode;
// Extra buffering for when GetSamples wants a buffer not a multiple of N // Extra buffering for when GetSamples wants a buffer not a multiple of N
float extra_buf[N]; float extra_buf[N];
@ -165,6 +166,10 @@ public :
void releaseResources(); void releaseResources();
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages);
void panic(); void panic();
bool isMonoMode() {
return monoMode;
}
void setMonoMode(bool mode);
//============================================================================== //==============================================================================
AudioProcessorEditor* createEditor(); AudioProcessorEditor* createEditor();

@ -2,20 +2,20 @@
* *
* Copyright (c) 2014 Pascal Gauthier. * Copyright (c) 2014 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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 "SysexComm.h" #include "SysexComm.h"
@ -30,6 +30,7 @@ SysexComm::SysexComm() {
input = NULL; input = NULL;
output = NULL; output = NULL;
inputOutput = false;
} }
SysexComm::~SysexComm() { SysexComm::~SysexComm() {
@ -51,7 +52,9 @@ bool SysexComm::setInput(String target) {
input->stop(); input->stop();
delete input; delete input;
input = NULL; input = NULL;
} }
inputOutput = false;
if ( listener == NULL ) if ( listener == NULL )
return true; return true;
@ -76,6 +79,10 @@ bool SysexComm::setInput(String target) {
inputName = target; inputName = target;
TRACE("sysex %s opened", target.toRawUTF8()); TRACE("sysex %s opened", target.toRawUTF8());
input->start(); input->start();
if ( output )
inputOutput = true;
return true; return true;
} }
@ -88,6 +95,7 @@ bool SysexComm::setOutput(String target) {
delete output; delete output;
output = NULL; output = NULL;
} }
inputOutput = false;
StringArray devices = MidiOutput::getDevices(); StringArray devices = MidiOutput::getDevices();
int idx = devices.indexOf(target); int idx = devices.indexOf(target);
@ -107,6 +115,10 @@ bool SysexComm::setOutput(String target) {
} }
outputName = target; outputName = target;
if ( input )
inputOutput = true;
TRACE("sysex %s opened", target.toRawUTF8()); TRACE("sysex %s opened", target.toRawUTF8());
return true; return true;
} }
@ -136,17 +148,11 @@ int SysexComm::send(const MidiMessage &message) {
return 0; return 0;
} }
// This is called from the UI Keyboard... void SysexComm::playBuffer(MidiBuffer &keyboardEvents, int numSamples ) {
void SysexComm::handleNoteOn(MidiKeyboardState*, int, int midiNoteNumber, float velocity) { noteOutput.addEvents(keyboardEvents, 0, numSamples, 0);
if ( output == NULL )
return;
outActivity = true;
char iVelo = velocity * 100;
MidiMessage msg(0x90 + sysexChl, midiNoteNumber, iVelo);
output->sendMessageNow(msg);
} }
void SysexComm::handleNoteOff(MidiKeyboardState* source, int midiChannel, int midiNoteNumber) { void SysexComm::handleAsyncUpdate() {
handleNoteOn(source, midiChannel, midiNoteNumber, 0);
} }

@ -2,20 +2,20 @@
* *
* Copyright (c) 2014 Pascal Gauthier. * Copyright (c) 2014 Pascal Gauthier.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or modify
* modify it under the terms of the GNU General Public License as * it under the terms of the GNU General Public License as published by
* published by the Free Software Foundation; either version 2 of * the Free Software Foundation; either version 3 of the License, or
* the License, or (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be * This program is distributed in the hope that it will be useful,
* useful, but WITHOUT ANY WARRANTY; without even the implied * but WITHOUT ANY WARRANTY; without even the implied warranty of
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* PURPOSE. See the GNU General Public License for more details. * 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
* *
* 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.
*/ */
#ifndef SYSEXCOMM_H_INCLUDED #ifndef SYSEXCOMM_H_INCLUDED
@ -23,15 +23,20 @@
#include "../JuceLibraryCode/JuceHeader.h" #include "../JuceLibraryCode/JuceHeader.h"
class SysexComm : public MidiKeyboardStateListener { class SysexComm : public AsyncUpdater {
MidiInput *input; MidiInput *input;
MidiOutput *output; MidiOutput *output;
String inputName; String inputName;
String outputName; String outputName;
int sysexChl; int sysexChl;
bool inputOutput;
MidiBuffer noteOutput;
public : public :
MidiInputCallback *listener; MidiInputCallback *listener;
bool inActivity;
bool outActivity;
SysexComm(); SysexComm();
~SysexComm(); ~SysexComm();
@ -46,14 +51,14 @@ public :
bool isInputActive(); bool isInputActive();
bool isOutputActive(); bool isOutputActive();
inline bool isInputOutputActive() {
return inputOutput;
}
int send(const MidiMessage& message); int send(const MidiMessage& message);
void handleNoteOn(MidiKeyboardState* source, int midiChannel, int midiNoteNumber, float velocity); void handleAsyncUpdate();
void handleNoteOff(MidiKeyboardState* source, int midiChannel, int midiNoteNumber); void playBuffer(MidiBuffer &keyboardEvents, int numSamples);
bool inActivity;
bool outActivity;
}; };
#endif // SYSEXCOMM_H_INCLUDED #endif // SYSEXCOMM_H_INCLUDED

@ -170,8 +170,7 @@ void Dx7Note::init(const char patch[156], int midinote, int velocity) {
basepitch_[op] = freq; basepitch_[op] = freq;
// cout << op << " freq: " << freq << endl; // cout << op << " freq: " << freq << endl;
params_[op].phase = 0; params_[op].phase = 0;
params_[op].gain[1] = 0; params_[op].gain_out = 0;
params_[op].level[1] = 0;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
} }
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -187,8 +186,7 @@ void Dx7Note::init(const char patch[156], int midinote, int velocity) {
ampmoddepth_ = (patch[140] * 165) >> 6; ampmoddepth_ = (patch[140] * 165) >> 6;
} }
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) {
const Controllers *ctrls) {
int32_t pitchmod = pitchenv_.getsample(); int32_t pitchmod = pitchenv_.getsample();
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32 uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
// TODO(PG) : make this integer friendly // TODO(PG) : make this integer friendly
@ -214,29 +212,19 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
pitchmod += pb; pitchmod += pb;
for (int op = 0; op < 6; op++) { for (int op = 0; op < 6; op++) {
// PG: MEEEEEH, this needs to be cleanup. The amp mod is way better
// with the exp2 value than the original level that contains a fraction
// into the integer ?
//
// TODO: to clean up ! The AMD should be calculated on the level, not the gain
params_[op].gain[0] = params_[op].gain[1];
params_[op].level[0] = params_[op].level[1];
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24))); //int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod); params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod);
int32_t level = env_[op].getsample(); int32_t level = env_[op].getsample();
params_[op].level[1] = level;
int32_t gain = Exp2::lookup(level - (14 * (1 << 24)));
if (ampmodsens_[op] != 0) { if (ampmodsens_[op] != 0) {
uint32_t sensamp = ((int64_t) ampmodsens_[op]) * ((int64_t) gain) >> 24; /*uint32_t sensamp = ((int64_t) ampmodsens_[op]) * ((int64_t) gain) >> 24;
sensamp = ((int64_t) sensamp) * ((int64_t) lfo_val) >> 24; sensamp = ((int64_t) sensamp) * ((int64_t) lfo_val) >> 24;
uint32_t amd_level = (((int64_t) amd) * (int64_t) sensamp) >> 24; uint32_t amd_level = (((int64_t) amd) * (int64_t) sensamp) >> 24;
gain -= amd_level; gain -= amd_level;*/
} }
params_[op].gain[1] = gain; params_[op].level_in = level;
} }
ctrls->core->compute(buf, params_, algorithm_, fb_buf_, fb_shift_, ctrls); ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_, ctrls);
} }
void Dx7Note::keyup() { void Dx7Note::keyup() {
@ -266,10 +254,18 @@ void Dx7Note::update(const char patch[156], int midinote) {
void Dx7Note::peekVoiceStatus(VoiceStatus &status) { void Dx7Note::peekVoiceStatus(VoiceStatus &status) {
for(int i=0;i<6;i++) { for(int i=0;i<6;i++) {
status.amp[i] = params_[i].gain[1]; status.amp[i] = Exp2::lookup(params_[i].level_in - (14 * (1 << 24)));
env_[i].getPosition(&status.ampStep[i]); env_[i].getPosition(&status.ampStep[i]);
} }
pitchenv_.getPosition(&status.pitchStep); pitchenv_.getPosition(&status.pitchStep);
} }
/**
* Used in monophonic mode to transfert voice state from different notes
*/
void Dx7Note::transfertState(Dx7Note &src) {
for (int i=0;i<6;i++) {
env_[i] = src.env_[i];
}
}

@ -52,6 +52,7 @@ class Dx7Note {
// PG:add the update // PG:add the update
void update(const char patch[156], int midinote); void update(const char patch[156], int midinote);
void peekVoiceStatus(VoiceStatus &status); void peekVoiceStatus(VoiceStatus &status);
void transfertState(Dx7Note& src);
private: private:
Env env_[6]; Env env_[6];

@ -19,12 +19,13 @@
#include "synth.h" #include "synth.h"
#include "env.h" #include "env.h"
#include "../Dexed.h"
//using namespace std; //using namespace std;
uint32_t Env::sr_multiplier = (1<<24); uint32_t Env::sr_multiplier = (1<<24);
void Env::init_sr(double sampleRate) { void Env::init_sr(double sampleRate) {
sr_multiplier = (44100 / sampleRate) * (1<<23); sr_multiplier = (44100.0 / sampleRate) * (1<<24);
} }
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) {
@ -106,10 +107,28 @@ void Env::advance(int newix) {
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2)); inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2));
// meh, this should be fixed elsewhere // meh, this should be fixed elsewhere
inc_ = ((int64_t)inc_ * (int64_t)sr_multiplier) >> 23; inc_ = ((int64_t)inc_ * (int64_t)sr_multiplier) >> 24;
} }
} }
void Env::getPosition(char *step) { void Env::getPosition(char *step) {
*step = ix_; *step = ix_;
} }
Env& Env::operator=(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_;
inc_ = src.inc_;
down_ = src.down_;
return *this;
}

@ -45,6 +45,7 @@ class Env {
void getPosition(char *step); void getPosition(char *step);
static void init_sr(double sample_rate); static void init_sr(double sample_rate);
Env& operator=(Env &src);
private: private:
// PG: This code is normalized to 44100, need to put a multiplier // PG: This code is normalized to 44100, need to put a multiplier

@ -19,6 +19,7 @@
#endif #endif
#include "synth.h" #include "synth.h"
#include "exp2.h"
#include "fm_op_kernel.h" #include "fm_op_kernel.h"
#include "fm_core.h" #include "fm_core.h"
@ -89,8 +90,8 @@ void FmCore::dump() {
#endif #endif
} }
void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm, void FmCore::render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controller) { int32_t *fb_buf, int feedback_shift, const Controllers *controller) {
const int kLevelThresh = 1120; const int kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm]; const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false }; bool has_contents[3] = { true, false, false };
@ -101,8 +102,10 @@ void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm,
int inbus = (flags >> 4) & 3; int inbus = (flags >> 4) & 3;
int outbus = flags & 3; int outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain[0]; int32_t gain1 = param.gain_out;
int32_t gain2 = param.gain[1]; int32_t gain2 = Exp2::lookup(param.level_in - (14 * (1 << 24)));
param.gain_out = gain2;
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) { if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) {
if (!has_contents[outbus]) { if (!has_contents[outbus]) {
add = false; add = false;

@ -48,7 +48,7 @@ class FmCore {
public: public:
virtual ~FmCore() {}; virtual ~FmCore() {};
static void dump(); static void dump();
virtual void compute(int32_t *output, FmOpParams *params, int algorithm, virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int32_t feedback_gain, const Controllers *controller); int32_t *fb_buf, int32_t feedback_gain, const Controllers *controller);
protected: protected:
AlignedBuf<int32_t, N>buf_[2]; AlignedBuf<int32_t, N>buf_[2];

@ -18,12 +18,10 @@
#define __FM_OP_KERNEL_H #define __FM_OP_KERNEL_H
struct FmOpParams { struct FmOpParams {
int32_t gain[2]; 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 freq;
int32_t phase; int32_t phase;
// PG: temporary code, only useful for engine that already calculate the exp value
int32_t level[2];
}; };
class FmOpKernel { class FmOpKernel {

Loading…
Cancel
Save