Browse Source

Update dexed to latest upstream code

tags/2018-04-16
falkTX 7 years ago
parent
commit
fb62971d56
45 changed files with 3100 additions and 2132 deletions
  1. +86
    -65
      ports/dexed/source/AlgoDisplay.cpp
  2. +3
    -1
      ports/dexed/source/AlgoDisplay.h
  3. +806
    -974
      ports/dexed/source/BinaryData.cpp
  4. +7
    -4
      ports/dexed/source/BinaryData.h
  5. +78
    -49
      ports/dexed/source/CartManager.cpp
  6. +9
    -7
      ports/dexed/source/CartManager.h
  7. +45
    -18
      ports/dexed/source/DXComponents.cpp
  8. +5
    -4
      ports/dexed/source/DXComponents.h
  9. +6
    -1
      ports/dexed/source/DXLookNFeel.cpp
  10. +6
    -5
      ports/dexed/source/DXLookNFeel.h
  11. +15
    -2
      ports/dexed/source/Dexed.h
  12. +219
    -170
      ports/dexed/source/EngineMkI.cpp
  13. +8
    -9
      ports/dexed/source/EngineMkI.h
  14. +16
    -4
      ports/dexed/source/EngineOpl.cpp
  15. +1
    -1
      ports/dexed/source/EngineOpl.h
  16. +88
    -5
      ports/dexed/source/GlobalEditor.cpp
  17. +6
    -2
      ports/dexed/source/GlobalEditor.h
  18. +2
    -2
      ports/dexed/source/JucePluginCharacteristics.h
  19. +36
    -7
      ports/dexed/source/OperatorEditor.cpp
  20. +7
    -8
      ports/dexed/source/OperatorEditor.h
  21. +364
    -7
      ports/dexed/source/ParamDialog.cpp
  22. +21
    -3
      ports/dexed/source/ParamDialog.h
  23. +130
    -138
      ports/dexed/source/PluginData.cpp
  24. +220
    -40
      ports/dexed/source/PluginData.h
  25. +34
    -73
      ports/dexed/source/PluginEditor.cpp
  26. +2
    -6
      ports/dexed/source/PluginEditor.h
  27. +16
    -0
      ports/dexed/source/PluginFx.cpp
  28. +4
    -0
      ports/dexed/source/PluginFx.h
  29. +199
    -47
      ports/dexed/source/PluginParam.cpp
  30. +1
    -0
      ports/dexed/source/PluginParam.h
  31. +127
    -65
      ports/dexed/source/PluginProcessor.cpp
  32. +11
    -7
      ports/dexed/source/PluginProcessor.h
  33. +26
    -19
      ports/dexed/source/ProgramListBox.cpp
  34. +11
    -9
      ports/dexed/source/ProgramListBox.h
  35. +0
    -3
      ports/dexed/source/SysexComm.cpp
  36. +94
    -11
      ports/dexed/source/msfa/controllers.h
  37. +192
    -172
      ports/dexed/source/msfa/dx7note.cc
  38. +41
    -40
      ports/dexed/source/msfa/dx7note.h
  39. +79
    -69
      ports/dexed/source/msfa/env.cc
  40. +3
    -1
      ports/dexed/source/msfa/env.h
  41. +3
    -4
      ports/dexed/source/msfa/fm_core.cc
  42. +10
    -11
      ports/dexed/source/msfa/fm_core.h
  43. +61
    -61
      ports/dexed/source/msfa/lfo.cc
  44. +1
    -1
      ports/dexed/source/msfa/lfo.h
  45. +1
    -7
      ports/dexed/source/msfa/synth.h

+ 86
- 65
ports/dexed/source/AlgoDisplay.cpp View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2014 Pascal Gauthier.
* Copyright (c) 2014, 2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,82 +27,103 @@ AlgoDisplay::AlgoDisplay() {
algo = &tmpAlgo;
}
inline void displayOp(Graphics &g, char id, int x, int y, char link, char fb) {
void AlgoDisplay::displayOp(Graphics &g, char id, int x, int y, char link, char fb) {
const int LINE_SZ = 3;
String t(id);
bool opOn = opStatus[6-id] == '1';
x *= 25;
x += 3;
y *= 21;
y += 5;
g.setColour(Colours::white);
if ( opOn )
g.setColour(Colours::white);
else
g.setColour(DXLookNFeel::roundBackground);
g.drawText(t, x, y, 16, 12, Justification::centred, true);
g.setColour(DXLookNFeel::fillColour);
if ( opOn )
g.setColour(DXLookNFeel::fillColour);
else
g.setColour(DXLookNFeel::roundBackground);
switch(link) {
case 0 : // LINE DOWN
g.drawLine(x+8, y+12, x+8, y+21, LINE_SZ);
break;
case 1: // ARROW TO RIGHT
g.drawLine(x+8, y+12, x+8, y+18, LINE_SZ);
g.drawLine(x+7, y+18, x+34, y+18, LINE_SZ);
break;
case 2: // ARROW TO RIGHT JOIN
g.drawLine(x+8, y+12, x+8, y+19, LINE_SZ);
break;
case 6:
g.drawLine(x+8, y+12, x+8, y+18, LINE_SZ);
g.drawLine(x+7, y+18, x+58, y+18, LINE_SZ);
break;
case 7: // ARROW TO LEFT
g.drawLine(x+8, y+12, x+8, y+19, LINE_SZ);
g.drawLine(x-17, y+18, x+9, y+18, LINE_SZ);
break;
case 0 : // LINE DOWN
g.drawLine(x+8, y+12, x+8, y+21, LINE_SZ);
break;
case 1: // ARROW TO RIGHT
g.drawLine(x+8, y+12, x+8, y+18, LINE_SZ);
g.drawLine(x+7, y+18, x+34, y+18, LINE_SZ);
break;
case 2: // ARROW TO RIGHT JOIN
g.drawLine(x+8, y+12, x+8, y+19, LINE_SZ);
break;
case 3: // ARROW TO RIGHT AND DOWN
g.drawLine(x+8, y+12, x+8, y+21, LINE_SZ);
g.drawLine(x+7, y+18, x+34, y+18, LINE_SZ);
g.drawLine(x+34, y+17, x+34, y+21, LINE_SZ);
break;
case 4: // ARROW TO RIGHT+LEFT AND DOWN
g.drawLine(x+8, y+12, x+8, y+21, LINE_SZ);
g.drawLine(x+7, y+18, x+34, y+18, LINE_SZ);
g.drawLine(x+34, y+17, x+34, y+21, LINE_SZ);
g.drawLine(x-17, y+18, x+8, y+18, LINE_SZ);
g.drawLine(x-17, y+17, x-17, y+21, LINE_SZ);
break;
case 6:
g.drawLine(x+8, y+12, x+8, y+18, LINE_SZ);
g.drawLine(x+7, y+18, x+58, y+18, LINE_SZ);
break;
case 7: // ARROW TO LEFT
g.drawLine(x+8, y+12, x+8, y+19, LINE_SZ);
g.drawLine(x-17, y+18, x+9, y+18, LINE_SZ);
break;
}
switch(fb) {
case 0:
break;
case 1:
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x+20, y-4, LINE_SZ);
g.drawLine(x+19, y-4, x+19, y+15, LINE_SZ);
g.drawLine(x+18, y+15, x+19, y+15, LINE_SZ);
g.drawLine(x+8, y+15, x+20, y+15, LINE_SZ);
break;
case 2: // ALGO 4
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x+20, y-4, LINE_SZ);
g.drawLine(x+19, y-4, x+19, y+59, LINE_SZ);
g.drawLine(x+8, y+58, x+19, y+58, LINE_SZ);
break;
case 3: // ALGO 6
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x+20, y-4, LINE_SZ);
g.drawLine(x+19, y-4, x+19, y+37, LINE_SZ);
g.drawLine(x+8, y+36, x+19, y+36, LINE_SZ);
break;
case 4:
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x-4, y-4, LINE_SZ);
g.drawLine(x-3, y-4, x-3, y+15, LINE_SZ);
g.drawLine(x-3, y+15, x+8, y+15, LINE_SZ);
g.drawLine(x+8, y+15, x+8, y+12, LINE_SZ);
break;
case 0:
break;
case 1:
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x+21, y-4, LINE_SZ);
g.drawLine(x+20, y-4, x+20, y+15, LINE_SZ);
g.drawLine(x+19, y+15, x+20, y+16, LINE_SZ);
g.drawLine(x+8, y+15, x+20, y+15, LINE_SZ);
break;
case 2: // ALGO 4
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x+20, y-4, LINE_SZ);
g.drawLine(x+19, y-4, x+19, y+59, LINE_SZ);
g.drawLine(x+8, y+58, x+19, y+58, LINE_SZ);
break;
case 3: // ALGO 6
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x+20, y-4, LINE_SZ);
g.drawLine(x+19, y-4, x+19, y+37, LINE_SZ);
g.drawLine(x+8, y+36, x+19, y+36, LINE_SZ);
break;
case 4:
g.drawLine(x+7, y, x+8, y-5, LINE_SZ);
g.drawLine(x+8, y-4, x-4, y-4, LINE_SZ);
g.drawLine(x-3, y-4, x-3, y+15, LINE_SZ);
g.drawLine(x-3, y+15, x+8, y+15, LINE_SZ);
g.drawLine(x+8, y+15, x+8, y+12, LINE_SZ);
break;
}
}
void AlgoDisplay::paint(Graphics &g) {
g.setColour(DXLookNFeel::fillColour);
g.fillRect(1, 3, 20, 15);
String n = String(*algo +1);
g.setColour(Colours::white);
g.drawText(n, 1, 3, 20, 15, Justification::centred, true);
switch(*algo) {
case 0:
displayOp(g, 6, 3, 0, 0, 1);
@@ -249,7 +270,7 @@ void AlgoDisplay::paint(Graphics &g) {
displayOp(g, 1, 3, 3, 0, 0);
break;
case 18:
displayOp(g, 6, 3, 2, 0, 1);
displayOp(g, 6, 3, 2, 3, 1);
displayOp(g, 5, 4, 3, 2, 0);
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 2, 1, 0, 0);
@@ -260,20 +281,20 @@ void AlgoDisplay::paint(Graphics &g) {
displayOp(g, 6, 4, 2, 0, 0);
displayOp(g, 5, 3, 2, 1, 0);
displayOp(g, 4, 4, 3, 2, 0);
displayOp(g, 3, 1, 2, 0, 1);
displayOp(g, 3, 1, 2, 3, 1);
displayOp(g, 2, 2, 3, 6, 0);
displayOp(g, 1, 1, 3, 1, 0);
break;
case 20:
displayOp(g, 6, 3, 2, 0, 0);
displayOp(g, 6, 3, 2, 3, 0);
displayOp(g, 5, 4, 3, 2, 0);
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 1, 2, 0, 1);
displayOp(g, 3, 1, 2, 3, 1);
displayOp(g, 2, 2, 3, 1, 0);
displayOp(g, 1, 1, 3, 1, 0);
break;
case 21:
displayOp(g, 6, 3, 2, 0, 1);
displayOp(g, 6, 3, 2, 4, 1);
displayOp(g, 5, 4, 3, 2, 0);
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 2, 3, 1, 0);
@@ -281,7 +302,7 @@ void AlgoDisplay::paint(Graphics &g) {
displayOp(g, 1, 1, 3, 1, 0);
break;
case 22: // CC
displayOp(g, 6, 3, 2, 0, 1);
displayOp(g, 6, 3, 2, 3, 1);
displayOp(g, 5, 4, 3, 2, 0);
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 2, 2, 0, 0);
@@ -289,7 +310,7 @@ void AlgoDisplay::paint(Graphics &g) {
displayOp(g, 1, 1, 3, 1, 0);
break;
case 23: // CC
displayOp(g, 6, 3, 2, 0, 1);
displayOp(g, 6, 3, 2, 4, 1);
displayOp(g, 5, 4, 3, 2, 0);
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 2, 3, 1, 0);
@@ -297,7 +318,7 @@ void AlgoDisplay::paint(Graphics &g) {
displayOp(g, 1, 0, 3, 1, 0);
break;
case 24: // CC
displayOp(g, 6, 3, 2, 0, 1);
displayOp(g, 6, 3, 2, 3, 1);
displayOp(g, 5, 4, 3, 2, 0);
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 2, 3, 1, 0);
@@ -350,7 +371,7 @@ void AlgoDisplay::paint(Graphics &g) {
displayOp(g, 4, 3, 3, 1, 0);
displayOp(g, 3, 2, 3, 1, 0);
displayOp(g, 2, 1, 3, 1, 0);
displayOp(g, 1, 0, 3, 1, 0);
displayOp(g, 1, 0, 3, 1, 0);
break;
case 31:
displayOp(g, 6, 5, 3, 2, 1);


+ 3
- 1
ports/dexed/source/AlgoDisplay.h View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2014 Pascal Gauthier.
* Copyright (c) 2014-2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,9 @@
#include "JuceHeader.h"
class AlgoDisplay : public Component {
void displayOp(Graphics &g, char id, int x, int y, char link, char fb);
public:
const char *opStatus;
AlgoDisplay();
char *algo;
void paint(Graphics &g);


+ 806
- 974
ports/dexed/source/BinaryData.cpp
File diff suppressed because it is too large
View File


+ 7
- 4
ports/dexed/source/BinaryData.h View File

@@ -9,6 +9,9 @@
namespace BinaryData
{
extern const char* Switch_32x32_png;
const int Switch_32x32_pngSize = 841;
extern const char* Switch_48x26_png;
const int Switch_48x26_pngSize = 2261;
@@ -43,19 +46,19 @@ namespace BinaryData
const int builtin_pgm_zipSize = 88890;
extern const char* about_png;
const int about_pngSize = 23143;
const int about_pngSize = 24863;
extern const char* GlobalEditor_864x144_png;
const int GlobalEditor_864x144_pngSize = 27571;
const int GlobalEditor_864x144_pngSize = 17334;
extern const char* OperatorEditor_287x218_png;
const int OperatorEditor_287x218_pngSize = 15614;
const int OperatorEditor_287x218_pngSize = 10927;
// Points to the start of a list of resource names.
extern const char* namedResourceList[];
// Number of elements in the namedResourceList array.
const int namedResourceListSize = 14;
const int namedResourceListSize = 15;
// If you provide the name of one of the binary resource variables above, this function will
// return the corresponding data and its size (or a null pointer if the name isn't found).


+ 78
- 49
ports/dexed/source/CartManager.cpp View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2015 Pascal Gauthier.
* Copyright (c) 2015-2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,15 +31,48 @@ class SyxFileFilter : public FileFilter {
public:
SyxFileFilter() : FileFilter(".syx") {}
bool isFileSuitable(const File &file) const {
return file.getFileExtension().toLowerCase() == ".syx" && file.getSize() == 4104;
return file.getFileExtension().toLowerCase() == ".syx" && file.getSize() >= 4096;
}
bool isDirectorySuitable(const File &file) const {
return true;
};
};
class FileTreeDrop : public FileTreeComponent {
public :
FileTreeDrop(DirectoryContentsList &listToShow) : FileTreeComponent(listToShow) {}
bool isInterestedInFileDrag (const StringArray &files) override {
bool found = false;
for(int i=0; i<files.size(); i++) {
String filename = files[i].toLowerCase();
found |= filename.endsWith(".syx");
}
return found;
}
void filesDropped(const StringArray &files, int x, int y) override {
File targetDir = getSelectedFile();
if ( ! targetDir.exists() )
targetDir = DexedAudioProcessor::dexedCartDir;
if ( ! targetDir.isDirectory() )
targetDir = targetDir.getParentDirectory();
for(int i=0; i<files.size(); i++) {
if ( files[i].toLowerCase().endsWith(".syx") ) {
File src(files[i]);
File target = targetDir.getChildFile(src.getFileName());
src.copyFileTo(target);
}
}
refresh();
}
};
CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartManager") {
mainWindow = editor;
cartDir = DexedAudioProcessor::dexedCartDir;
@@ -57,8 +90,10 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan
timeSliceThread->startThread();
cartBrowserList = new DirectoryContentsList(syxFileFilter, *timeSliceThread);
cartBrowserList->setDirectory(cartDir, true, true);
cartBrowser = new FileTreeComponent(*cartBrowserList);
cartBrowser = new FileTreeDrop(*cartBrowserList);
cartBrowser->addKeyListener(this);
addAndMakeVisible(cartBrowser);
cartBrowser->setBounds(23, 18, 590, 384);
cartBrowser->setDragAndDropDescription("Sysex Browser");
cartBrowser->addListener(this);
@@ -82,7 +117,7 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan
*
* I've removed this since it only works on the DX7 II. TBC.
*
addAndMakeVisible(getDXPgmButton = new TextButton("GET DX7 PGM"));
getDXPgmButton->setBounds(656, 545, 100, 30);
getDXPgmButton->addListener(this);
@@ -90,7 +125,6 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan
addAndMakeVisible(getDXCartButton = new TextButton("GET DX7 CART"));
getDXCartButton->setBounds(755, 545, 100, 30);
getDXCartButton->addListener(this);
*/
}
@@ -115,10 +149,8 @@ void CartManager::programSelected(ProgramListBox *source, int pos) {
mainWindow->processor->setCurrentProgram(pos);
mainWindow->processor->updateHostDisplay();
} else {
if ( source->getCurrentCart() == nullptr )
return;
char unpackPgm[161];
unpackProgramFromSysex(unpackPgm, source->getCurrentCart(), pos);
uint8_t unpackPgm[161];
source->getCurrentCart().unpackProgram(unpackPgm, pos);
activeCart->setSelected(-1);
browserCart->setSelected(pos);
repaint();
@@ -177,11 +209,11 @@ void CartManager::fileDoubleClicked(const File& file) {
if ( file.isDirectory() )
return;
mainWindow->loadCart(file);
activeCart->setCartridge(mainWindow->processor->sysex);
activeCart->setCartridge(mainWindow->processor->currentCart);
}
void CartManager::fileClicked(const File& file, const MouseEvent& e) {
if ( e.mods.isRightButtonDown() ) {
if ( e.mods.isRightButtonDown() || e.mods.isAnyModifierKeyDown() ) {
PopupMenu menu;
menu.addItem(1000, "Open location");
@@ -215,7 +247,7 @@ void CartManager::setActiveProgram(int idx, String activeName) {
}
void CartManager::resetActiveSysex() {
activeCart->setCartridge(mainWindow->processor->sysex);
activeCart->setCartridge(mainWindow->processor->currentCart);
}
void CartManager::selectionChanged() {
@@ -227,21 +259,14 @@ void CartManager::selectionChanged() {
if ( file.isDirectory() )
return;
String f = file.getFullPathName();
uint8_t syx_data[4104];
ifstream fp_in(f.toRawUTF8(), ios::binary);
if (fp_in.fail()) {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Unable to open: " + f);
Cartridge browserSysex;
int rc = browserSysex.load(file);
if ( rc < 0 ) {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Unable to open file");
return;
}
fp_in.read((char *)syx_data, 4104);
fp_in.close();
char browserSysex[4104];
memcpy(browserSysex, syx_data+6, 4096);
int checksum = sysexChecksum(((char *) &browserSysex), 4096);
if ( checksum != syx_data[4102] ) {
if ( rc != 0 ) {
browserCart->readOnly = true;
} else {
browserCart->readOnly = false;
@@ -260,19 +285,19 @@ void CartManager::programRightClicked(ProgramListBox *source, int pos) {
switch(menu.show()) {
case 1000:
char unpackPgm[161];
uint8_t unpackPgm[161];
if ( source == activeCart ) {
unpackProgramFromSysex(unpackPgm, mainWindow->processor->sysex, pos);
mainWindow->processor->currentCart.unpackProgram(unpackPgm, pos);
} else {
char *sysex = source->getCurrentCart();
if ( sysex == nullptr )
return;
unpackProgramFromSysex(unpackPgm, sysex, pos);
source->getCurrentCart().unpackProgram(unpackPgm, pos);
}
if ( mainWindow->processor->sysexComm.isOutputActive() )
mainWindow->processor->sysexComm.send(MidiMessage(unpackPgm, 161));
if ( mainWindow->processor->sysexComm.isOutputActive() ) {
uint8_t msg[163];
exportSysexPgm(msg, unpackPgm);
mainWindow->processor->sysexComm.send(MidiMessage(msg, 163));
}
break;
case 1010:
@@ -284,7 +309,7 @@ void CartManager::programRightClicked(ProgramListBox *source, int pos) {
void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) {
if ( destListBox == activeCart ) {
char *sysex = mainWindow->processor->sysex;
char *sysex = mainWindow->processor->currentCart.getRawVoice();
memcpy(sysex+(dest*128), packedPgm, 128);
mainWindow->updateUI();
} else {
@@ -295,22 +320,14 @@ void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *pa
if ( file.isDirectory() )
return;
if ( file.getSize() > 5000 )
return;
MemoryBlock block;
file.loadFileAsData(block);
if ( block.getSize() < 4104 )
if ( file.getSize() != 4104 && file.getSize() != 4096 )
return;
char *sysex = ((char *) block.getData()) + 6;
memcpy(sysex+(dest*128), packedPgm, 128);
char exported[4104];
exportSysexCart(exported, sysex, 0);
file.replaceWithData(exported, 4104);
browserCart->setCartridge(sysex);
Cartridge cart;
cart.load(file);
memcpy(cart.getRawVoice()+(dest*128), packedPgm, 128);
cart.saveVoice(file);
browserCart->setCartridge(cart);
}
}
@@ -318,6 +335,18 @@ void CartManager::initialFocus() {
cartBrowser->grabKeyboardFocus();
}
bool CartManager::keyPressed(const KeyPress& key, Component* originatingComponent) {
if ( key.getKeyCode() == 13 ) {
File file = cartBrowser->getSelectedFile();
if ( file.isDirectory() )
return true;
mainWindow->loadCart(file);
activeCart->setCartridge(mainWindow->processor->currentCart);
return true;
}
return false;
}
void CartManager::showSysexConfigMsg() {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Warning", "The DX7 midi interface is not configured correctly.\n\n"
"These buttons are used to 'ask' the DX7 to send the current program/cartridge.\n\n"


+ 9
- 7
ports/dexed/source/CartManager.h View File

@@ -24,9 +24,10 @@
#include "JuceHeader.h"
#include "PluginData.h"
#include "ProgramListBox.h"
#include "PluginData.h"
class CartManager : public Component, public ButtonListener, public DragAndDropContainer, public FileBrowserListener
, public ProgramListBoxListener {
, public ProgramListBoxListener, public KeyListener {
ScopedPointer<TextButton> newButton;
ScopedPointer<TextButton> loadButton;
ScopedPointer<TextButton> saveButton;
@@ -53,13 +54,13 @@ class CartManager : public Component, public ButtonListener, public DragAndDrop
public:
CartManager(DexedAudioProcessorEditor *editor);
virtual ~CartManager();
void paint(Graphics& g);
void buttonClicked (Button* buttonThatWasClicked);
void paint(Graphics& g) override;
void buttonClicked (Button* buttonThatWasClicked) override;
void selectionChanged();
void fileClicked (const File& file, const MouseEvent& e);
void fileDoubleClicked (const File& file);
void browserRootChanged (const File& newRoot);
void selectionChanged() override;
void fileClicked (const File& file, const MouseEvent& e) override;
void fileDoubleClicked (const File& file) override;
void browserRootChanged (const File& newRoot) override;
void setActiveProgram(int idx, String activeName);
void resetActiveSysex();
@@ -67,6 +68,7 @@ public:
virtual void programSelected(ProgramListBox *source, int pos) override;
virtual void programRightClicked(ProgramListBox *source, int pos) override;
virtual void programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) override;
virtual bool keyPressed(const KeyPress& key, Component* originatingComponent) override;
void initialFocus();
};


+ 45
- 18
ports/dexed/source/DXComponents.cpp View File

@@ -1,4 +1,4 @@
/**
/**
*
* Copyright (c) 2014 Pascal Gauthier.
*
@@ -157,13 +157,13 @@ static double getDuration(int p_rate, int p_level_l, int p_level_r) {
}
EnvDisplay::EnvDisplay() {
pvalues = (char *) &TMP_LEVEL_PTR;
pvalues = (uint8_t *) &TMP_LEVEL_PTR;
}
void EnvDisplay::paint(Graphics &g) {
int h = getHeight();
char *rates = pvalues;
char *levels = pvalues + 4;
uint8_t *rates = pvalues;
uint8_t *levels = pvalues + 4;
double d[4];
double keyoff = 0.0;
@@ -250,15 +250,15 @@ void EnvDisplay::paint(Graphics &g) {
}
PitchEnvDisplay::PitchEnvDisplay() {
pvalues = (char *) &TMP_LEVEL_PTR;
pvalues = (uint8_t *) &TMP_LEVEL_PTR;
vPos = 0;
}
void PitchEnvDisplay::paint(Graphics &g) {
g.setColour(Colours::white);
char *levels = pvalues + 4;
char *rates = pvalues;
uint8_t *levels = pvalues + 4;
uint8_t *rates = pvalues;
float dist[4];
float total = 0;
@@ -363,25 +363,22 @@ ComboBoxImage::ComboBoxImage() {
itemPos[0] = -1;
}
static void comboBoxPopupMenuFinishedCallback (int result, ComboBoxImage* combo)
{
if (combo != nullptr)
{
static void comboBoxPopupMenuFinishedCallback (int result, ComboBoxImage* combo) {
if (combo != nullptr) {
combo->hidePopup();
if (result != 0)
combo->setSelectedId (result);
}
}
void ComboBoxImage::showPopup() {
popup.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
.withItemThatMustBeVisible (getSelectedId())
.withMinimumWidth (getWidth())
.withMaximumNumColumns (1)
.withStandardItemHeight (itemHeight),
ModalCallbackFunction::forComponent (comboBoxPopupMenuFinishedCallback, this));
.withItemThatMustBeVisible(getSelectedId())
.withMinimumWidth(getWidth())
.withMaximumNumColumns(1)
.withStandardItemHeight(itemHeight),
ModalCallbackFunction::forComponent(comboBoxPopupMenuFinishedCallback, this));
}
void ComboBoxImage::setImage(Image image) {
@@ -411,5 +408,35 @@ void ComboBoxImage::setImage(Image image, int pos[]) {
itemPos[i] = pos[i];
}
void ProgramSelector::mouseDown(const MouseEvent &event) {
if ( event.x < getWidth() - 8) {
ComboBox::mouseDown(event);
return;
}
int cur = getSelectedItemIndex();
if ( event.y < getHeight() / 2 ) {
if ( cur == 0 )
cur = 31;
else
cur--;
} else {
if ( cur == 31 )
cur = 0;
else
cur++;
}
setSelectedItemIndex(cur);
}
void ProgramSelector::paint(Graphics &g) {
int x = getWidth();
int y = getHeight();
Path path;
path.addTriangle(x-8, y/2-1, x-4, 2, x, y/2-1);
path.addTriangle(x-8, y/2+1, x-4, y-2, x, y/2+1);
g.setColour(Colours::white);
g.fillPath(path);
}

+ 5
- 4
ports/dexed/source/DXComponents.h View File

@@ -22,11 +22,12 @@
#define DXCOMPONENTS_H_INCLUDED
#include "JuceHeader.h"
#include <stdint.h>
class EnvDisplay : public Component {
public:
EnvDisplay();
char *pvalues;
uint8_t *pvalues;
char vPos;
void paint(Graphics &g);
};
@@ -35,7 +36,7 @@ class PitchEnvDisplay : public Component {
char rvalues[8];
public:
PitchEnvDisplay();
char *pvalues;
uint8_t *pvalues;
char vPos;
void paint(Graphics &g);
};
@@ -58,12 +59,11 @@ class ComboBoxImage : public ComboBox {
Image items;
int itemHeight;
PopupMenu popup;
int itemPos[4];
public:
ComboBoxImage();
virtual void paint(Graphics &g);
virtual void paint(Graphics &g) override;
virtual void showPopup() override;
void setImage(Image image);
void setImage(Image image, int pos[]);
@@ -71,6 +71,7 @@ public:
class ProgramSelector : public ComboBox {
public:
void mouseDown(const MouseEvent &event) override;
virtual void paint(Graphics &g) override;
};


+ 6
- 1
ports/dexed/source/DXLookNFeel.cpp View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2013-2014 Pascal Gauthier.
* Copyright (c) 2013-2016 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -64,6 +64,7 @@ DXLookNFeel::DXLookNFeel() {
imageKnob = ImageCache::getFromMemory(BinaryData::Knob_34x34_png, BinaryData::Knob_34x34_pngSize);
imageSwitch = ImageCache::getFromMemory(BinaryData::Switch_48x26_png, BinaryData::Switch_48x26_pngSize);
imageSwitchOperator = ImageCache::getFromMemory(BinaryData::Switch_32x32_png, BinaryData::Switch_32x32_pngSize);
imageButton = ImageCache::getFromMemory(BinaryData::ButtonUnlabeled_50x30_png, BinaryData::ButtonUnlabeled_50x30_pngSize);
imageSlider = ImageCache::getFromMemory(BinaryData::Slider_26x26_png, BinaryData::Slider_26x26_pngSize);
imageScaling = ImageCache::getFromMemory(BinaryData::Scaling_36_26_png, BinaryData::Scaling_36_26_pngSize);;
@@ -119,6 +120,10 @@ DXLookNFeel::DXLookNFeel() {
imageSwitch = findImage(path);
continue;
}
if ( name == "Switch_32x64.png" ) {
imageSwitchOperator = findImage(path);
continue;
}
if ( name == "ButtonUnlabeled_50x30.png" ) {
imageButton = findImage(path);
continue;


+ 6
- 5
ports/dexed/source/DXLookNFeel.h View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2013-2014 Pascal Gauthier.
* Copyright (c) 2013-2016 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,11 +35,12 @@ public:
Typeface::Ptr defaultFontBold;
Image imageKnob, imageSwitch, imageButton, imageSlider, imageScaling, imageLight, imageLFO;
Image imageSwitchOperator;
Image imageOperator, imageGlobal;
/* overriden methods */
virtual void drawRotarySlider(Graphics &g, int x, int y, int width, int height, float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, Slider &slider );
virtual void drawToggleButton(Graphics& g, ToggleButton& button, bool isMouseOverButton, bool isButtonDown);
virtual void drawRotarySlider(Graphics &g, int x, int y, int width, int height, float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, Slider &slider ) override;
virtual void drawToggleButton(Graphics& g, ToggleButton& button, bool isMouseOverButton, bool isButtonDown) override;
virtual void drawLinearSliderBackground (Graphics&, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle, Slider&) override;
@@ -49,8 +50,8 @@ public:
virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override;
virtual Font getTextButtonFont(TextButton&, int buttonHeight) override;
virtual Typeface::Ptr getTypefaceForFont(const Font &);
virtual void positionComboBoxText (ComboBox& box, Label& label);
virtual Typeface::Ptr getTypefaceForFont(const Font &) override;
virtual void positionComboBoxText (ComboBox& box, Label& label) override;
static DXLookNFeel *getLookAndFeel();
static Colour fillColour;


+ 15
- 2
ports/dexed/source/Dexed.h View File

@@ -21,7 +21,20 @@
#ifndef DEXED_H_INCLUDED
#define DEXED_H_INCLUDED
#define DEXED_VERSION "0.9.0"
#define TRACE(fmt, ...)
void dexed_trace(const char *source, const char *fmt, ...);
#define DEXED_ID "0.9.4"
#ifdef DEBUG
#define DEXED_VERSION DEXED_ID " DEBUG"
#ifdef _MSC_VER
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
#else
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#endif
#else
#define DEXED_VERSION DEXED_ID
#define TRACE(fmt, ...)
#endif
#endif // DEXED_H_INCLUDED

+ 219
- 170
ports/dexed/source/EngineMkI.cpp View File

@@ -1,155 +1,211 @@
/*
* Copyright 2014 Pascal Gauthier.
* Copyright 2012 Google Inc.
* Copyright (C) 2015-2017 Pascal Gauthier.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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
*
* The code is based on ppplay https://github.com/stohrendorf/ppplay and opl3
* math documentation :
* https://github.com/gtaylormb/opl3_fpga/blob/master/docs/opl3math/opl3math.pdf
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EngineMkI.h"
#include <math.h>
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstdlib>
#include "msfa/sin.h"
#include "msfa/exp2.h"
const FmAlgorithm EngineMkI::algo2[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
{ { 0xc4, 0x00, 0x00, 0x01, 0x11, 0x14 } }, // 4 ** EXCEPTION VIA CODE
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
{ { 0xc4, 0x00, 0x01, 0x14, 0x01, 0x14 } }, // 6 ** EXCEPTION VIA CODE
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
};
#ifdef DEBUG
#include "time.h"
//#define MKIDEBUG
#endif
#ifdef _WIN32
#if _MSC_VER < 1800
double log2(double n) {
return log(n) / log(2.0);
}
double round(double n) {
return n < 0.0 ? ceil(n - 0.5) : floor(n + 0.5);
}
#endif
__declspec(align(16)) const int zeros[N] = {0};
#else
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0};
#endif
static const uint16_t NEGATIVE_BIT = 0x8000;
static const uint16_t ENV_BITDEPTH = 14;
static const uint16_t SINLOG_BITDEPTH = 10;
static const uint16_t SINLOG_TABLESIZE = 1<<SINLOG_BITDEPTH;
static uint16_t sinLogTable[SINLOG_TABLESIZE];
static const uint16_t SINEXP_BITDEPTH = 10;
static const uint16_t SINEXP_TABLESIZE = 1<<SINEXP_BITDEPTH;
static uint16_t sinExpTable[SINEXP_TABLESIZE];
const uint16_t ENV_MAX = 1<<ENV_BITDEPTH;
static inline uint16_t sinLog(uint16_t phi) {
const uint16_t SINLOG_TABLEFILTER = SINLOG_TABLESIZE-1;
const uint16_t index = (phi & SINLOG_TABLEFILTER);
switch( ( phi & (SINLOG_TABLESIZE * 3) ) ) {
case 0:
return sinLogTable[index];
case SINLOG_TABLESIZE:
return sinLogTable[index ^ SINLOG_TABLEFILTER];
case SINLOG_TABLESIZE * 2 :
return sinLogTable[index] | NEGATIVE_BIT;
default:
return sinLogTable[index ^ SINLOG_TABLEFILTER] | NEGATIVE_BIT;
}
}
EngineMkI::EngineMkI() {
float bitReso = SINLOG_TABLESIZE;
for(int i=0;i<SINLOG_TABLESIZE;i++) {
float x1 = sin(((0.5+i)/bitReso) * M_PI/2.0);
sinLogTable[i] = round(-1024 * log2(x1));
}
bitReso = SINEXP_TABLESIZE;
for(int i=0;i<SINEXP_TABLESIZE;i++) {
float x1 = (pow(2, float(i)/bitReso)-1) * 4096;
sinExpTable[i] = round(x1);
}
#ifdef MKIDEBUG
char buffer[4096];
int pos = 0;
TRACE("****************************************");
for(int i=0;i<SINLOG_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinLogTable[i]);
if ( pos > 90 ) {
TRACE("SINLOGTABLE: %s" ,buffer);
buffer[0] = 0;
pos = 0;
}
}
TRACE("SINLOGTABLE: %s", buffer);
buffer[0] = 0;
pos = 0;
TRACE("----------------------------------------");
for(int i=0;i<SINEXP_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinExpTable[i]);
if ( pos > 90 ) {
TRACE("SINEXTTABLE: %s" ,buffer);
buffer[0] = 0;
pos = 0;
}
}
TRACE("SINEXTTABLE: %s", buffer);
TRACE("****************************************");
#endif
}
inline int32_t mkiSin(int32_t phase, uint16_t env) {
uint16_t expVal = sinLog(phase >> (22 - SINLOG_BITDEPTH)) + (env);
//int16_t expValShow = expVal;
const bool isSigned = expVal & NEGATIVE_BIT;
expVal &= ~NEGATIVE_BIT;
const uint16_t SINEXP_FILTER = 0x3FF;
uint16_t result = 4096 + sinExpTable[( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER];
//uint16_t resultB4 = result;
result >>= ( expVal >> 10 ); // exp
#ifdef MKIDEBUG
if ( ( time(NULL) % 5 ) == 0 ) {
if ( expValShow < 0 ) {
expValShow = (expValShow + 0x7FFF) * -1;
}
//TRACE(",%d,%d,%d,%d,%d,%d", phase >> (22 - SINLOG_BITDEPTH), env, expValShow, ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER, resultB4, result);
}
#endif
if( isSigned )
return (-result - 1) << 13;
else
return result << 13;
}
void EngineMkI::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 gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
const int32_t *adder = add ? output : zeros;
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = mkiSin((phase+input[i]), gain);
output[i] = y + adder[i];
phase += 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) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
const int32_t *adder = add ? output : zeros;
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = mkiSin(phase , gain);
output[i] = y + adder[i];
phase += freq;
}
}
void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add, const Controllers *controllers) {
int32_t *fb_buf, int fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y &= controllers->sinBitFilter;
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y &= controllers->sinBitFilter;
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase += freq;
}
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = mkiSin((phase+scaled_fb), gain);
output[i] = y + adder[i];
phase += freq;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
// exclusively used for ALGO 6 with feedback
void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift, const Controllers *cont) {
void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) {
int32_t dgain[2];
int32_t gain[2];
int32_t phase[2];
@@ -158,39 +214,37 @@ void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01,
phase[0] = parms[0].phase;
phase[1] = parms[1].phase;
parms[1].gain_out = (ENV_MAX-(parms[1].level_in >> (28-ENV_BITDEPTH)));
gain[0] = gain01;
gain[1] = parms[1].gain_out;
gain[1] = parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out;
dgain[0] = (gain02 - gain01 + (N >> 1)) >> LG_N;
dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out));
parms[1].gain_out = Exp2::lookup(parms[1].level_in - (14 * (1 << 24)));
dgain[1] = (parms[1].gain_out - gain[1] + (N >> 1)) >> LG_N;
for (int i = 0; i < N; i++) {
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
// op 0
gain[0] += dgain[0];
int32_t scaled_fb = (y0 + y) >> (fb_shift + 2); // tsk tsk tsk: this needs some tuning
y0 = y;
y = Sin::lookup(phase[0] + scaled_fb);
y &= cont->sinBitFilter;
y = ((int64_t)y * (int64_t)gain[0]) >> 24;
y = mkiSin(phase[0]+scaled_fb, gain[0]);
phase[0] += parms[0].freq;
// op 1
gain[1] += dgain[1];
y = Sin::lookup(phase[1] + y);
y &= cont->sinBitFilter;
y = ((int64_t)y * (int64_t)gain[1]) >> 24;
output[i] = y;
y = mkiSin(phase[1]+y, gain[1]);
phase[1] += parms[1].freq;
output[i] = y;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
// exclusively used for ALGO 4 with feedback
void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift, const Controllers *cont) {
void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) {
int32_t dgain[3];
int32_t gain[3];
int32_t phase[3];
@@ -200,53 +254,55 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01,
phase[0] = parms[0].phase;
phase[1] = parms[1].phase;
phase[2] = parms[2].phase;
gain[0] = gain01;
gain[1] = parms[1].gain_out;
gain[2] = parms[2].gain_out;
parms[1].gain_out = (ENV_MAX-(parms[1].level_in >> (28-ENV_BITDEPTH)));
parms[2].gain_out = (ENV_MAX-(parms[2].level_in >> (28-ENV_BITDEPTH)));
gain[0] = gain01;
gain[1] = parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out;
gain[2] = parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out;
dgain[0] = (gain02 - gain01 + (N >> 1)) >> LG_N;
dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out));
dgain[2] = (parms[2].gain_out - (parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out));
parms[1].gain_out = Exp2::lookup(parms[1].level_in - (14 * (1 << 24)));
dgain[1] = (parms[1].gain_out - gain[1] + (N >> 1)) >> LG_N;
parms[2].gain_out = Exp2::lookup(parms[2].level_in - (14 * (1 << 24)));
dgain[2] = (parms[1].gain_out - gain[2] + (N >> 1)) >> LG_N;
for (int i = 0; i < N; i++) {
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
// op 0
gain[0] += dgain[0];
int32_t scaled_fb = (y0 + y) >> (fb_shift + 6); // tsk tsk tsk: this needs some tuning
y0 = y;
y = Sin::lookup(phase[0] + scaled_fb);
y &= cont->sinBitFilter;
y = ((int64_t)y * (int64_t)gain[0]) >> 24;
y = mkiSin(phase[0]+scaled_fb, gain[0]);
phase[0] += parms[0].freq;
// op 1
gain[1] += dgain[1];
y = Sin::lookup(phase[1] + y);
y &= cont->sinBitFilter;
y = ((int64_t)y * (int64_t)gain[1]) >> 24;
y = mkiSin(phase[1]+y, gain[1]);
phase[1] += parms[1].freq;
// op 2
gain[2] += dgain[2];
y = Sin::lookup(phase[2] + y);
y &= cont->sinBitFilter;
y = ((int64_t)y * (int64_t)gain[2]) >> 24;
y = mkiSin(phase[2]+y, gain[2]);
phase[2] += parms[2].freq;
output[i] = y;
phase[2] += parms[2].freq;
}
fb_buf[0] = y0;
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 = algo2[algorithm];
void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) {
const int kLevelThresh = ENV_MAX-100;
FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
bool fb_on = feedback_shift < 16;
switch(algorithm) {
case 3 : case 5 :
if ( fb_on )
alg.ops[0] = 0xc4;
}
for (int op = 0; op < 6; op++) {
int flags = alg.ops[op];
@@ -255,11 +311,11 @@ void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm,
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)));
int32_t gain1 = param.gain_out == 0 ? (ENV_MAX-1) : param.gain_out;
int32_t gain2 = ENV_MAX-(param.level_in >> (28-ENV_BITDEPTH));
param.gain_out = gain2;
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) {
if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
@@ -267,41 +323,34 @@ void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm,
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) {
if ((flags & 0xc0) == 0xc0 && fb_on) {
switch ( algorithm ) {
// two operator feedback, process exception for ALGO 6
case 5 :
compute_fb2(outptr, params, gain1, gain2, 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 3 :
compute_fb3(outptr, params, gain1, gain2, fb_buf, feedback_shift, controllers);
compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16));
params[1].phase += params[1].freq << LG_N; // hack, we already processed op-5 - op-4
params[2].phase += params[2].freq << LG_N; // yuk yuk
op += 2; // ignore the 2 other operators
break;
// two operator feedback, process exception for ALGO 6
case 5 :
compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16));
params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5
op++; // ignore next operator;
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);
compute_fb(outptr, param.phase, param.freq, gain1, gain2, fb_buf, feedback_shift, add);
break;
}
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
compute_pure(outptr, param.phase, param.freq, gain1, gain2, add, controllers);
compute_pure(outptr, param.phase, param.freq, gain1, gain2, add);
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add, controllers);
compute(outptr, buf_[inbus - 1].get(), param.phase, param.freq, gain1, gain2, add);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}


+ 8
- 9
ports/dexed/source/EngineMkI.h View File

@@ -26,24 +26,23 @@
class EngineMkI : public FmCore {
//refacter this when it is working
const static FmAlgorithm algo2[32];
public:
virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers);
EngineMkI();
void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) override;
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);
bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2,
bool add, const Controllers *controllers);
bool add);
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);
int32_t *fb_buf, int fb_gain, bool add);
void compute_fb2(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift, const Controllers *controllers);
void compute_fb2(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift);
void compute_fb3(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift, const Controllers *controllers);
void compute_fb3(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift);
};


+ 16
- 4
ports/dexed/source/EngineOpl.cpp View File

@@ -26,7 +26,7 @@
#include "EngineOpl.h"
#ifdef _WIN32
__declspec(align(16)) int zeros[N] = {0};
__declspec(align(16)) const int zeros[N] = {0};
#else
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0};
#endif
@@ -71,7 +71,7 @@ uint16_t sinExpTable[256] = {
937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018
};
inline uint16_t sinLog( uint16_t phi ) {
inline uint16_t sinLog(uint16_t phi) {
const uint8_t index = (phi & 0xff);
switch( ( phi & 0x0300 ) ) {
@@ -169,7 +169,7 @@ void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers) {
int32_t *fb_buf, int feedback_shift) {
const int kLevelThresh = 507; // really ????
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
@@ -211,4 +211,16 @@ void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,
}
param.phase += param.freq << LG_N;
}
}
}

+ 1
- 1
ports/dexed/source/EngineOpl.h View File

@@ -31,7 +31,7 @@
class EngineOpl : public FmCore {
public:
virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers);
int32_t *fb_buf, int feedback_shift) override;
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq,


+ 88
- 5
ports/dexed/source/GlobalEditor.cpp View File

@@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.0
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
@@ -59,11 +59,39 @@ public:
}
}
};
class AboutBox : public DialogWindow {
public:
Image about_png;
AboutBox(Component *parent) : DialogWindow("About", Colour(0xFF000000), true) {
setUsingNativeTitleBar(false);
setAlwaysOnTop(true);
about_png = ImageCache::getFromMemory(BinaryData::about_png, BinaryData::about_pngSize);
setSize(about_png.getWidth(), about_png.getHeight());
centreAroundComponent (parent, getWidth(), getHeight());
}
void closeButtonPressed() {
setVisible (false);
}
void paint(Graphics &g) {
g.drawImage (about_png, 0, 0, about_png.getWidth(), about_png.getHeight(),
0, 0, about_png.getWidth(), about_png.getHeight());
g.setColour(Colour(0xFF000000));
String ver("Version " DEXED_VERSION " ; build date " __DATE__ );
g.drawSingleLineText(ver, 18, 130);
}
};
//[/MiscUserDefs]
//==============================================================================
GlobalEditor::GlobalEditor ()
{
//[Constructor_pre] You can add your own custom stuff here..
//[/Constructor_pre]
addAndMakeVisible (lfoSpeed = new Slider ("lfoSpeed"));
lfoSpeed->setRange (0, 99, 1);
lfoSpeed->setSliderStyle (Slider::RotaryVerticalDrag);
@@ -224,6 +252,20 @@ GlobalEditor::GlobalEditor ()
addAndMakeVisible (programSelector = new ProgramSelector());
programSelector->setName ("programSelector");
addAndMakeVisible (aboutButton = new ImageButton ("aboutButton"));
aboutButton->setButtonText (String::empty);
aboutButton->addListener (this);
aboutButton->setImages (false, true, false,
Image(), 1.000f, Colour (0x00000000),
Image(), 1.000f, Colour (0x00000000),
Image(), 1.000f, Colour (0x00000000));
addAndMakeVisible (tune = new Slider ("tune"));
tune->setRange (0, 1, 0);
tune->setSliderStyle (Slider::RotaryVerticalDrag);
tune->setTextBoxStyle (Slider::NoTextBox, true, 80, 20);
tune->addListener (this);
//[UserPreSize]
//[/UserPreSize]
@@ -284,6 +326,8 @@ GlobalEditor::~GlobalEditor()
monoMode = nullptr;
lfoType = nullptr;
programSelector = nullptr;
aboutButton = nullptr;
tune = nullptr;
//[Destructor]. You can add your own custom destruction code here..
@@ -306,6 +350,9 @@ void GlobalEditor::paint (Graphics& g)
void GlobalEditor::resized()
{
//[UserPreResize] Add your own custom resize code here..
//[/UserPreResize]
lfoSpeed->setBounds (564, 50, 34, 34);
lfoAmDepth->setBounds (686, 50, 34, 34);
lfoPitchDepth->setBounds (646, 50, 34, 34);
@@ -338,6 +385,8 @@ void GlobalEditor::resized()
monoMode->setBounds (249, 65, 48, 26);
lfoType->setBounds (583, 8, 36, 26);
programSelector->setBounds (153, 115, 112, 18);
aboutButton->setBounds (8, 11, 135, 46);
tune->setBounds (190, 9, 34, 34);
//[UserResized] Add your own custom resize handling here..
//[/UserResized]
}
@@ -450,6 +499,11 @@ void GlobalEditor::sliderValueChanged (Slider* sliderThatWasMoved)
//[UserSliderCode_output] -- add your slider handling code here..
//[/UserSliderCode_output]
}
else if (sliderThatWasMoved == tune)
{
//[UserSliderCode_tune] -- add your slider handling code here..
//[/UserSliderCode_tune]
}
//[UsersliderValueChanged_Post]
//[/UsersliderValueChanged_Post]
@@ -503,6 +557,13 @@ void GlobalEditor::buttonClicked (Button* buttonThatWasClicked)
repaint();
//[/UserButtonCode_monoMode]
}
else if (buttonThatWasClicked == aboutButton)
{
//[UserButtonCode_aboutButton] -- add your button handler code here..
AboutBox about(this->getParentComponent());
about.runModalLoop();
//[/UserButtonCode_aboutButton]
}
//[UserbuttonClicked_Post]
//[/UserbuttonClicked_Post]
@@ -536,14 +597,17 @@ void GlobalEditor::bind(DexedAudioProcessorEditor *edit) {
processor->fxCutoff->bind(cutoff);
processor->fxReso->bind(reso);
processor->output->bind(output);
algoDisplay->algo = &(processor->data[134]);
processor->tune->bind(tune);
algoDisplay->algo = (char *) &(processor->data[134]);
pitchEnvDisplay->pvalues = &(processor->data[126]);
algoDisplay->opStatus = processor->controllers.opSwitch;
editor = edit;
midiMonitor = new MidiMonitor(&(processor->sysexComm));
addAndMakeVisible(midiMonitor);
midiMonitor->setBounds(155, 21, 80, 45);
//addAndMakeVisible(midiMonitor);
//midiMonitor->setBounds(155, 21, 80, 45);
repaint();
}
@@ -574,6 +638,15 @@ void GlobalEditor::updateVu(float f) {
void GlobalEditor::setMonoState(bool state) {
monoMode->setToggleState(state ? Button::buttonDown : Button::buttonNormal, dontSendNotification);
}
void GlobalEditor::mouseDown(const MouseEvent &e) {
if ( e.mods.isRightButtonDown() || e.mods.isAnyModifierKeyDown() ) {
PopupMenu popup;
popup.addItem(1, "Send current program to DX7");
if ( popup.show() == 1 )
processor->sendCurrentSysexProgram();
}
}
//[/MiscUserCode]
@@ -705,6 +778,16 @@ BEGIN_JUCER_METADATA
<GENERICCOMPONENT name="programSelector" id="990bbcccae72dbe6" memberName="programSelector"
virtualName="" explicitFocusOrder="0" pos="153 115 112 18" class="ProgramSelector"
params=""/>
<IMAGEBUTTON name="aboutButton" id="d195a60b29440aa1" memberName="aboutButton"
virtualName="" explicitFocusOrder="0" pos="8 11 135 46" buttonText=""
connectedEdges="0" needsCallback="1" radioGroupId="0" keepProportions="0"
resourceNormal="" opacityNormal="1" colourNormal="0" resourceOver=""
opacityOver="1" colourOver="0" resourceDown="" opacityDown="1"
colourDown="0"/>
<SLIDER name="tune" id="d22c34aa3363a28a" memberName="tune" virtualName=""
explicitFocusOrder="0" pos="190 9 34 34" min="0" max="1" int="0"
style="RotaryVerticalDrag" textBoxPos="NoTextBox" textBoxEditable="0"
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
</JUCER_COMPONENT>
END_JUCER_METADATA


+ 6
- 2
ports/dexed/source/GlobalEditor.h View File

@@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.0
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
@@ -61,6 +61,8 @@ public:
void setMonoState(bool state);
ProgramSelector *programs;
ScopedPointer<Component> midiMonitor;
void mouseDown(const MouseEvent& e) override;
//[/UserMethods]
void paint (Graphics& g);
@@ -112,6 +114,8 @@ private:
ScopedPointer<ToggleButton> monoMode;
ScopedPointer<ComboBoxImage> lfoType;
ScopedPointer<ProgramSelector> programSelector;
ScopedPointer<ImageButton> aboutButton;
ScopedPointer<Slider> tune;
//==============================================================================


+ 2
- 2
ports/dexed/source/JucePluginCharacteristics.h View File

@@ -33,10 +33,10 @@
#define JucePlugin_ManufacturerEmail ""
#endif
#ifndef JucePlugin_ManufacturerCode
#define JucePlugin_ManufacturerCode 'DGSB'
#define JucePlugin_ManufacturerCode 0x44475342 // 'DGSB'
#endif
#ifndef JucePlugin_PluginCode
#define JucePlugin_PluginCode 'Dexd'
#define JucePlugin_PluginCode 0x44657864 // 'Dexd'
#endif
#ifndef JucePlugin_MaxNumInputChannels
#define JucePlugin_MaxNumInputChannels 0


+ 36
- 7
ports/dexed/source/OperatorEditor.cpp View File

@@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.0
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
@@ -25,13 +25,30 @@
//[MiscUserDefs] You can add your own user definitions and misc code here...
#ifndef M_LN10
#define M_LN10 2.30258509299404568402
#define M_LN10 2.30258509299404568402
#endif
class OperatorSwitch : public ToggleButton {
Image image;
public :
OperatorSwitch() : ToggleButton("opSwitch") {
image = DXLookNFeel::getLookAndFeel()->imageSwitchOperator;
setSize(32, 32);
}
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) {
g.drawImage(image, 0, 0, 32, 32, 0, getToggleState() ? 0 : 32, 32, 32);
}
};
//[/MiscUserDefs]
//==============================================================================
OperatorEditor::OperatorEditor ()
{
//[Constructor_pre] You can add your own custom stuff here..
//[/Constructor_pre]
addAndMakeVisible (s_egl1 = new Slider ("egl1"));
s_egl1->setRange (0, 99, 1);
s_egl1->setSliderStyle (Slider::RotaryVerticalDrag);
@@ -173,6 +190,7 @@ OperatorEditor::OperatorEditor ()
//[UserPreSize]
addAndMakeVisible(opSwitch = new OperatorSwitch());
//[/UserPreSize]
setSize (287, 218);
@@ -233,6 +251,7 @@ OperatorEditor::~OperatorEditor()
//[Destructor]. You can add your own custom destruction code here..
opSwitch = nullptr;
//[/Destructor]
}
@@ -244,9 +263,14 @@ void OperatorEditor::paint (Graphics& g)
//[/UserPrePaint]
//[UserPaint] Add your own custom painting code here..
g.setColour (Colours::white);
if ( opSwitch->getToggleState() )
g.setColour(Colours::white);
else
g.setColour(DXLookNFeel::roundBackground);
g.setFont(Font (30.00f, Font::plain));
g.drawText(opNum, 242, 8, 30, 30, Justification::centred, true);
g.drawText(opNum, 250, 14, 30, 30, Justification::centred, true);
bool state = opMode->getToggleState();
@@ -259,6 +283,9 @@ void OperatorEditor::paint (Graphics& g)
void OperatorEditor::resized()
{
//[UserPreResize] Add your own custom resize code here..
//[/UserPreResize]
s_egl1->setBounds (5, 128, 34, 34);
s_egl2->setBounds (33, 129, 34, 34);
s_egl3->setBounds (61, 128, 34, 34);
@@ -284,6 +311,7 @@ void OperatorEditor::resized()
kbdLeftCurve->setBounds (128, 170, 36, 26);
kbdRightCurve->setBounds (240, 170, 36, 26);
//[UserResized] Add your own custom resize handling here..
opSwitch->setBounds(226, 13, 64, 32);
//[/UserResized]
}
@@ -439,6 +467,7 @@ void OperatorEditor::bind(DexedAudioProcessor *parent, int op) {
parent->opCtrl[op].sclRate->bind(sclRateScaling);
parent->opCtrl[op].ampModSens->bind(ampModSens);
parent->opCtrl[op].velModSens->bind(keyVelSens);
parent->opCtrl[op].opSwitch->bind(opSwitch);
int offset = parent->opCtrl[op].egRate[0]->getOffset();
envDisplay->pvalues = &(parent->data[offset]);
@@ -486,7 +515,7 @@ void OperatorEditor::updateEnvPos(char pos) {
}
void OperatorEditor::mouseDown(const MouseEvent &event) {
if ( event.mods.isRightButtonDown() ) {
if ( event.mods.isRightButtonDown() || event.mods.isAnyModifierKeyDown()) {
PopupMenu popup;
popup.addItem(1, "Copy Operator Values");
@@ -507,7 +536,7 @@ void OperatorEditor::mouseDown(const MouseEvent &event) {
case 3:
processor->pasteOpFromClipboard(internalOp);
break;
case 4:
processor->sendCurrentSysexProgram();
break;


+ 7
- 8
ports/dexed/source/OperatorEditor.h View File

@@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.0
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
@@ -56,10 +56,10 @@ public:
void mouseDown(const MouseEvent& e) override;
//[/UserMethods]
void paint (Graphics& g);
void resized();
void sliderValueChanged (Slider* sliderThatWasMoved);
void buttonClicked (Button* buttonThatWasClicked);
void paint (Graphics& g) override;
void resized() override;
void sliderValueChanged (Slider* sliderThatWasMoved) override;
void buttonClicked (Button* buttonThatWasClicked) override;
@@ -68,10 +68,9 @@ private:
String opNum;
int internalOp;
Image light;
DexedAudioProcessor *processor;
Image background;
ScopedPointer<ToggleButton> opSwitch;
//[/UserVariables]
//==============================================================================


+ 364
- 7
ports/dexed/source/ParamDialog.cpp View File

@@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.0
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
@@ -30,6 +30,9 @@
//==============================================================================
ParamDialog::ParamDialog ()
{
//[Constructor_pre] You can add your own custom stuff here..
//[/Constructor_pre]
addAndMakeVisible (pitchRange = new Slider ("pitchRange"));
pitchRange->setRange (0, 12, 1);
pitchRange->setSliderStyle (Slider::RotaryVerticalDrag);
@@ -67,7 +70,7 @@ ParamDialog::ParamDialog ()
engineReso->setJustificationType (Justification::centredLeft);
engineReso->setTextWhenNothingSelected (String::empty);
engineReso->setTextWhenNoChoicesAvailable (TRANS("(no choices)"));
engineReso->addItem (TRANS("Modern (Direct)"), 1);
engineReso->addItem (TRANS("Modern (24-bit)"), 1);
engineReso->addItem (TRANS("Mark I"), 2);
engineReso->addItem (TRANS("OPL Series"), 3);
engineReso->addListener (this);
@@ -75,11 +78,83 @@ ParamDialog::ParamDialog ()
addAndMakeVisible (showKeyboard = new ToggleButton ("showKeyboard"));
showKeyboard->setButtonText (String::empty);
addAndMakeVisible (whlRange = new Slider ("whlRange"));
whlRange->setRange (0, 99, 1);
whlRange->setSliderStyle (Slider::RotaryVerticalDrag);
whlRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
whlRange->addListener (this);
addAndMakeVisible (ftRange = new Slider ("ftRange"));
ftRange->setRange (0, 99, 1);
ftRange->setSliderStyle (Slider::RotaryVerticalDrag);
ftRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
ftRange->addListener (this);
addAndMakeVisible (brRange = new Slider ("brRange"));
brRange->setRange (0, 99, 1);
brRange->setSliderStyle (Slider::RotaryVerticalDrag);
brRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
brRange->addListener (this);
addAndMakeVisible (atRange = new Slider ("atRange"));
atRange->setRange (0, 99, 1);
atRange->setSliderStyle (Slider::RotaryVerticalDrag);
atRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
atRange->addListener (this);
addAndMakeVisible (whlEg = new ToggleButton ("whlEg"));
whlEg->setButtonText (String::empty);
whlEg->addListener (this);
addAndMakeVisible (ftEg = new ToggleButton ("ftEg"));
ftEg->setButtonText (String::empty);
ftEg->addListener (this);
addAndMakeVisible (brEg = new ToggleButton ("brEg"));
brEg->setButtonText (String::empty);
brEg->addListener (this);
addAndMakeVisible (atEg = new ToggleButton ("atEg"));
atEg->setButtonText (String::empty);
atEg->addListener (this);
addAndMakeVisible (whlAmp = new ToggleButton ("whlAmp"));
whlAmp->setButtonText (String::empty);
whlAmp->addListener (this);
addAndMakeVisible (ftAmp = new ToggleButton ("ftAmp"));
ftAmp->setButtonText (String::empty);
ftAmp->addListener (this);
addAndMakeVisible (brAmp = new ToggleButton ("brAmp"));
brAmp->setButtonText (String::empty);
brAmp->addListener (this);
addAndMakeVisible (atAmp = new ToggleButton ("atAmp"));
atAmp->setButtonText (String::empty);
atAmp->addListener (this);
addAndMakeVisible (whlPitch = new ToggleButton ("whlPitch"));
whlPitch->setButtonText (String::empty);
whlPitch->addListener (this);
addAndMakeVisible (ftPitch = new ToggleButton ("ftPitch"));
ftPitch->setButtonText (String::empty);
ftPitch->addListener (this);
addAndMakeVisible (brPitch = new ToggleButton ("brPitch"));
brPitch->setButtonText (String::empty);
brPitch->addListener (this);
addAndMakeVisible (atPitch = new ToggleButton ("atPitch"));
atPitch->setButtonText (String::empty);
atPitch->addListener (this);
//[UserPreSize]
//[/UserPreSize]
setSize (350, 350);
setSize (710, 350);
//[Constructor] You can add your own custom stuff here..
@@ -110,6 +185,22 @@ ParamDialog::~ParamDialog()
sysexChl = nullptr;
engineReso = nullptr;
showKeyboard = nullptr;
whlRange = nullptr;
ftRange = nullptr;
brRange = nullptr;
atRange = nullptr;
whlEg = nullptr;
ftEg = nullptr;
brEg = nullptr;
atEg = nullptr;
whlAmp = nullptr;
ftAmp = nullptr;
brAmp = nullptr;
atAmp = nullptr;
whlPitch = nullptr;
ftPitch = nullptr;
brPitch = nullptr;
atPitch = nullptr;
//[Destructor]. You can add your own custom destruction code here..
@@ -172,12 +263,60 @@ void ParamDialog::paint (Graphics& g)
20, 96, 276, 23,
Justification::centredLeft, true);
g.setColour (Colours::black);
g.fillRect (352, 11, 1, 325);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("Wheel"),
368, 16, 276, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("Foot"),
368, 96, 276, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("Breath"),
368, 56, 276, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("After Touch"),
368, 136, 276, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("PITCH"),
533, 163, 48, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("AMP"),
589, 163, 48, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("EG BIAS"),
645, 163, 48, 23,
Justification::centredLeft, true);
//[UserPaint] Add your own custom painting code here..
//[/UserPaint]
}
void ParamDialog::resized()
{
//[UserPreResize] Add your own custom resize code here..
//[/UserPreResize]
pitchRange->setBounds (264, 16, 72, 24);
pitchStep->setBounds (264, 56, 72, 24);
sysexIn->setBounds (104, 224, 224, 24);
@@ -185,6 +324,22 @@ void ParamDialog::resized()
sysexChl->setBounds (264, 304, 72, 24);
engineReso->setBounds (160, 156, 168, 24);
showKeyboard->setBounds (264, 96, 56, 24);
whlRange->setBounds (448, 16, 72, 24);
ftRange->setBounds (448, 56, 72, 24);
brRange->setBounds (448, 96, 72, 24);
atRange->setBounds (448, 136, 72, 24);
whlEg->setBounds (640, 16, 56, 24);
ftEg->setBounds (640, 56, 56, 24);
brEg->setBounds (640, 96, 56, 24);
atEg->setBounds (640, 136, 56, 24);
whlAmp->setBounds (584, 16, 56, 24);
ftAmp->setBounds (584, 56, 56, 24);
brAmp->setBounds (584, 96, 56, 24);
atAmp->setBounds (584, 136, 56, 24);
whlPitch->setBounds (528, 16, 56, 24);
ftPitch->setBounds (528, 56, 56, 24);
brPitch->setBounds (528, 96, 56, 24);
atPitch->setBounds (528, 136, 56, 24);
//[UserResized] Add your own custom resize handling here..
//[/UserResized]
}
@@ -210,6 +365,26 @@ void ParamDialog::sliderValueChanged (Slider* sliderThatWasMoved)
//[UserSliderCode_sysexChl] -- add your slider handling code here..
//[/UserSliderCode_sysexChl]
}
else if (sliderThatWasMoved == whlRange)
{
//[UserSliderCode_whlRange] -- add your slider handling code here..
//[/UserSliderCode_whlRange]
}
else if (sliderThatWasMoved == ftRange)
{
//[UserSliderCode_ftRange] -- add your slider handling code here..
//[/UserSliderCode_ftRange]
}
else if (sliderThatWasMoved == brRange)
{
//[UserSliderCode_brRange] -- add your slider handling code here..
//[/UserSliderCode_brRange]
}
else if (sliderThatWasMoved == atRange)
{
//[UserSliderCode_atRange] -- add your slider handling code here..
//[/UserSliderCode_atRange]
}
//[UsersliderValueChanged_Post]
//[/UsersliderValueChanged_Post]
@@ -240,6 +415,76 @@ void ParamDialog::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
//[/UsercomboBoxChanged_Post]
}
void ParamDialog::buttonClicked (Button* buttonThatWasClicked)
{
//[UserbuttonClicked_Pre]
//[/UserbuttonClicked_Pre]
if (buttonThatWasClicked == whlEg)
{
//[UserButtonCode_whlEg] -- add your button handler code here..
//[/UserButtonCode_whlEg]
}
else if (buttonThatWasClicked == ftEg)
{
//[UserButtonCode_ftEg] -- add your button handler code here..
//[/UserButtonCode_ftEg]
}
else if (buttonThatWasClicked == brEg)
{
//[UserButtonCode_brEg] -- add your button handler code here..
//[/UserButtonCode_brEg]
}
else if (buttonThatWasClicked == atEg)
{
//[UserButtonCode_atEg] -- add your button handler code here..
//[/UserButtonCode_atEg]
}
else if (buttonThatWasClicked == whlAmp)
{
//[UserButtonCode_whlAmp] -- add your button handler code here..
//[/UserButtonCode_whlAmp]
}
else if (buttonThatWasClicked == ftAmp)
{
//[UserButtonCode_ftAmp] -- add your button handler code here..
//[/UserButtonCode_ftAmp]
}
else if (buttonThatWasClicked == brAmp)
{
//[UserButtonCode_brAmp] -- add your button handler code here..
//[/UserButtonCode_brAmp]
}
else if (buttonThatWasClicked == atAmp)
{
//[UserButtonCode_atAmp] -- add your button handler code here..
//[/UserButtonCode_atAmp]
}
else if (buttonThatWasClicked == whlPitch)
{
//[UserButtonCode_whlPitch] -- add your button handler code here..
//[/UserButtonCode_whlPitch]
}
else if (buttonThatWasClicked == ftPitch)
{
//[UserButtonCode_ftPitch] -- add your button handler code here..
//[/UserButtonCode_ftPitch]
}
else if (buttonThatWasClicked == brPitch)
{
//[UserButtonCode_brPitch] -- add your button handler code here..
//[/UserButtonCode_brPitch]
}
else if (buttonThatWasClicked == atPitch)
{
//[UserButtonCode_atPitch] -- add your button handler code here..
//[/UserButtonCode_atPitch]
}
//[UserbuttonClicked_Post]
//[/UserbuttonClicked_Post]
}
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
@@ -249,6 +494,27 @@ void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr, int reso, bool
pitchStep->setValue(c.values_[kControllerPitchStep]);
sysexChl->setValue(mgr.getChl() + 1);
whlRange->setValue(c.wheel.range);
whlPitch->setToggleState(c.wheel.pitch, dontSendNotification);
whlPitch->setToggleState(c.wheel.pitch, dontSendNotification);
whlAmp->setToggleState(c.wheel.amp, dontSendNotification);
whlEg->setToggleState(c.wheel.eg, dontSendNotification);
ftRange->setValue(c.foot.range);
ftPitch->setToggleState(c.foot.pitch, dontSendNotification);
ftAmp->setToggleState(c.foot.amp, dontSendNotification);
ftEg->setToggleState(c.foot.eg, dontSendNotification);
brRange->setValue(c.breath.range);
brPitch->setToggleState(c.breath.pitch, dontSendNotification);
brAmp->setToggleState(c.breath.amp, dontSendNotification);
brEg->setToggleState(c.breath.eg, dontSendNotification);
atRange->setValue(c.at.range);
atPitch->setToggleState(c.at.pitch, dontSendNotification);
atAmp->setToggleState(c.at.amp, dontSendNotification);
atEg->setToggleState(c.at.eg, dontSendNotification);
StringArray inputs = MidiInput::getDevices();
int idx = inputs.indexOf(mgr.getInput());
idx = idx == -1 ? 0 : idx + 1;
@@ -260,7 +526,7 @@ void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr, int reso, bool
sysexOut->setSelectedItemIndex(idx);
engineReso->setSelectedItemIndex(reso);
showKeyboard->setToggleState(showKey, dontSendNotification);
showKeyboard->setToggleState(showKey, NotificationType::dontSendNotification);
}
bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso, bool *showKey) {
@@ -268,11 +534,35 @@ bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso, boo
c.values_[kControllerPitchRange] = pitchRange->getValue();
c.values_[kControllerPitchStep] = pitchStep->getValue();
c.wheel.range = whlRange->getValue();
c.wheel.pitch = whlPitch->getToggleState();
c.wheel.amp = whlAmp->getToggleState();
c.wheel.eg = whlEg->getToggleState();
c.foot.range = ftRange->getValue();
c.foot.pitch = ftPitch->getToggleState();
c.foot.amp = ftAmp->getToggleState();
c.foot.eg = ftEg->getToggleState();
c.breath.range = brRange->getValue();
c.breath.pitch = brPitch->getToggleState();
c.breath.amp = brAmp->getToggleState();
c.breath.eg = brEg->getToggleState();
c.at.range = atRange->getValue();
c.at.pitch = atPitch->getToggleState();
c.at.amp = atAmp->getToggleState();
c.at.eg = atEg->getToggleState();
c.refresh();
ret &= mgr.setInput(sysexIn->getItemText(sysexIn->getSelectedItemIndex()));
ret &= mgr.setOutput(sysexOut->getItemText(sysexOut->getSelectedItemIndex()));
mgr.setChl(sysexChl->getValue() - 1);
*reso = engineReso->getSelectedItemIndex();
// *showKey = showKeyboard->getToggleStateValue() == Button::ButtonState::buttonDown;
*showKey = showKeyboard->getToggleState();
return ret;
}
@@ -292,7 +582,7 @@ BEGIN_JUCER_METADATA
<JUCER_COMPONENT documentType="Component" className="ParamDialog" componentName=""
parentClasses="public Component" constructorParams="" variableInitialisers=""
snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.330"
fixedSize="1" initialWidth="350" initialHeight="350">
fixedSize="1" initialWidth="710" initialHeight="350">
<BACKGROUND backgroundColour="ff3c322f">
<TEXT pos="20 16 276 23" fill="solid: ffffffff" hasStroke="0" text="Pitch Bend Range"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
@@ -310,6 +600,21 @@ BEGIN_JUCER_METADATA
<RECT pos="22 195 306 1" fill="solid: ff000000" hasStroke="0"/>
<TEXT pos="20 96 276 23" fill="solid: ffffffff" hasStroke="0" text="Show Keyboard"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<RECT pos="352 11 1 325" fill="solid: ff000000" hasStroke="0"/>
<TEXT pos="368 16 276 23" fill="solid: ffffffff" hasStroke="0" text="Wheel"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="368 96 276 23" fill="solid: ffffffff" hasStroke="0" text="Foot"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="368 56 276 23" fill="solid: ffffffff" hasStroke="0" text="Breath"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="368 136 276 23" fill="solid: ffffffff" hasStroke="0" text="After Touch"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="533 163 48 23" fill="solid: ffffffff" hasStroke="0" text="PITCH"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="589 163 48 23" fill="solid: ffffffff" hasStroke="0" text="AMP"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="645 163 48 23" fill="solid: ffffffff" hasStroke="0" text="EG BIAS"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
</BACKGROUND>
<SLIDER name="pitchRange" id="7409be5a8dfaa91" memberName="pitchRange"
virtualName="" explicitFocusOrder="0" pos="264 16 72 24" min="0"
@@ -331,11 +636,63 @@ BEGIN_JUCER_METADATA
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<COMBOBOX name="new combo box" id="4087ff978c3d9e8d" memberName="engineReso"
virtualName="" explicitFocusOrder="0" pos="160 156 168 24" editable="0"
layout="33" items="Modern (Direct)&#10;Mark I&#10;OPL Series"
layout="33" items="Modern (24-bit)&#10;Mark I&#10;OPL Series"
textWhenNonSelected="" textWhenNoItems="(no choices)"/>
<TOGGLEBUTTON name="showKeyboard" id="c963d2cb8e49ffd7" memberName="showKeyboard"
virtualName="" explicitFocusOrder="0" pos="264 96 56 24" buttonText=""
connectedEdges="0" needsCallback="0" radioGroupId="0" state="0"/>
<SLIDER name="whlRange" id="3d6522f5f581e580" memberName="whlRange" virtualName=""
explicitFocusOrder="0" pos="448 16 72 24" min="0" max="99" int="1"
style="RotaryVerticalDrag" textBoxPos="TextBoxLeft" textBoxEditable="1"
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<SLIDER name="ftRange" id="cf553f74c3fb0d12" memberName="ftRange" virtualName=""
explicitFocusOrder="0" pos="448 56 72 24" min="0" max="99" int="1"
style="RotaryVerticalDrag" textBoxPos="TextBoxLeft" textBoxEditable="1"
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<SLIDER name="brRange" id="c4aa6814f75016a7" memberName="brRange" virtualName=""
explicitFocusOrder="0" pos="448 96 72 24" min="0" max="99" int="1"
style="RotaryVerticalDrag" textBoxPos="TextBoxLeft" textBoxEditable="1"
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<SLIDER name="atRange" id="d0aa1ebb24284577" memberName="atRange" virtualName=""
explicitFocusOrder="0" pos="448 136 72 24" min="0" max="99" int="1"
style="RotaryVerticalDrag" textBoxPos="TextBoxLeft" textBoxEditable="1"
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<TOGGLEBUTTON name="whlEg" id="d8242ae592c912a" memberName="whlEg" virtualName=""
explicitFocusOrder="0" pos="640 16 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="ftEg" id="69d816607bd71cb0" memberName="ftEg" virtualName=""
explicitFocusOrder="0" pos="640 56 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="brEg" id="ba89ae54d676983f" memberName="brEg" virtualName=""
explicitFocusOrder="0" pos="640 96 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="atEg" id="371934a58ce5f1bc" memberName="atEg" virtualName=""
explicitFocusOrder="0" pos="640 136 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="whlAmp" id="3d4e46e63c3ddd86" memberName="whlAmp" virtualName=""
explicitFocusOrder="0" pos="584 16 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="ftAmp" id="f631892e209b094a" memberName="ftAmp" virtualName=""
explicitFocusOrder="0" pos="584 56 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="brAmp" id="d0a68d37220638f1" memberName="brAmp" virtualName=""
explicitFocusOrder="0" pos="584 96 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="atAmp" id="4220c7b22e7845ea" memberName="atAmp" virtualName=""
explicitFocusOrder="0" pos="584 136 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="whlPitch" id="b7a626ec1e45af16" memberName="whlPitch" virtualName=""
explicitFocusOrder="0" pos="528 16 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="ftPitch" id="1acedf6f16a5a3" memberName="ftPitch" virtualName=""
explicitFocusOrder="0" pos="528 56 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="brPitch" id="23fa82533e004b96" memberName="brPitch" virtualName=""
explicitFocusOrder="0" pos="528 96 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
<TOGGLEBUTTON name="atPitch" id="43805c6a4673e291" memberName="atPitch" virtualName=""
explicitFocusOrder="0" pos="528 136 56 24" buttonText="" connectedEdges="0"
needsCallback="1" radioGroupId="0" state="0"/>
</JUCER_COMPONENT>
END_JUCER_METADATA


+ 21
- 3
ports/dexed/source/ParamDialog.h View File

@@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.0
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
@@ -38,7 +38,8 @@
*/
class ParamDialog : public Component,
public SliderListener,
public ComboBoxListener
public ComboBoxListener,
public ButtonListener
{
public:
//==============================================================================
@@ -55,6 +56,7 @@ public:
void resized();
void sliderValueChanged (Slider* sliderThatWasMoved);
void comboBoxChanged (ComboBox* comboBoxThatHasChanged);
void buttonClicked (Button* buttonThatWasClicked);
@@ -70,6 +72,22 @@ private:
ScopedPointer<Slider> sysexChl;
ScopedPointer<ComboBox> engineReso;
ScopedPointer<ToggleButton> showKeyboard;
ScopedPointer<Slider> whlRange;
ScopedPointer<Slider> ftRange;
ScopedPointer<Slider> brRange;
ScopedPointer<Slider> atRange;
ScopedPointer<ToggleButton> whlEg;
ScopedPointer<ToggleButton> ftEg;
ScopedPointer<ToggleButton> brEg;
ScopedPointer<ToggleButton> atEg;
ScopedPointer<ToggleButton> whlAmp;
ScopedPointer<ToggleButton> ftAmp;
ScopedPointer<ToggleButton> brAmp;
ScopedPointer<ToggleButton> atAmp;
ScopedPointer<ToggleButton> whlPitch;
ScopedPointer<ToggleButton> ftPitch;
ScopedPointer<ToggleButton> brPitch;
ScopedPointer<ToggleButton> atPitch;
//==============================================================================


+ 130
- 138
ports/dexed/source/PluginData.cpp View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2014-2015 Pascal Gauthier.
* Copyright (c) 2014-2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
#include <fstream>
using namespace ::std;
uint8_t sysexChecksum(const char *sysex, int size) {
uint8_t sysexChecksum(const uint8_t *sysex, int size) {
int sum = 0;
int i;
@@ -36,71 +36,14 @@ uint8_t sysexChecksum(const char *sysex, int size) {
return sum & 0x7F;
}
String normalizeSysexName(const char *sysexName) {
char buffer[11];
memcpy(buffer, sysexName, 10);
for (int j = 0; j < 10; j++) {
char c = (unsigned char) buffer[j];
switch (c) {
case 92:
c = 'Y';
break; /* yen */
case 126:
c = '>';
break; /* >> */
case 127:
c = '<';
break; /* << */
default:
if (c < 32 || c > 127)
c = 32;
break;
}
buffer[j] = c;
}
buffer[10] = 0;
return String(buffer);
}
void extractProgramNames(const char *block, StringArray &dest) {
dest.clear();
for (int i = 0; i < 32; i++) {
dest.add(String(normalizeSysexName(block + ((i * 128) + 118))));
}
}
void exportSysexCart(char *dest, char *src, char sysexChl) {
uint8_t header[] = { 0xF0, 0x43, 0x00, 0x09, 0x20, 0x00 };
header[2] = sysexChl;
memcpy(dest, header, 6);
// copy 32 voices
memcpy(dest+6, src, 4096);
// make checksum for dump
uint8_t footer[] = { sysexChecksum(src, 4096), 0xF7 };
memcpy(dest+4102, footer, 2);
}
void exportSysexPgm(char *dest, char *src, char sysexChl) {
void exportSysexPgm(uint8_t *dest, uint8_t *src) {
uint8_t header[] = { 0xF0, 0x43, 0x00, 0x00, 0x01, 0x1B };
header[2] = sysexChl;
memcpy(dest, header, 6);
// copy 1 unpacked voices
memcpy(dest+6, src, 155);
// put some logic to "mute" an operator if the level is 0
// make checksum for dump
uint8_t footer[] = { sysexChecksum(src, 155), 0xF7 };
@@ -110,8 +53,8 @@ void exportSysexPgm(char *dest, char *src, char sysexChl) {
/**
* Pack a program into a 32 packed sysex
*/
void packProgram(uint8_t *dest, uint8_t *src, int idx, String name) {
uint8_t *bulk = dest + (idx * 128);
void Cartridge::packProgram(uint8_t *src, int idx, String name, char *opSwitch) {
uint8_t *bulk = voiceData + 6 + (idx * 128);
for(int op = 0; op < 6; op++) {
// eg rate and level, brk pt, depth, scaling
@@ -125,7 +68,10 @@ void packProgram(uint8_t *dest, uint8_t *src, int idx, String name) {
// kvs_ams
bulk[pp+13] = (src[up+14]&0x03) | ((src[up+15]&0x07) << 2);
// output lvl
bulk[pp+14] = src[up+16];
if ( opSwitch[op] == '0' )
bulk[pp+14] = 0;
else
bulk[pp+14] = src[up+16];
// fcoarse_mode
bulk[pp+15] = (src[up+17]&0x01) | ((src[up+18]&0x1f) << 1);
// fine freq
@@ -171,8 +117,9 @@ char normparm(char value, char max, int id) {
return v;
}
void unpackProgramFromSysex(char *unpackPgm, char *sysexCart, int idx) {
char *bulk = sysexCart + (idx * 128);
void Cartridge::unpackProgram(uint8_t *unpackPgm, int idx) {
// TODO put this in uint8_t :D
char *bulk = (char *)voiceData + 6 + (idx * 128);
for (int op = 0; op < 6; op++) {
// eg rate and level, brk pt, depth, scaling
@@ -212,61 +159,57 @@ void unpackProgramFromSysex(char *unpackPgm, char *sysexCart, int idx) {
unpackPgm[142] = (lpms_lfw_lks >> 1) & 7;
unpackPgm[143] = lpms_lfw_lks >> 4;
memcpy(unpackPgm + 144, bulk + 117, 11); // transpose, name
unpackPgm[155] = 1; // operator on/off
unpackPgm[156] = 1;
unpackPgm[157] = 1;
unpackPgm[158] = 1;
unpackPgm[159] = 1;
unpackPgm[160] = 1;
unpackPgm[155] = 63; // operator on/off (DEPRECATED)
}
void DexedAudioProcessor::unpackProgram(int idx) {
unpackProgramFromSysex(data, sysex, idx);
void DexedAudioProcessor::loadCartridge(Cartridge &sysex) {
currentCart = sysex;
currentCart.getProgramNames(programNames);
}
int DexedAudioProcessor::importSysex(const char *imported) {
memcpy(sysex, imported + 6, 4096);
uint8_t checksum = sysexChecksum(((char *) &sysex), 4096);
extractProgramNames(sysex, programNames);
if ( checksum != imported[4102] ) {
TRACE("sysex import checksum doesnt match %d != %d", checksum, imported[4102]);
return 1;
}
return 0;
void DexedAudioProcessor::packOpSwitch() {
char value = (controllers.opSwitch[5] == '1') << 5;
value += (controllers.opSwitch[4] == '1') << 4;
value += (controllers.opSwitch[3] == '1') << 3;
value += (controllers.opSwitch[2] == '1') << 2;
value += (controllers.opSwitch[1] == '1') << 1;
value += (controllers.opSwitch[0] == '1');
data[155] = value;
}
void DexedAudioProcessor::updateProgramFromSysex(const uint8 *rawdata) {
memcpy(data, rawdata, 161);
void DexedAudioProcessor::unpackOpSwitch(char packOpValue) {
controllers.opSwitch[5] = (packOpValue & 32) + 48;
controllers.opSwitch[4] = (packOpValue & 16) + 48;
controllers.opSwitch[3] = (packOpValue & 8) + 48;
controllers.opSwitch[2] = (packOpValue & 4) + 48;
controllers.opSwitch[1] = (packOpValue & 2) + 48;
controllers.opSwitch[0] = (packOpValue & 1) + 48;
}
void DexedAudioProcessor::updateProgramFromSysex(const uint8_t *rawdata) {
memcpy(data, rawdata, 155);
unpackOpSwitch(rawdata[155]);
lfo.reset(data + 137);
triggerAsyncUpdate();
}
void DexedAudioProcessor::setupStartupCart() {
char syx_data[4104];
memset(&syx_data, 0, 4104);
File startup = dexedCartDir.getChildFile("Dexed_01.syx");
if ( currentCart.load(startup) != -1 )
return;
if ( startup.exists() ) {
FileInputStream *fis = startup.createInputStream();
if ( fis == nullptr ) {
TRACE("unable to open default cartridge");
return;
}
fis->read(syx_data, 4104);
delete fis;
} else {
// The user deleted the file :/, load from the builtin zip file.
MemoryInputStream *mis = new MemoryInputStream(BinaryData::builtin_pgm_zip, BinaryData::builtin_pgm_zipSize, false);
ZipFile *builtin_pgm = new ZipFile(mis, true);
InputStream *is = builtin_pgm->createStreamForEntry(builtin_pgm->getIndexOfFileName(("Dexed_01.syx")));
is->read(syx_data, 4104);
delete is;
delete builtin_pgm;
}
importSysex((char *) &syx_data);
// The user deleted the file :/, load from the builtin zip file.
MemoryInputStream *mis = new MemoryInputStream(BinaryData::builtin_pgm_zip, BinaryData::builtin_pgm_zipSize, false);
ZipFile *builtin_pgm = new ZipFile(mis, true);
InputStream *is = builtin_pgm->createStreamForEntry(builtin_pgm->getIndexOfFileName(("Dexed_01.syx")));
Cartridge init;
if ( init.load(*is) != -1 )
loadCartridge(init);
delete is;
delete builtin_pgm;
}
void DexedAudioProcessor::resetToInitVoice() {
@@ -303,9 +246,10 @@ void DexedAudioProcessor::pasteEnvFromClipboard(int destOp) {
}
void DexedAudioProcessor::sendCurrentSysexProgram() {
uint8_t raw[167];
uint8_t raw[163];
exportSysexPgm((char *) raw, data, sysexComm.getChl());
packOpSwitch();
exportSysexPgm(raw, data);
if ( sysexComm.isOutputActive() ) {
sysexComm.send(MidiMessage(raw, 163));
}
@@ -314,7 +258,7 @@ void DexedAudioProcessor::sendCurrentSysexProgram() {
void DexedAudioProcessor::sendCurrentSysexCartridge() {
uint8_t raw[4104];
exportSysexCart((char *) raw, (char *) &sysex, sysexComm.getChl());
currentCart.saveVoice(raw);
if ( sysexComm.isOutputActive() ) {
sysexComm.send(MidiMessage(raw, 4104));
}
@@ -323,18 +267,27 @@ void DexedAudioProcessor::sendCurrentSysexCartridge() {
void DexedAudioProcessor::sendSysexCartridge(File cart) {
if ( ! sysexComm.isOutputActive() )
return;
String f = cart.getFullPathName();
uint8_t syx_data[4104];
ifstream fp_in(f.toRawUTF8(), ios::binary);
if (fp_in.fail()) {
FileInputStream *fis = cart.createInputStream();
if ( fis == NULL ) {
String f = cart.getFullPathName();
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Error",
"Unable to open: " + f);
}
uint8 syx_data[65535];
int sz = fis->read(syx_data, 65535);
delete fis;
if (syx_data[0] != 0xF0) {
String f = cart.getFullPathName();
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Error",
"File: " + f + " doesn't seems to contain any sysex data");
return;
}
fp_in.read((char *)syx_data, 4104);
fp_in.close();
sysexComm.send(MidiMessage(syx_data, 4104));
sysexComm.send(MidiMessage(syx_data, sz));
}
@@ -359,15 +312,24 @@ void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) {
dexedState.setAttribute("currentProgram", currentProgram);
dexedState.setAttribute("monoMode", monoMode);
dexedState.setAttribute("engineType", (int) engineType);
dexedState.setAttribute("masterTune", controllers.masterTune);
dexedState.setAttribute("opSwitch", controllers.opSwitch);
char mod_cfg[15];
controllers.wheel.setConfig(mod_cfg);
dexedState.setAttribute("wheelMod", mod_cfg);
controllers.foot.setConfig(mod_cfg);
dexedState.setAttribute("footMod", mod_cfg);
controllers.breath.setConfig(mod_cfg);
dexedState.setAttribute("breathMod", mod_cfg);
controllers.at.setConfig(mod_cfg);
dexedState.setAttribute("aftertouchMod", mod_cfg);
if ( activeFileCartridge.exists() )
dexedState.setAttribute("activeFileCartridge", activeFileCartridge.getFullPathName());
char sysex_blob[4104];
exportSysexCart((char *) &sysex_blob, (char *) sysex, 0);
NamedValueSet blobSet;
blobSet.set("sysex", var((void *) &sysex_blob, 4104));
blobSet.set("sysex", var((void *) currentCart.getVoiceSysex(), 4104));
blobSet.set("program", var((void *) &data, 161));
blobSet.copyToXmlAttributes(*dexedBlob);
@@ -391,8 +353,23 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte
fx.uiGain = root->getDoubleAttribute("gain");
currentProgram = root->getIntAttribute("currentProgram");
setEngineType(root->getIntAttribute("engineType", 0));
String opSwitchValue = root->getStringAttribute("opSwitch");
if ( opSwitchValue.length() != 6 ) {
strcpy(controllers.opSwitch, "111111");
} else {
strncpy(controllers.opSwitch, opSwitchValue.toRawUTF8(), 6);
}
controllers.wheel.parseConfig(root->getStringAttribute("wheelMod").toRawUTF8());
controllers.foot.parseConfig(root->getStringAttribute("footMod").toRawUTF8());
controllers.breath.parseConfig(root->getStringAttribute("breathMod").toRawUTF8());
controllers.at.parseConfig(root->getStringAttribute("aftertouchMod").toRawUTF8());
controllers.refresh();
setEngineType(root->getIntAttribute("engineType", 1));
monoMode = root->getIntAttribute("monoMode", 0);
controllers.masterTune = root->getIntAttribute("masterTune", 0);
File possibleCartridge = File(root->getStringAttribute("activeFileCartridge"));
if ( possibleCartridge.exists() )
@@ -415,7 +392,9 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte
return;
}
importSysex((char *) sysex_blob.getBinaryData()->getData());
Cartridge cart;
cart.load((uint8 *)sysex_blob.getBinaryData()->getData(), 4104);
loadCartridge(cart);
memcpy(data, program.getBinaryData()->getData(), 161);
lastStateSave = (long) time(NULL);
@@ -427,19 +406,32 @@ File DexedAudioProcessor::dexedAppDir;
File DexedAudioProcessor::dexedCartDir;
void DexedAudioProcessor::resolvAppDir() {
#if JUCE_MAC || JUCE_IOS
File parent = File::getSpecialLocation(File::currentExecutableFile).getParentDirectory().getParentDirectory().getParentDirectory().getSiblingFile("Dexed");
#if JUCE_MAC || JUCE_IOS
dexedAppDir = File("~/Library/Application Support/DigitalSuburban/Dexed");
#elif JUCE_WINDOWS
dexedAppDir = File::getSpecialLocation(File::userApplicationDataDirectory).getChildFile("DigitalSuburban").getChildFile("Dexed");
#else
char *xdgHome = getenv("XDG_DATA_HOME");
if ( xdgHome == nullptr ) {
dexedAppDir = File("~/.local/share").getChildFile("DigitalSuburban").getChildFile("Dexed");
} else {
dexedAppDir = File(xdgHome).getChildFile("DigitalSuburban").getChildFile("Dexed");
}
#endif
if ( parent.isDirectory() ) {
dexedAppDir = parent;
} else {
dexedAppDir = File("~/Library/Application Support/DigitalSuburban/Dexed");
}
#elif JUCE_WINDOWS
if ( File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("Dexed").isDirectory() ) {
dexedAppDir = File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("Dexed");
} else {
dexedAppDir = File::getSpecialLocation(File::userApplicationDataDirectory).getChildFile("DigitalSuburban").getChildFile("Dexed");
}
#else
if ( File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("Dexed").isDirectory() ) {
dexedAppDir = File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("Dexed");
} else {
char *xdgHome = getenv("XDG_DATA_HOME");
if ( xdgHome == nullptr ) {
dexedAppDir = File("~/.local/share").getChildFile("DigitalSuburban").getChildFile("Dexed");
} else {
dexedAppDir = File(xdgHome).getChildFile("DigitalSuburban").getChildFile("Dexed");
}
}
#endif
if ( ! dexedAppDir.exists() ) {
dexedAppDir.createDirectory();


+ 220
- 40
ports/dexed/source/PluginData.h View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2014-2015 Pascal Gauthier.
* Copyright (c) 2014-2016 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,49 +22,229 @@
#define PLUGINDATA_H_INCLUDED
#include "JuceHeader.h"
#define SYSEX_SIZE 4104
#include <stdint.h>
#include <string.h>
#include "Dexed.h"
enum UnpackedOffset {
egRate,
egLevel = 4,
breakpoint = 8,
lScaleDepth,
rScaleDepth,
lKeyScale,
rKeyScale,
rateScaling,
keyVelocity,
outputLevel,
mode,
fCoarse,
fFine,
oscDetune,
uint8_t sysexChecksum(const uint8_t *sysex, int size);
void exportSysexPgm(uint8_t *dest, uint8_t *src);
// Global values
pitchEgRate = 126,
pitchEgLevel = 130,
algorythm = 134,
feedback,
oscKeySync,
lfoSpeed,
lfoDelay,
lfoPmDepth,
lfoAmDepth,
lfoKeySync,
lfoWave,
middleC,
pModeSens,
osc6state
};
#define SYSEX_HEADER { 0xF0, 0x43, 0x00, 0x09, 0x20, 0x00 }
#define SYSEX_SIZE 4104
String normalizeSysexName(const char *sysexName);
uint8_t sysexChecksum(const char *sysex, int size);
void extractProgramNames(const char *block, StringArray &dest);
void exportSysexCart(char *dest, char *src, char sysexChl);
void exportSysexPgm(char *dest, char *src, char sysexChl);
void packProgram(uint8_t *dest, uint8_t *src, int idx, String name);
void unpackProgramFromSysex(char *unpackPgm, char *sysexCart, int idx);
class Cartridge {
uint8_t voiceData[SYSEX_SIZE];
uint8_t perfData[SYSEX_SIZE];
void setHeader() {
uint8 voiceHeader[] = SYSEX_HEADER;
memcpy(voiceData, voiceHeader, 6);
voiceData[4102] = sysexChecksum(voiceData+6, 4096);
voiceData[4103] = 0xF7;
}
public:
Cartridge() { }
Cartridge(const Cartridge &cpy) {
memcpy(voiceData, cpy.voiceData, SYSEX_SIZE);
memcpy(perfData, cpy.perfData, SYSEX_SIZE);
}
static String normalizePgmName(const char *sysexName) {
char buffer[11];
memcpy(buffer, sysexName, 10);
for (int j = 0; j < 10; j++) {
char c = (unsigned char) buffer[j];
switch (c) {
case 92:
c = 'Y';
break; /* yen */
case 126:
c = '>';
break; /* >> */
case 127:
c = '<';
break; /* << */
default:
if (c < 32 || c > 127)
c = 32;
break;
}
buffer[j] = c;
}
buffer[10] = 0;
return String(buffer);
}
int load(File f) {
FileInputStream *fis = f.createInputStream();
if ( fis == NULL )
return -1;
int rc = load(*fis);
delete fis;
return rc;
}
/**
* Loads sysex stream
* Returns 0 if it was parsed sucessfully
* Returns -1 if it cannot open the stream
*/
int load(InputStream &fis) {
uint8 buffer[65535];
int sz = fis.read(buffer, 65535);
if ( sz == 0 )
return -1;
return load(buffer, sz);
}
/**
* Loads sysex buffer
* Returns 0 if it was parsed sucessfully
* Returns 1 if sysex checksum didn't match
* Returns 2 if no sysex data found, probably random data
*/
int load(const uint8_t *stream, int size) {
const uint8 *pos = stream;
if ( size < 4096 ) {
memcpy(voiceData+6, pos, size);
TRACE("too small sysex rc=2");
return 2;
}
if ( pos[0] != 0xF0 ) {
// it is not, just copy the first 4096 bytes
memcpy(voiceData + 6, pos, 4096);
TRACE("stream is not a sysex rc=2");
return 2;
}
// limit the size of the sysex scan
if ( size > 65535 )
size = 65535;
// we loop until we find something that looks like a DX7 cartridge (based on size)
while(size >= 4104) {
// it was a sysex first, now random data; return random
if ( pos[0] != 0xF0 ) {
memcpy(voiceData + 6, stream, 4096);
TRACE("stream was a sysex, but not anymore rc=2");
return 2;
}
// check if this is the size of a DX7 sysex cartridge
for(int i=0;i<size;i++) {
if ( pos[i] == 0xF7 ) {
if ( i == SYSEX_SIZE - 1 ) {
memcpy(voiceData, pos, SYSEX_SIZE);
if ( sysexChecksum(voiceData + 6, 4096) == pos[4102] ) {
TRACE("valid sysex found!");
return 0;
} else {
TRACE("sysex found, but checksum doesn't match rc=1");
return 1;
}
}
size -= i;
pos += i;
TRACE("end of sysex with wrong DX size... still scanning stream: size=%d", i);
break;
}
}
TRACE("sysex stream parsed without any end message, skipping...");
break;
}
// it is a sysex, but doesn't seems to be related to any DX series ...
memcpy(voiceData + 6, stream, 4096);
TRACE("nothing in the sysex stream was DX related rc=2");
return 2;
}
int saveVoice(File f) {
setHeader();
if ( ! f.existsAsFile() ) {
// file doesn't exists, create it
return f.replaceWithData(voiceData, SYSEX_SIZE);
}
FileInputStream *fis = f.createInputStream();
if ( fis == NULL )
return -1;
uint8 buffer[65535];
int sz = fis->read(buffer, 65535);
delete fis;
// if the file is smaller than 4104, it probably needs to be overriden.
if ( sz <= 4104 ) {
return f.replaceWithData(voiceData, SYSEX_SIZE);
}
// To avoid to erase the performance data, we skip the sysex stream until
// we see the header 0xF0, 0x43, 0x00, 0x09, 0x20, 0x00
int pos = 0;
bool found = 0;
while(pos < sz) {
// corrupted sysex, erase everything :
if ( buffer[pos] != 0xF0 )
return f.replaceWithData(voiceData, SYSEX_SIZE);
uint8_t header[] = SYSEX_HEADER;
if ( memcmp(buffer+pos, header, 6) ) {
found = true;
memcpy(buffer+pos, voiceData, SYSEX_SIZE);
break;
} else {
for(;pos<sz;pos++) {
if ( buffer[pos] == 0xF7 )
break;
}
}
}
if ( ! found )
return -1;
return f.replaceWithData(buffer, sz);
}
void saveVoice(uint8_t *sysex) {
setHeader();
memcpy(sysex, voiceData, SYSEX_SIZE);
}
char *getRawVoice() {
return (char *) voiceData + 6;
}
char *getVoiceSysex() {
setHeader();
return (char *) voiceData;
}
void getProgramNames(StringArray &dest) {
dest.clear();
for (int i = 0; i < 32; i++)
dest.add( normalizePgmName(getRawVoice() + ((i * 128) + 118)) );
}
Cartridge operator =(const Cartridge other) {
memcpy(voiceData, other.voiceData, SYSEX_SIZE);
memcpy(perfData, other.perfData, SYSEX_SIZE);
return *this;
}
void unpackProgram(uint8_t *unpackPgm, int idx);
void packProgram(uint8_t *src, int idx, String name, char *opSwitch);
};
#endif // PLUGINDATA_H_INCLUDED

+ 34
- 73
ports/dexed/source/PluginEditor.cpp View File

@@ -30,33 +30,6 @@
#include "msfa/fm_op_kernel.h"
using namespace ::std;
class AboutBox : public DialogWindow {
public:
Image about_png;
AboutBox(Component *parent) : DialogWindow("About", Colour(0xFF000000), true) {
setUsingNativeTitleBar(false);
setAlwaysOnTop(true);
about_png = ImageCache::getFromMemory(BinaryData::about_png, BinaryData::about_pngSize);
setSize(about_png.getWidth(), about_png.getHeight());
centreAroundComponent (parent, getWidth(), getHeight());
}
void closeButtonPressed() {
setVisible (false);
}
void paint(Graphics &g) {
g.drawImage (about_png, 0, 0, about_png.getWidth(), about_png.getHeight(),
0, 0, about_png.getWidth(), about_png.getHeight());
g.setColour(Colour(0xFF000000));
String ver("Version " DEXED_VERSION " ; built date " __DATE__ );
g.drawSingleLineText(ver, 9, 118);
}
};
//==============================================================================
DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* ownerFilter)
: AudioProcessorEditor (ownerFilter),
@@ -140,22 +113,28 @@ void DexedAudioProcessorEditor::cartShow() {
void DexedAudioProcessorEditor::loadCart(File file) {
String f = file.getFullPathName();
uint8_t syx_data[4104];
ifstream fp_in(f.toRawUTF8(), ios::binary);
if (fp_in.fail()) {
Cartridge cart;
int rc = cart.load(file);
if ( rc < 0 ) {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Error",
"Unable to open: " + f);
"Unable to open: " + file.getFullPathName());
return;
}
fp_in.read((char *)syx_data, 4104);
fp_in.close();
if ( processor->importSysex((char *) &syx_data) ) {
global.setSystemMessage(String("Unkown sysex format !?"));
if ( rc != 0 ) {
rc = AlertWindow::showOkCancelBox(AlertWindow::QuestionIcon, "Unable to find DX7 sysex cartridge in file",
"This sysex file is not for the DX7 or it is corrupted. "
"Do you still want to load this file as random data ?");
if ( rc == 0 )
return;
}
processor->setCurrentProgram(0);
processor->loadCartridge(cart);
rebuildProgramCombobox();
processor->setCurrentProgram(0);
global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification);
processor->updateHostDisplay();
@@ -165,21 +144,12 @@ void DexedAudioProcessorEditor::loadCart(File file) {
void DexedAudioProcessorEditor::saveCart() {
File startFileName = processor->activeFileCartridge.exists() ? processor->activeFileCartridge : processor->dexedCartDir;
FileChooser fc ("Export DX sysex...", processor->dexedCartDir, "*.syx", 1);
FileChooser fc ("Export DX sysex...", processor->dexedCartDir, "*.syx;*.SYX", 1);
if ( fc.browseForFileToSave(true) ) {
String f = fc.getResults().getReference(0).getFullPathName();
char syx_data[4104];
exportSysexCart((char *) syx_data, (char *) &processor->sysex, 0);
ofstream fp_out(f.toRawUTF8(), ios::binary);
fp_out.write((char *)syx_data, 4104);
fp_out.close();
if (fp_out.fail()) {
if ( ! processor->currentCart.saveVoice(fc.getResults().getReference(0)) ) {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Error",
"Unable to write: " + f);
"Unable to write: " + fc.getResults().getReference(0).getFullPathName());
}
}
}
@@ -187,7 +157,7 @@ void DexedAudioProcessorEditor::saveCart() {
void DexedAudioProcessorEditor::parmShow() {
int tp = processor->getEngineType();
AlertWindow window("Dexed Params","", AlertWindow::NoIcon, this);
AlertWindow window("","", AlertWindow::NoIcon, this);
ParamDialog param;
param.setColour(AlertWindow::backgroundColourId, Colour(0x32FFFFFF));
param.setDialogValues(processor->controllers, processor->sysexComm, tp, processor->showKeyboard);
@@ -250,6 +220,8 @@ void DexedAudioProcessorEditor::updateUI() {
void DexedAudioProcessorEditor::rebuildProgramCombobox() {
global.programs->clear(dontSendNotification);
processor->currentCart.getProgramNames(processor->programNames);
for(int i=0;i<processor->getNumPrograms();i++) {
String id;
id << (i+1) << ". " << processor->getProgramName(i);
@@ -258,7 +230,7 @@ void DexedAudioProcessorEditor::rebuildProgramCombobox() {
global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification);
String name = normalizeSysexName((const char *) processor->data+145);
String name = Cartridge::normalizePgmName((const char *) processor->data+145);
cartManager.setActiveProgram(processor->getCurrentProgram(), name);
if ( name != processor->getProgramName(processor->getCurrentProgram()) )
global.programs->setText("**. " + name, dontSendNotification);
@@ -267,11 +239,9 @@ void DexedAudioProcessorEditor::rebuildProgramCombobox() {
}
void DexedAudioProcessorEditor::storeProgram() {
String currentName = normalizeSysexName((const char *) processor->data+145);
char destSysex[4096];
String currentName = Cartridge::normalizePgmName((const char *) processor->data+145);
Cartridge destSysex = processor->currentCart;
File *externalFile = NULL;
memcpy(&destSysex, processor->sysex, 4096);
bool activeCartridgeFound = processor->activeFileCartridge.exists();
@@ -292,7 +262,7 @@ void DexedAudioProcessorEditor::storeProgram() {
// TODO: fix the name length to 10
StringArray programs;
extractProgramNames((char *) &destSysex, programs);
destSysex.getProgramNames(programs);
dialog.addComboBox("Dest", programs, "Program Destination");
@@ -318,12 +288,9 @@ void DexedAudioProcessorEditor::storeProgram() {
if ( externalFile != NULL )
delete externalFile;
MemoryBlock block;
externalFile = new File(fc.getResults().getReference(0));
if ( externalFile->loadFileAsData(block) ) {
block.copyTo(destSysex, 6, 4096);
if ( destSysex.load(*externalFile) == 0 )
continue;
}
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Read error", "Unable to read file");
}
}
@@ -340,8 +307,7 @@ void DexedAudioProcessorEditor::storeProgram() {
}
if ( externalFile == NULL ) {
packProgram((uint8_t *) processor->sysex, (uint8_t *) processor->data, programNum, programName);
processor->programNames.set(programNum, programName);
processor->currentCart.packProgram((uint8_t *) processor->data, programNum, programName, processor->controllers.opSwitch);
rebuildProgramCombobox();
processor->setCurrentProgram(programNum);
processor->updateHostDisplay();
@@ -349,24 +315,19 @@ void DexedAudioProcessorEditor::storeProgram() {
int action = dialog.getComboBoxComponent("SaveAction")->getSelectedItemIndex();
if ( action > 0 ) {
File destination = processor->activeFileCartridge;
if ( ! destination.exists() ) {
FileChooser fc("Destination Sysex", processor->dexedCartDir, "*.syx", 1);
if ( action == 1 ) {
FileChooser fc("Destination Sysex", processor->dexedCartDir, "*.syx;*.SYX", 1);
if ( ! fc.browseForFileToSave(true) )
break;
destination = fc.getResult();
}
char sysexFile[4104];
exportSysexCart((char *) &sysexFile, (char *) &processor->sysex, 0);
if ( ! destination.replaceWithData(sysexFile, 4104) ) {
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file");
}
processor->currentCart.saveVoice(destination);
processor->activeFileCartridge = destination;
}
} else {
packProgram((uint8_t *) &destSysex, (uint8_t *) processor->data, programNum, programName);
char sysexFile[4104];
exportSysexCart((char *) &sysexFile, (char *) &destSysex, 0);
if ( ! externalFile->replaceWithData(sysexFile, 4104) ) {
destSysex.packProgram((uint8_t *) processor->data, programNum, programName, processor->controllers.opSwitch);
if ( ! destSysex.saveVoice(*externalFile)) {
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file");
}
}


+ 2
- 6
ports/dexed/source/PluginEditor.h View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2013-2015 Pascal Gauthier.
* Copyright (c) 2013-2016 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,11 +31,7 @@
//==============================================================================
/**
*/
class DexedAudioProcessorEditor : public AudioProcessorEditor,
public ComboBoxListener,
public Timer {
PopupMenu cartPopup;
class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBoxListener, public Timer {
MidiKeyboardComponent midiKeyboard;
OperatorEditor operators[6];
Colour background;


+ 16
- 0
ports/dexed/source/PluginFx.cpp View File

@@ -81,6 +81,10 @@ void PluginFx::init(int sr) {
pCutoff = -1;
pReso = -1;
dc_r = 1.0-(126.0/sr);
dc_id = 0;
dc_od = 0;
}
inline float PluginFx::NR24(float sample,float g,float lpc) {
@@ -97,6 +101,18 @@ inline float PluginFx::NR(float sample, float g) {
}
void PluginFx::process(float *work, int sampleSize) {
// very basic DC filter
float t_fd = work[0];
work[0] = work[0] - dc_id + dc_r * dc_od;
dc_id = t_fd;
for (int i=1; i<sampleSize; i++) {
t_fd = work[i];
work[i] = work[i] - dc_id + dc_r * work[i-1];
dc_id = t_fd;
}
dc_od = work[sampleSize-1];
if ( uiGain != 1 ) {
for(int i=0; i < sampleSize; i++ )
work[i] *= uiGain;


+ 4
- 0
ports/dexed/source/PluginFx.h View File

@@ -55,6 +55,10 @@ class PluginFx {
float rcor,rcorInv;
int R;
float dc_id;
float dc_od;
float dc_r;
public:
PluginFx();


+ 199
- 47
ports/dexed/source/PluginParam.cpp View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2013-2015 Pascal Gauthier.
* Copyright (c) 2013-2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,8 +49,6 @@ public:
String getValueDisplay() {
String ret;
int value = getValue();
if ( value == 48 )
value = 47;
switch(value % 12) {
case 0: ret << "C"; break;
@@ -66,10 +64,128 @@ public:
case 10: ret << "A#"; break;
case 11: ret << "B"; break;
}
return ret << (value/12-2);
return ret << (value/12+1);
}
};
class CtrlDXSwitch : public CtrlDX {
public:
CtrlDXSwitch(String name, int steps, int offset) : CtrlDX(name, steps, offset, 0) {
};
String getValueDisplay() {
return getValue() ? String("ON") : String("OFF");
}
};
class CtrlDXOpMode : public CtrlDX {
public:
CtrlDXOpMode(String name, int steps, int offset) : CtrlDX(name, steps, offset, 0) {
};
String getValueDisplay() {
return getValue() ? String("FIXED") : String("RATIO");
}
};
class CtrlDXBreakpoint : public CtrlDX {
public:
CtrlDXBreakpoint(String name, int steps, int offset) : CtrlDX(name, steps, offset, 0) {
};
String getValueDisplay() {
const char *breakNames[] = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
String ret;
ret << breakNames[getValue()%12] << (getValue()+9) / 12 - 1;
return ret;
}
};
class CtrlTune : public Ctrl {
public:
DexedAudioProcessor *processor;
CtrlTune(String name, DexedAudioProcessor *owner) : Ctrl(name) {
processor = owner;
}
float getValueHost() {
// meh. good enough for now
int32_t tune = processor->controllers.masterTune / (1.0/12);
tune = (tune >> 11) + 0x2000;
return (float)tune / 0x4000;
}
void setValueHost(float v) {
int32_t tune = (v * 0x4000) - 0x2000;
processor->controllers.masterTune = ((float) (tune << 11)) * (1.0/12);
}
String getValueDisplay() {
String display;
display << (getValueHost() * 2) -1;
return display;
}
void updateComponent() {
if (slider != NULL) {
slider->setValue(getValueHost(), dontSendNotification);
}
}
};
class CtrlOpSwitch : public Ctrl {
DexedAudioProcessor *processor;
char *value;
public :
CtrlOpSwitch(String name, char *switchValue, DexedAudioProcessor *owner) : Ctrl(name) {
processor = owner;
value = switchValue;
}
void setValueHost(float f) {
if ( f == 0 )
*value = '0';
else
*value = '1';
updateDisplayName();
// the value is based on the controller
parent->setDxValue(155, -1);
}
float getValueHost() {
if ( *value == '0' )
return 0;
else
return 1;
}
String getValueDisplay() {
String ret;
ret << label << " " << (*value == '0' ? "OFF" : "ON");
return ret;
}
void updateComponent() {
if (button != NULL) {
if (*value == '0') {
button->setToggleState(false, dontSendNotification);
} else {
button->setToggleState(true, dontSendNotification);
}
}
}
void updateDisplayName() {
DexedAudioProcessorEditor *editor = (DexedAudioProcessorEditor *) parent->getActiveEditor();
if ( editor == NULL ) {
return;
}
editor->global.setParamMessage(getValueDisplay());
editor->global.repaint();
}
};
// ************************************************************************
//
@@ -147,7 +263,7 @@ void Ctrl::updateDisplayName() {
}
// ************************************************************************
// CtrlDX - control DX mapping
// CtrlFloat - control float values
CtrlFloat::CtrlFloat(String name, float *storageValue) : Ctrl(name) {
vPointer = storageValue;
}
@@ -182,21 +298,15 @@ CtrlDX::CtrlDX(String name, int steps, int offset, int displayValue) : Ctrl(name
}
float CtrlDX::getValueHost() {
return dxValue / steps;
return getValue() / (float) steps;
}
void CtrlDX::setValueHost(float f) {
if ( f == 1 )
f = 0.999;
setValue((f * steps));
setValue(roundToInt(f * steps));
}
void CtrlDX::setValue(int v) {
TRACE("setting value %d %d", dxOffset, v);
if (v >= steps) {
TRACE("WARNING: value too big %s : %d", label.toRawUTF8(), v);
v = steps - 1;
}
dxValue = v;
if (dxOffset >= 0) {
if (parent != NULL)
@@ -288,28 +398,31 @@ void DexedAudioProcessor::initCtrl() {
output = new CtrlFloat("Output", &fx.uiGain);
ctrl.add(output);
algo = new CtrlDX("ALGORITHM", 32, 134, 1);
tune = new CtrlTune("MASTER TUNE ADJ", this);
ctrl.add(tune);
algo = new CtrlDX("ALGORITHM", 31, 134, 1);
ctrl.add(algo);
feedback = new CtrlDX("FEEDBACK", 8, 135);
feedback = new CtrlDX("FEEDBACK", 7, 135);
ctrl.add(feedback);
oscSync = new CtrlDX("OSC KEY SYNC", 2, 136);
oscSync = new CtrlDXSwitch("OSC KEY SYNC", 1, 136);
ctrl.add(oscSync);
lfoRate = new CtrlDX("LFO SPEED", 100, 137);
lfoRate = new CtrlDX("LFO SPEED", 99, 137);
ctrl.add(lfoRate);
lfoDelay = new CtrlDX("LFO DELAY", 100, 138);
lfoDelay = new CtrlDX("LFO DELAY", 99, 138);
ctrl.add(lfoDelay);
lfoPitchDepth = new CtrlDX("LFO PM DEPTH", 100, 139);
lfoPitchDepth = new CtrlDX("LFO PM DEPTH", 99, 139);
ctrl.add(lfoPitchDepth);
lfoAmpDepth = new CtrlDX("LFO AM DEPTH", 100, 140);
lfoAmpDepth = new CtrlDX("LFO AM DEPTH", 99, 140);
ctrl.add(lfoAmpDepth);
lfoSync = new CtrlDX("LFO KEY SYNC", 2, 141);
lfoSync = new CtrlDXSwitch("LFO KEY SYNC", 1, 141);
ctrl.add(lfoSync);
StringArray lbl;
@@ -320,26 +433,26 @@ void DexedAudioProcessor::initCtrl() {
lbl.add("SINE");
lbl.add("S&HOLD");
lfoWaveform = new CtrlDXLabel("LFO WAVE", 6, 142, lbl);
lfoWaveform = new CtrlDXLabel("LFO WAVE", 5, 142, lbl);
ctrl.add(lfoWaveform);
transpose = new CtrlDXTranspose("MIDDLE C", 49, 144);
transpose = new CtrlDXTranspose("MIDDLE C", 48, 144);
ctrl.add(transpose);
pitchModSens = new CtrlDX("P MODE SENS.", 8, 143);
pitchModSens = new CtrlDX("P MODE SENS.", 7, 143);
ctrl.add(pitchModSens);
for (int i=0;i<4;i++) {
String rate;
rate << "PITCH EG RATE " << (i+1);
pitchEgRate[i] = new CtrlDX(rate, 100, 126+i);
pitchEgRate[i] = new CtrlDX(rate, 99, 126+i);
ctrl.add(pitchEgRate[i]);
}
for (int i=0;i<4;i++) {
String level;
level << "PITCH EG LEVEL " << (i+1);
pitchEgLevel[i] = new CtrlDX(level, 100, 130+i);
pitchEgLevel[i] = new CtrlDX(level, 99, 130+i);
ctrl.add(pitchEgLevel[i]);
}
@@ -360,81 +473,86 @@ void DexedAudioProcessor::initCtrl() {
for (int j = 0; j < 4; j++) {
String opRate;
opRate << opName << " EG RATE " << (j + 1);
opCtrl[opVal].egRate[j] = new CtrlDX(opRate, 100, opTarget + j);
opCtrl[opVal].egRate[j] = new CtrlDX(opRate, 99, opTarget + j);
ctrl.add(opCtrl[opVal].egRate[j]);
}
for (int j = 0; j < 4; j++) {
String opLevel;
opLevel << opName << " EG LEVEL " << (j + 1);
opCtrl[opVal].egLevel[j] = new CtrlDX(opLevel, 100, opTarget + j + 4);
opCtrl[opVal].egLevel[j] = new CtrlDX(opLevel, 99, opTarget + j + 4);
ctrl.add(opCtrl[opVal].egLevel[j]);
}
String opVol;
opVol << opName << " OUTPUT LEVEL";
opCtrl[opVal].level = new CtrlDX(opVol, 100, opTarget + 16);
opCtrl[opVal].level = new CtrlDX(opVol, 99, opTarget + 16);
ctrl.add(opCtrl[opVal].level);
String opMode;
opMode << opName << " MODE";
opCtrl[opVal].opMode = new CtrlDX(opMode, 2, opTarget + 17);
opCtrl[opVal].opMode = new CtrlDXOpMode(opMode, 1, opTarget + 17);
ctrl.add(opCtrl[opVal].opMode);
String coarse;
coarse << opName << " F COARSE";
opCtrl[opVal].coarse = new CtrlDX(coarse, 32, opTarget + 18);
opCtrl[opVal].coarse = new CtrlDX(coarse, 31, opTarget + 18);
ctrl.add(opCtrl[opVal].coarse);
String fine;
fine << opName << " F FINE";
opCtrl[opVal].fine = new CtrlDX(fine, 100, opTarget + 19);
opCtrl[opVal].fine = new CtrlDX(fine, 99, opTarget + 19);
ctrl.add(opCtrl[opVal].fine);
String detune;
detune << opName << " OSC DETUNE";
opCtrl[opVal].detune = new CtrlDX(detune, 15, opTarget + 20, -7);
opCtrl[opVal].detune = new CtrlDX(detune, 14, opTarget + 20, -7);
ctrl.add(opCtrl[opVal].detune);
String sclBrkPt;
sclBrkPt << opName << " BREAK POINT";
opCtrl[opVal].sclBrkPt = new CtrlDX(sclBrkPt, 100, opTarget + 8);
opCtrl[opVal].sclBrkPt = new CtrlDXBreakpoint(sclBrkPt, 99, opTarget + 8);
ctrl.add(opCtrl[opVal].sclBrkPt);
String sclLeftDepth;
sclLeftDepth << opName << " L SCALE DEPTH";
opCtrl[opVal].sclLeftDepth = new CtrlDX(sclLeftDepth, 100, opTarget + 9);
opCtrl[opVal].sclLeftDepth = new CtrlDX(sclLeftDepth, 99, opTarget + 9);
ctrl.add(opCtrl[opVal].sclLeftDepth);
String sclRightDepth;
sclRightDepth << opName << " R SCALE DEPTH";
opCtrl[opVal].sclRightDepth = new CtrlDX(sclRightDepth, 100, opTarget + 10);
opCtrl[opVal].sclRightDepth = new CtrlDX(sclRightDepth, 99, opTarget + 10);
ctrl.add(opCtrl[opVal].sclRightDepth);
String sclLeftCurve;
sclLeftCurve << opName << " L KEY SCALE";
opCtrl[opVal].sclLeftCurve = new CtrlDXLabel(sclLeftCurve, 4, opTarget + 11, keyScaleLabels);
opCtrl[opVal].sclLeftCurve = new CtrlDXLabel(sclLeftCurve, 3, opTarget + 11, keyScaleLabels);
ctrl.add(opCtrl[opVal].sclLeftCurve);
String sclRightCurve;
sclRightCurve << opName << " R KEY SCALE";
opCtrl[opVal].sclRightCurve = new CtrlDXLabel(sclRightCurve, 4, opTarget + 12, keyScaleLabels);
opCtrl[opVal].sclRightCurve = new CtrlDXLabel(sclRightCurve, 3, opTarget + 12, keyScaleLabels);
ctrl.add(opCtrl[opVal].sclRightCurve);
String sclRate;
sclRate << opName << " RATE SCALING";
opCtrl[opVal].sclRate = new CtrlDX(sclRate, 8, opTarget + 13);
opCtrl[opVal].sclRate = new CtrlDX(sclRate, 7, opTarget + 13);
ctrl.add(opCtrl[opVal].sclRate);
String ampModSens;
ampModSens << opName << " A MOD SENS.";
opCtrl[opVal].ampModSens = new CtrlDX(ampModSens, 4, opTarget + 14);
opCtrl[opVal].ampModSens = new CtrlDX(ampModSens, 3, opTarget + 14);
ctrl.add(opCtrl[opVal].ampModSens);
String velModSens;
velModSens << opName << " KEY VELOCITY";
opCtrl[opVal].velModSens = new CtrlDX(velModSens, 8, opTarget + 15);
opCtrl[opVal].velModSens = new CtrlDX(velModSens, 7, opTarget + 15);
ctrl.add(opCtrl[opVal].velModSens);
String opSwitchLabel;
opSwitchLabel << opName << " SWITCH";
opCtrl[opVal].opSwitch = new CtrlOpSwitch(opSwitchLabel, (char *)&(controllers.opSwitch)+(5-i), this);
ctrl.add(opCtrl[opVal].opSwitch);
}
for (int i=0; i < ctrl.size(); i++) {
@@ -447,7 +565,12 @@ void DexedAudioProcessor::setDxValue(int offset, int v) {
if (offset < 0)
return;
if ( data[offset] != v ) {
if ( offset == 155 ) {
// used on op switch that are not part of a Sysex packed cartridge, we render it
// ourselves.
packOpSwitch();
v = data[155];
} else if ( data[offset] != v ) {
TRACE("setting dx %d %d", offset, v);
data[offset] = v;
} else {
@@ -469,6 +592,7 @@ void DexedAudioProcessor::setDxValue(int offset, int v) {
msg[4] = offset & 0x7F;
if ( sysexComm.isOutputActive() ) {
//TRACE("SENDING SYSEX: %.2X%.2X %.2X%.2X %.2X%.2X %.2X", msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6]);
sysexComm.send(MidiMessage(msg,7));
}
}
@@ -512,7 +636,8 @@ void DexedAudioProcessor::setCurrentProgram(int index) {
panic();
index = index > 31 ? 31 : index;
unpackProgram(index);
currentCart.unpackProgram(data, index);
strcpy(controllers.opSwitch, "111111");
lfo.reset(data + 137);
currentProgram = index;
triggerAsyncUpdate();
@@ -577,13 +702,31 @@ void DexedAudioProcessor::loadPreference() {
sysexComm.setChl( prop.getIntValue( String("sysexChl") ) );
}
if ( prop.containsKey( String("engineType" ) ) ) {
engineType = prop.getIntValue( String("engineType") );
if ( prop.containsKey( String("engineType") ) ) {
setEngineType(prop.getIntValue(String("engineType")));
}
if ( prop.containsKey( String("showKeyboard") ) ) {
showKeyboard = prop.getIntValue( String("showKeyboard") );
}
if ( prop.containsKey( String("wheelMod") ) ) {
controllers.wheel.parseConfig(prop.getValue(String("wheelMod")).toRawUTF8());
}
if ( prop.containsKey( String("footMod") ) ) {
controllers.foot.parseConfig(prop.getValue(String("footMod")).toRawUTF8());
}
if ( prop.containsKey( String("breathMod") ) ) {
controllers.breath.parseConfig(prop.getValue(String("breathMod")).toRawUTF8());
}
if ( prop.containsKey( String("aftertouchMod") ) ) {
controllers.at.parseConfig(prop.getValue(String("aftertouchMod")).toRawUTF8());
}
controllers.refresh();
}
void DexedAudioProcessor::savePreference() {
@@ -601,9 +744,18 @@ void DexedAudioProcessor::savePreference() {
prop.setValue(String("showKeyboard"), showKeyboard);
//prop.setValue(String("engineResolution"), engineResolution);
char mod_cfg[15];
controllers.wheel.setConfig(mod_cfg);
prop.setValue(String("wheelMod"), mod_cfg);
controllers.foot.setConfig(mod_cfg);
prop.setValue(String("footMod"), mod_cfg);
controllers.breath.setConfig(mod_cfg);
prop.setValue(String("breathMod"), mod_cfg);
controllers.at.setConfig(mod_cfg);
prop.setValue(String("aftertouchMod"), mod_cfg);
prop.setValue(String("engineType"), (int) engineType);
prop.save();
}

+ 1
- 0
ports/dexed/source/PluginParam.h View File

@@ -121,6 +121,7 @@ struct OperatorCtrl {
ScopedPointer<CtrlDX> sclRate;
ScopedPointer<CtrlDX> ampModSens;
ScopedPointer<CtrlDX> velModSens;
ScopedPointer<Ctrl> opSwitch;
};
#endif // PLUGINPARAM_H_INCLUDED

+ 127
- 65
ports/dexed/source/PluginProcessor.cpp View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2013-2015 Pascal Gauthier.
* Copyright (c) 2013-2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,6 +18,9 @@
*
*/
#include <stdarg.h>
#include <bitset>
#include "PluginProcessor.h"
#include "PluginEditor.h"
@@ -54,19 +57,23 @@ DexedAudioProcessor::DexedAudioProcessor() {
resolvAppDir();
TRACE("controler %s", controllers.opSwitch);
initCtrl();
sendSysexChange = true;
normalizeDxVelocity = false;
sysexComm.listener = this;
showKeyboard = true;
memset(&voiceStatus, 0, sizeof(VoiceStatus));
setEngineType(DEXED_ENGINE_MARKI);
controllers.values_[kControllerPitchRange] = 3;
controllers.values_[kControllerPitchStep] = 0;
controllers.masterTune = 0;
loadPreference();
setEngineType(DEXED_ENGINE_MODERN);
for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) {
voices[note].dx7_note = NULL;
}
@@ -98,13 +105,18 @@ void DexedAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
currentNote = 0;
controllers.values_[kControllerPitch] = 0x2000;
controllers.values_[kControllerModWheel] = 0;
controllers.modwheel_cc = 0;
controllers.foot_cc = 0;
controllers.breath_cc = 0;
controllers.aftertouch_cc = 0;
sustain = false;
extra_buf_size = 0;
keyboardState.reset();
lfo.reset(data + 137);
nextMidi = new MidiMessage(0xF0);
midiMsg = new MidiMessage(0xF0);
}
@@ -134,13 +146,13 @@ void DexedAudioProcessor::releaseResources() {
}
void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages) {
const int numSamples = buffer.getNumSamples();
int numSamples = buffer.getNumSamples();
int i;
if ( refreshVoice ) {
for(i=0;i < MAX_ACTIVE_NOTES;i++) {
if ( voices[i].live )
voices[i].dx7_note->update(data, voices[i].midi_note);
voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity);
}
lfo.reset(data + 137);
refreshVoice = false;
@@ -190,11 +202,11 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi
voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers);
for (int j=0; j < N; ++j) {
int32_t val = audiobuf.get()[j]; //& 0xFFFFF000);
int32_t val = audiobuf.get()[j];
val = val >> 4;
int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9;
float f = ((float) clip_val) / (float) 32768;
float f = ((float) clip_val) / (float) 0x8000;
if( f > 1 ) f = 1;
if( f < -1 ) f = -1;
sumbuf[j] += f;
@@ -231,11 +243,6 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi
else
vuSignal = 0;
}
// DX7 is a mono synth
for (int channel = 1; channel < getTotalNumOutputChannels(); ++channel) {
buffer.copyFrom(channel, 0, channelData, numSamples, 1);
}
}
@@ -266,36 +273,52 @@ void DexedAudioProcessor::processMidiMessage(const MidiMessage *msg) {
case 0x90 :
keydown(buf[1], buf[2]);
return;
case 0xb0 : {
int controller = buf[1];
int ctrl = buf[1];
int value = buf[2];
// mod wheel
if ( controller == 1 ) {
controllers.values_[kControllerModWheel] = value;
return;
}
// pedal
if (controller == 64) {
sustain = value != 0;
if (!sustain) {
for (int note = 0; note < MAX_ACTIVE_NOTES; note++) {
if (voices[note].sustained && !voices[note].keydown) {
voices[note].dx7_note->keyup();
voices[note].sustained = false;
switch(ctrl) {
case 1:
controllers.modwheel_cc = value;
controllers.refresh();
break;
case 2:
controllers.breath_cc = value;
controllers.refresh();
break;
case 4:
controllers.foot_cc = value;
controllers.refresh();
break;
case 64:
sustain = value > 63;
if (!sustain) {
for (int note = 0; note < MAX_ACTIVE_NOTES; note++) {
if (voices[note].sustained && !voices[note].keydown) {
voices[note].dx7_note->keyup();
voices[note].sustained = false;
}
}
}
}
return;
break;
case 123:
panic();
break;
}
}
return;
case 0xc0 :
setCurrentProgram(buf[1]);
return;
return;
// aftertouch
case 0xd0 :
controllers.aftertouch_cc = buf[1];
controllers.refresh();
return;
}
switch (cmd) {
@@ -303,7 +326,6 @@ void DexedAudioProcessor::processMidiMessage(const MidiMessage *msg) {
controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7);
break;
}
}
void DexedAudioProcessor::keydown(uint8_t pitch, uint8_t velo) {
@@ -324,6 +346,7 @@ void DexedAudioProcessor::keydown(uint8_t pitch, uint8_t velo) {
currentNote = (note + 1) % MAX_ACTIVE_NOTES;
lfo.keydown(); // TODO: should only do this if # keys down was 0
voices[note].midi_note = pitch;
voices[note].velocity = velo;
voices[note].sustained = sustain;
voices[note].keydown = true;
voices[note].dx7_note->init(data, pitch, velo);
@@ -414,45 +437,72 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid
sysexComm.inActivity = true;
if ( ! message.isSysEx() )
return;
//const uint8 *buf = msg->getSysExData();
const uint8 *buf = message.getRawData();
int sz = message.getRawDataSize();
if ( sz < 3 )
return;
TRACE("SYSEX RECEIVED %d", sz);
//TRACE("%X %X %X %X %X %X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
if ( ! message.isSysEx() )
return;
// test if it is a Yamaha Sysex
if ( buf[1] != 0x43 ) {
TRACE("not a yamaha sysex %d", buf[0]);
return;
}
int substatus = buf[2] >> 4;
if ( substatus == 0 ) {
// single voice dump
if ( buf[3] == 0 ) {
if ( sz < 156 ) {
TRACE("wrong single voice datasize %d", sz);
return;
}
updateProgramFromSysex(buf+6);
}
// single voice dump
if ( buf[3] == 0 ) {
if ( sz < 155 ) {
TRACE("wrong single voice datasize %d", sz);
return;
// 32 voice dump
if ( buf[3] == 9 ) {
if ( sz < 4104 ) {
TRACE("wrong 32 voice dump data size %d", sz);
return;
}
Cartridge received;
if ( received.load(buf, sz) == 0 ) {
loadCartridge(received);
setCurrentProgram(0);
}
}
updateProgramFromSysex(buf+6);
}
// 32 voice dump
if ( buf[3] == 9 ) {
if ( sz < 4104 ) {
TRACE("wrong 32 voice datasize %d", sz);
} else if ( substatus == 1 ) {
// parameter change
if ( sz < 7 ) {
TRACE("wrong single voice datasize %d", sz);
return;
}
uint8 offset = (buf[3] << 7) + buf[4];
uint8 value = buf[5];
TRACE("parameter change message offset:%d value:%d", offset, value);
if ( offset > 155 ) {
TRACE("wrong offset size");
return;
}
TRACE("update 32bulk voice");
importSysex((const char *)buf);
setCurrentProgram(0);
if ( offset == 155 ) {
unpackOpSwitch(value);
} else {
data[offset] = value;
}
} else {
TRACE("unknown sysex substatus: %d", substatus);
}
updateHostDisplay();
forceRefreshUI = true;
}
@@ -462,18 +512,18 @@ int DexedAudioProcessor::getEngineType() {
}
void DexedAudioProcessor::setEngineType(int tp) {
TRACE("settings engine %d", tp);
switch (tp) {
case DEXED_ENGINE_MODERN :
controllers.core = &engineMsfa;
break;
case DEXED_ENGINE_MARKI:
controllers.sinBitFilter = 0xFFFFC000; // 10 bit
controllers.dacBitFilter = 0xFFFFF000; // semi 14 bit
controllers.core = &engineMkI;
break;
case DEXED_ENGINE_OPL:
controllers.core = &engineOpl;
break;
default:
controllers.core = &engineMsfa;
break;
}
engineType = tp;
}
@@ -581,3 +631,15 @@ AudioProcessorEditor* DexedAudioProcessor::createEditor() {
void DexedAudioProcessor::handleAsyncUpdate() {
updateUI();
}
void dexed_trace(const char *source, const char *fmt, ...) {
char output[4096];
va_list argptr;
va_start(argptr, fmt);
vsnprintf(output, 4095, fmt, argptr);
va_end(argptr);
String dest;
dest << source << " " << output;
Logger::writeToLog(dest);
}

+ 11
- 7
ports/dexed/source/PluginProcessor.h View File

@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2013-2015 Pascal Gauthier.
* Copyright (c) 2013-2017 Pascal Gauthier.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@
struct ProcessorVoice {
int midi_note;
int velocity;
bool keydown;
bool sustained;
bool live;
@@ -115,14 +116,17 @@ class DexedAudioProcessor : public AudioProcessor, public AsyncUpdater, public
char clipboardContent;
void resolvAppDir();
void unpackOpSwitch(char packOpValue);
void packOpSwitch();
public :
// in MIDI units (0x4000 is neutral)
Controllers controllers;
StringArray programNames;
char sysex[4096];
char data[161];
StringArray programNames;
Cartridge currentCart;
uint8_t data[161];
//CartridgeManager cartManager;
SysexComm sysexComm;
VoiceStatus voiceStatus;
File activeFileCartridge;
@@ -153,8 +157,9 @@ public :
ScopedPointer<CtrlFloat> fxCutoff;
ScopedPointer<CtrlFloat> fxReso;
ScopedPointer<CtrlFloat> output;
ScopedPointer<Ctrl> tune;
int importSysex(const char *imported);
void loadCartridge(Cartridge &cart);
void setDxValue(int offset, int v);
//==============================================================================
@@ -184,7 +189,6 @@ public :
bool hasEditor() const;
void updateUI();
bool peekVoiceStatus();
void unpackProgram(int idx);
void updateProgramFromSysex(const uint8 *rawdata);
void setupStartupCart();


+ 26
- 19
ports/dexed/source/ProgramListBox.cpp View File

@@ -29,7 +29,9 @@ ProgramListBox::ProgramListBox(const String name, int numCols) : Component(name)
selectedPgm = -1;
hasContent = false;
dragCandidate = -1;
pgmCandidate = -1;
readOnly = false;
programNames.clear();
}
void ProgramListBox::paint(Graphics &g) {
@@ -85,9 +87,9 @@ void ProgramListBox::resized() {
cellHeight = getHeight() / rows;
}
void ProgramListBox::setCartridge(char *sysex) {
extractProgramNames((const char *)sysex, programNames);
memcpy(cartContent, sysex, 4104);
void ProgramListBox::setCartridge(Cartridge &cart) {
cartContent = cart;
cartContent.getProgramNames(programNames);
hasContent = true;
repaint();
}
@@ -100,29 +102,36 @@ int ProgramListBox::programPosition(int x, int y) {
return (y / cellHeight) + ((x / cellWidth) * rows);
}
void ProgramListBox::mouseDoubleClick(const MouseEvent &event) {
void ProgramListBox::mouseDown(const MouseEvent &event) {
pgmCandidate = -1;
if ( ! hasContent )
return;
if ( ! event.mods.isLeftButtonDown() )
return;
int pos = programPosition(event.getMouseDownX(), event.getMouseDownY());
if ( listener != nullptr )
listener->programSelected(this, pos);
if ( event.mods.isRightButtonDown() || event.mods.isAnyModifierKeyDown() ) {
int pos = programPosition(event.getMouseDownX(), event.getMouseDownY());
if ( listener != nullptr )
listener->programRightClicked(this, pos);
return;
}
pgmCandidate = programPosition(event.getMouseDownX(), event.getMouseDownY());
}
void ProgramListBox::mouseDown(const MouseEvent &event) {
if ( ! hasContent )
return;
if ( ! event.mods.isRightButtonDown() )
void ProgramListBox::mouseUp(const MouseEvent &event) {
if ( pgmCandidate == -1 )
return;
int pos = programPosition(event.getMouseDownX(), event.getMouseDownY());
if ( listener != nullptr )
listener->programRightClicked(this, pos);
if ( pgmCandidate == pos) {
if ( listener != nullptr )
listener->programSelected(this, pgmCandidate);
pgmCandidate = -1;
}
}
void ProgramListBox::mouseDrag(const MouseEvent &event) {
pgmCandidate = -1;
if ( ! hasContent )
return;
if ( dragCandidate != -1 )
@@ -138,7 +147,7 @@ void ProgramListBox::mouseDrag(const MouseEvent &event) {
g.fillRect(0,0,cellWidth, cellHeight);
g.setColour(Colours::white);
g.drawFittedText(programNames[position], 0, 0, cellWidth, cellHeight, Justification::centred, true);
void *src = cartContent + (position*128);
void *src = cartContent.getRawVoice() + (position*128);
var description = var(src, 128);
dragContainer->startDragging(description, this, snapshot, false);
}
@@ -148,9 +157,7 @@ void ProgramListBox::setSelected(int idx) {
selectedPgm = idx;
}
char* ProgramListBox::getCurrentCart() {
if ( ! hasContent )
return nullptr;
Cartridge &ProgramListBox::getCurrentCart() {
return cartContent;
}


+ 11
- 9
ports/dexed/source/ProgramListBox.h View File

@@ -22,6 +22,7 @@
#define PROGRAMLISTBOX_H_INCLUDED
#include "JuceHeader.h"
#include "PluginData.h"
class ProgramListBox;
class ProgramListBoxListener {
@@ -41,10 +42,10 @@ class ProgramListBox : public Component, public DragAndDropTarget {
int programPosition(int x, int y);
int selectedPgm;
// TODO: this should be a pointer
char cartContent[4104];
Cartridge cartContent;
int dragCandidate;
int pgmCandidate;
public:
StringArray programNames;
@@ -52,14 +53,15 @@ public:
ProgramListBox(const String name, int numCols);
void addListener(ProgramListBoxListener *listener);
void paint(Graphics &g);
void setCartridge(char *sysex);
void resized();
void mouseDoubleClick(const MouseEvent &event);
void mouseDown(const MouseEvent &event);
void mouseDrag(const MouseEvent &event);
void paint(Graphics &g) override;
void resized() override;
void mouseDown(const MouseEvent &event) override;
void mouseDrag(const MouseEvent &event) override;
void mouseUp(const MouseEvent &event) override;
void setSelected(int idx);
char* getCurrentCart();
Cartridge &getCurrentCart();
void setCartridge(Cartridge &cart);
bool isInterestedInDragSource(const SourceDetails& dragSourceDetails) override;
void itemDragEnter(const SourceDetails &dragSourceDetails) override;


+ 0
- 3
ports/dexed/source/SysexComm.cpp View File

@@ -142,9 +142,6 @@ void SysexComm::setChl(int chl) {
int SysexComm::send(const MidiMessage &message) {
if ( output == NULL )
return 2;
TRACE("send sysex");
outActivity = true;
output->sendMessageNow(message);
return 0;


+ 94
- 11
ports/dexed/source/msfa/controllers.h View File

@@ -1,12 +1,12 @@
/*
* Copyright 2013 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,25 +18,108 @@
#define __CONTROLLERS_H

#include "synth.h"
#include "../Dexed.h"
#include <stdio.h>
#include <string.h>

#ifdef _WIN32
#define snprintf _snprintf
#endif

// State of MIDI controllers
const int kControllerModWheel = 1;
const int kControllerPitch = 128;
const int kControllerPitchRange = 129;
const int kControllerPitchStep = 130;

class FmCore;

struct FmMod {
int range;
bool pitch;
bool amp;
bool eg;
FmMod() {
range = 0;
pitch = false;
amp = false;
eg = false;
}

void parseConfig(const char *cfg) {
int r = 0, p = 0, a = 0, e = 0;
sscanf(cfg, "%d %d %d %d", &r, &p, &a, &e);
range = r < 0 && r > 127 ? 0 : r;
pitch = p != 0;
amp = a != 0;
eg = e != 0;
}
void setConfig(char *cfg) {
snprintf(cfg, 13, "%d %d %d %d", range, pitch, amp, eg);
}
};

class Controllers {
public:
int values_[131];
void applyMod(int cc, FmMod &mod) {
float range = 0.01 * mod.range;
int total = cc * range;
if ( mod.amp )
amp_mod = max(amp_mod, total);
if ( mod.pitch )
pitch_mod = max(pitch_mod, total);
if ( mod.eg )
eg_mod = max(eg_mod, total);
}
public:
int values_[131];
// engine bit filters
uint32_t sinBitFilter; // bit filter based upon sin LUT lookup
uint32_t dacBitFilter; // bit filter based upon DAC resolution
uint32_t mulBitFilter; // bit filter based upon multipliers (?????)
char opSwitch[7];
int amp_mod;
int pitch_mod;
int eg_mod;
int aftertouch_cc;
int breath_cc;
int foot_cc;
int modwheel_cc;
int masterTune;
FmMod wheel;
FmMod foot;
FmMod breath;
FmMod at;
Controllers() {
amp_mod = 0;
pitch_mod = 0;
eg_mod = 0;
strcpy(opSwitch, "111111");
}

void refresh() {
amp_mod = 0;
pitch_mod = 0;
eg_mod = 0;
applyMod(modwheel_cc, wheel);
applyMod(breath_cc, breath);
applyMod(foot_cc, foot);
applyMod(aftertouch_cc, at);
if ( ! ((wheel.eg || foot.eg) || (breath.eg || at.eg)) )
eg_mod = 127;
TRACE("amp_mod %d pitch_mod %d", amp_mod, pitch_mod);
}
FmCore *core;
FmCore *core;
};

#endif // __CONTROLLERS_H


+ 192
- 172
ports/dexed/source/msfa/dx7note.cc View File

@@ -1,12 +1,13 @@
/*
* Copyright 2016-2017 Pascal Gauthier.
* Copyright 2012 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,10 +15,6 @@
* limitations under the License.
*/
#ifdef VERBOSE
#include <iostream>
using namespace std;
#endif
#include <math.h>
#include <stdlib.h>
#include "synth.h"
@@ -26,105 +23,107 @@ using namespace std;
#include "controllers.h"
#include "dx7note.h"
const int FEEDBACK_BITDEPTH = 8;
int32_t midinote_to_logfreq(int midinote) {
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int step = (1 << 24) / 12;
return base + step * midinote;
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int step = (1 << 24) / 12;
return base + step * midinote;
}
const int32_t coarsemul[] = {
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600,
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816,
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858,
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032,
81503396, 82323963, 83117622
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600,
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816,
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858,
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032,
81503396, 82323963, 83117622
};
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
// TODO: pitch randomization
int32_t logfreq;
if (mode == 0) {
logfreq = midinote_to_logfreq(midinote);
logfreq += coarsemul[coarse & 31];
if (fine) {
// (1 << 24) / log(2)
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5);
// TODO: pitch randomization
int32_t logfreq;
if (mode == 0) {
logfreq = midinote_to_logfreq(midinote);
logfreq += coarsemul[coarse & 31];
if (fine) {
// (1 << 24) / log(2)
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5);
}
// This was measured at 7.213Hz per count at 9600Hz, but the exact
// value is somewhat dependent on midinote. Close enough for now.
logfreq += 12606 * (detune - 7);
} else {
// ((1 << 24) * log(10) / log(2) * .01) << 3
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3;
logfreq += detune > 7 ? 13457 * (detune - 7) : 0;
}
// This was measured at 7.213Hz per count at 9600Hz, but the exact
// value is somewhat dependent on midinote. Close enough for now.
logfreq += 12606 * (detune - 7);
} else {
// ((1 << 24) * log(10) / log(2) * .01) << 3
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3;
logfreq += detune > 7 ? 13457 * (detune - 7) : 0;
}
return logfreq;
return logfreq;
}
const uint8_t velocity_data[64] = {
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163,
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202,
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232,
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251,
252, 253, 254
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163,
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202,
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232,
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251,
252, 253, 254
};
// See "velocity" section of notes. Returns velocity delta in microsteps.
int ScaleVelocity(int velocity, int sensitivity) {
int clamped_vel = max(0, min(127, velocity));
int vel_value = velocity_data[clamped_vel >> 1] - 239;
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4;
return scaled_vel;
int clamped_vel = max(0, min(127, velocity));
int vel_value = velocity_data[clamped_vel >> 1] - 239;
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4;
return scaled_vel;
}
int ScaleRate(int midinote, int sensitivity) {
int x = min(31, max(0, midinote / 3 - 7));
int qratedelta = (sensitivity * x) >> 3;
int x = min(31, max(0, midinote / 3 - 7));
int qratedelta = (sensitivity * x) >> 3;
#ifdef SUPER_PRECISE
int rem = x & 7;
if (sensitivity == 3 && rem == 3) {
qratedelta -= 1;
} else if (sensitivity == 7 && rem > 0 && rem < 4) {
qratedelta += 1;
}
int rem = x & 7;
if (sensitivity == 3 && rem == 3) {
qratedelta -= 1;
} else if (sensitivity == 7 && rem > 0 && rem < 4) {
qratedelta += 1;
}
#endif
return qratedelta;
return qratedelta;
}
const uint8_t exp_scale_data[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66,
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66,
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250
};
int ScaleCurve(int group, int depth, int curve) {
int scale;
if (curve == 0 || curve == 3) {
// linear
scale = (group * depth * 329) >> 12;
} else {
// exponential
int n_scale_data = sizeof(exp_scale_data);
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)];
scale = (raw_exp * depth * 329) >> 15;
}
if (curve < 2) {
scale = -scale;
}
return scale;
int scale;
if (curve == 0 || curve == 3) {
// linear
scale = (group * depth * 329) >> 12;
} else {
// exponential
int n_scale_data = sizeof(exp_scale_data);
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)];
scale = (raw_exp * depth * 329) >> 15;
}
if (curve < 2) {
scale = -scale;
}
return scale;
}
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth,
int left_curve, int right_curve) {
int offset = midinote - break_pt - 17;
if (offset >= 0) {
return ScaleCurve(offset / 3, right_depth, right_curve);
} else {
return ScaleCurve((-offset) / 3, left_depth, left_curve);
}
int left_curve, int right_curve) {
int offset = midinote - break_pt - 17;
if (offset >= 0) {
return ScaleCurve((offset+1) / 3, right_depth, right_curve);
} else {
return ScaleCurve(-(offset-1) / 3, left_depth, left_curve);
}
}
static const uint8_t pitchmodsenstab[] = {
0, 10, 20, 33, 55, 92, 153, 255
0, 10, 20, 33, 55, 92, 153, 255
};
// 0, 66, 109, 255
@@ -139,75 +138,62 @@ Dx7Note::Dx7Note() {
}
}
void Dx7Note::init(const char patch[156], int midinote, int velocity) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].init(rates, levels, outlevel, rate_scaling);
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
basepitch_[op] = freq;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
#ifdef VERBOSE
for (int j = 8; j < 12; j++) {
cout << (int)patch[off + j] << " ";
for (int i = 0; i < 4; i++) {
rates[i] = patch[126 + i];
levels[i] = patch[130 + i];
}
#endif
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
#ifdef VERBOSE
cout << op << ": " << level_scaling << " " << outlevel << endl;
#endif
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].init(rates, levels, outlevel, rate_scaling);
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
basepitch_[op] = freq;
// cout << op << " freq: " << freq << endl;
//params_[op].phase = 0;
//params_[op].gain_out = 0;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
}
for (int i = 0; i < 4; i++) {
rates[i] = patch[126 + i];
levels[i] = patch[130 + i];
}
pitchenv_.set(rates, levels);
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
pitchenv_.set(rates, levels);
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
}
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) {
int32_t pitchmod = pitchenv_.getsample();
// ==== PITCH ====
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
// TODO(PG) : make this integer friendly
uint32_t pwmd = (ctrls->values_[kControllerModWheel] * 0.7874) * (1 << 24);
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23));
int32_t pmod_1 = (((int64_t) pmd) * (int64_t) senslfo) >> 39;
pmod_1 = abs(pmod_1);
int32_t pmod_2 = ((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14;
pmod_2 = abs(pmod_2);
int32_t pitch_mod = max(pmod_1, pmod_2);
pitch_mod = pitchenv_.getsample() + (pitch_mod * (senslfo < 0 ? -1 : 1));
uint32_t amd = ((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8; // Q24 :D
amd = ((int64_t) amd * (int64_t) lfo_val) >> 24;
pitchmod += (((int64_t) pwmd) * (int64_t) senslfo) >> 39;
pitchmod += (((int64_t) pmd) * (int64_t) senslfo) >> 39;
// ---- PITCH BEND ----
int pitchbend = ctrls->values_[kControllerPitch];
int32_t pb = (pitchbend - 0x2000);
if (pb != 0) {
if (ctrls->values_[kControllerPitchStep] == 0) {
pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRange]) / 12.0;
@@ -217,57 +203,92 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co
pb = (pb * (8191 / stp)) << 11;
}
}
pitchmod += pb;
pitch_mod += pb;
pitch_mod += ctrls->masterTune;
// ==== AMP MOD ====
uint32_t amod_1 = ((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8; // Q24 :D
amod_1 = ((int64_t) amod_1 * (int64_t) lfo_val) >> 24;
uint32_t amod_2 = ((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7; // Q?? :|
uint32_t amd_mod = max(amod_1, amod_2);
// ==== EG AMP MOD ====
uint32_t amod_3 = (ctrls->eg_mod+1) << 17;
amd_mod = max((1<<24) - amod_3, amd_mod);
// ==== OP RENDER ====
for (int op = 0; op < 6; op++) {
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod);
int32_t level = env_[op].getsample();
if (ampmodsens_[op] != 0) {
uint32_t sensamp = ((uint64_t) amd) * ((uint64_t) ampmodsens_[op]) >> 24;
if ( ctrls->opSwitch[op] == '0' ) {
env_[op].getsample(); // advance the envelop even if it is not playing
params_[op].level_in = 0;
} else {
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_mod);
// TODO: mehhh.. this needs some real tuning.
uint32_t pt = exp(((float)sensamp)/262144 * 0.07 + 12.2);
uint32_t ldiff = ((uint64_t)level) * (((uint64_t)pt<<4)) >> 28;
level -= ldiff;
int32_t level = env_[op].getsample();
if (ampmodsens_[op] != 0) {
uint32_t sensamp = ((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24;
// TODO: mehhh.. this needs some real tuning.
uint32_t pt = exp(((float)sensamp)/262144 * 0.07 + 12.2);
uint32_t ldiff = ((uint64_t)level) * (((uint64_t)pt<<4)) >> 28;
level -= ldiff;
}
params_[op].level_in = level;
}
params_[op].level_in = level;
}
ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_, ctrls);
ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_);
}
void Dx7Note::keyup() {
for (int op = 0; op < 6; op++) {
env_[op].keydown(false);
for (int op = 0; op < 6; op++) {
env_[op].keydown(false);
}
pitchenv_.keydown(false);
}
}
void Dx7Note::update(const char patch[156], int midinote) {
for (int op = 0; op < 6; op++) {
int off = op * 21;
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune);
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
}
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune);
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].update(rates, levels, outlevel, rate_scaling);
}
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
}
void Dx7Note::peekVoiceStatus(VoiceStatus &status) {
for(int i=0;i<6;i++) {
status.amp[i] = Exp2::lookup(params_[i].level_in - (14 * (1 << 24)));
env_[i].getPosition(&status.ampStep[i]);
}
pitchenv_.getPosition(&status.pitchStep);
for(int i=0;i<6;i++) {
status.amp[i] = Exp2::lookup(params_[i].level_in - (14 * (1 << 24)));
env_[i].getPosition(&status.ampStep[i]);
}
pitchenv_.getPosition(&status.pitchStep);
}
/**
@@ -285,7 +306,6 @@ void Dx7Note::transferSignal(Dx7Note &src) {
for (int i=0;i<6;i++) {
params_[i].gain_out = src.params_[i].gain_out;
params_[i].phase = src.params_[i].phase;
//params_[i].phase = 0;
}
}


+ 41
- 40
ports/dexed/source/msfa/dx7note.h View File

@@ -1,12 +1,13 @@
/*
* Copyright 2016-2017 Pascal Gauthier.
* Copyright 2012 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -28,48 +29,48 @@
#include "fm_core.h"
struct VoiceStatus {
uint32_t amp[6];
char ampStep[6];
char pitchStep;
uint32_t amp[6];
char ampStep[6];
char pitchStep;
};
class Dx7Note {
public:
Dx7Note();
void init(const char patch[156], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
const Controllers *ctrls);
void keyup();
public:
Dx7Note();
void init(const uint8_t patch[156], int midinote, int velocity);
// TODO: some way of indicating end-of-note. Maybe should be a return
// value from the compute method? (Having a count return from keyup
// is also tempting, but if there's a dynamic parameter change after
// keyup, that won't work.
// PG:add the update
void update(const char patch[156], int midinote);
void peekVoiceStatus(VoiceStatus &status);
void transferState(Dx7Note& src);
void transferSignal(Dx7Note &src);
void oscSync();
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
const Controllers *ctrls);
void keyup();
// TODO: some way of indicating end-of-note. Maybe should be a return
// value from the compute method? (Having a count return from keyup
// is also tempting, but if there's a dynamic parameter change after
// keyup, that won't work.
// PG:add the update
void update(const uint8_t patch[156], int midinote, int velocity);
void peekVoiceStatus(VoiceStatus &status);
void transferState(Dx7Note& src);
void transferSignal(Dx7Note &src);
void oscSync();
private:
Env env_[6];
FmOpParams params_[6];
PitchEnv pitchenv_;
int32_t basepitch_[6];
int32_t fb_buf_[2];
int32_t fb_shift_;
int32_t ampmodsens_[6];
private:
Env env_[6];
FmOpParams params_[6];
PitchEnv pitchenv_;
int32_t basepitch_[6];
int32_t fb_buf_[2];
int32_t fb_shift_;
int32_t ampmodsens_[6];
int ampmoddepth_;
int algorithm_;
int pitchmoddepth_;
int pitchmodsens_;
int ampmoddepth_;
int algorithm_;
int pitchmoddepth_;
int pitchmodsens_;
};
#endif // SYNTH_DX7NOTE_H_

+ 79
- 69
ports/dexed/source/msfa/env.cc View File

@@ -1,12 +1,13 @@
/*
* Copyright 2017 Pascal Gauthier.
* Copyright 2012 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,91 +25,100 @@
uint32_t Env::sr_multiplier = (1<<24);
const int levellut[] = {
0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46
};
void Env::init_sr(double sampleRate) {
sr_multiplier = (44100.0 / sampleRate) * (1<<24);
}
void Env::init(const int r[4], const int l[4], int32_t ol, int rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
outlevel_ = ol;
rate_scaling_ = rate_scaling;
level_ = 0;
down_ = true;
advance(0);
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
outlevel_ = ol;
rate_scaling_ = rate_scaling;
level_ = 0;
down_ = true;
advance(0);
}
int32_t Env::getsample() {
if (ix_ < 3 || ((ix_ < 4) && !down_)) {
if (rising_) {
const int jumptarget = 1716;
if (level_ < (jumptarget << 16)) {
level_ = jumptarget << 16;
}
level_ += (((17 << 24) - level_) >> 24) * inc_;
// TODO: should probably be more accurate when inc is large
if (level_ >= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
} else { // !rising
level_ -= inc_;
if (level_ <= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
if (ix_ < 3 || ((ix_ < 4) && !down_)) {
if (rising_) {
const int jumptarget = 1716;
if (level_ < (jumptarget << 16)) {
level_ = jumptarget << 16;
}
level_ += (((17 << 24) - level_) >> 24) * inc_;
// TODO: should probably be more accurate when inc is large
if (level_ >= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
} else { // !rising
level_ -= inc_;
if (level_ <= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
}
}
}
// TODO: this would be a good place to set level to 0 when under threshold
return level_;
// TODO: this would be a good place to set level to 0 when under threshold
return level_;
}
void Env::keydown(bool d) {
if (down_ != d) {
down_ = d;
advance(d ? 0 : 3);
}
}
void Env::setparam(int param, int value) {
if (param < 4) {
rates_[param] = value;
} else if (param < 8) {
levels_[param - 4] = value;
}
// Unknown parameter, ignore for now
if (down_ != d) {
down_ = d;
advance(d ? 0 : 3);
}
}
const int levellut[] = {
0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46
};
int Env::scaleoutlevel(int outlevel) {
return outlevel >= 20 ? 28 + outlevel : levellut[outlevel];
return outlevel >= 20 ? 28 + outlevel : levellut[outlevel];
}
void Env::advance(int newix) {
ix_ = newix;
if (ix_ < 4) {
int newlevel = levels_[ix_];
int actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) + outlevel_ - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
// level here is same as Java impl
targetlevel_ = actuallevel << 16;
rising_ = (targetlevel_ > level_);
// rate
int qrate = (rates_[ix_] * 41) >> 6;
qrate += rate_scaling_;
qrate = min(qrate, 63);
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2));
ix_ = newix;
if (ix_ < 4) {
int newlevel = levels_[ix_];
int actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) + outlevel_ - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
// level here is same as Java impl
targetlevel_ = actuallevel << 16;
rising_ = (targetlevel_ > level_);
// rate
int qrate = (rates_[ix_] * 41) >> 6;
qrate += rate_scaling_;
qrate = min(qrate, 63);
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2));
// meh, this should be fixed elsewhere
inc_ = ((int64_t)inc_ * (int64_t)sr_multiplier) >> 24;
}
}
// meh, this should be fixed elsewhere
inc_ = ((int64_t)inc_ * (int64_t)sr_multiplier) >> 24;
}
void Env::update(const int r[4], const int l[4], int32_t ol, int rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
outlevel_ = ol;
rate_scaling_ = rate_scaling;
if ( down_ ) {
// for now we simply reset ourselve at level 3
int newlevel = levels_[2];
int actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
targetlevel_ = actuallevel << 16;
advance(2);
}
}
void Env::getPosition(char *step) {


+ 3
- 1
ports/dexed/source/msfa/env.h View File

@@ -1,4 +1,5 @@
/*
* Copyright 2017 Pascal Gauthier.
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,6 +32,8 @@ class Env {
void init(const int rates[4], const int levels[4], int outlevel,
int rate_scaling);

void update(const int rates[4], const int levels[4], int outlevel,
int rate_scaling);
// Result is in Q24/doubling log format. Also, result is subsampled
// for every N samples.
// A couple more things need to happen for this to be used as a gain
@@ -40,7 +43,6 @@ class Env {
int32_t getsample();

void keydown(bool down);
void setparam(int param, int value);
static int scaleoutlevel(int outlevel);
void getPosition(char *step);


+ 3
- 4
ports/dexed/source/msfa/fm_core.cc View File

@@ -30,9 +30,9 @@ const FmAlgorithm FmCore::algorithms[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
{ { 0xc1, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4 ** EXCEPTION VIA CODE
{ { 0xc1, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
{ { 0xc1, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6 ** EXCEPTION VIA CODE
{ { 0xc1, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
@@ -90,8 +90,7 @@ void FmCore::dump() {
#endif
}
void FmCore::render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controller) {
void FmCore::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) {
const int kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };


+ 10
- 11
ports/dexed/source/msfa/fm_core.h View File

@@ -1,12 +1,12 @@
/*
* Copyright 2012 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -45,14 +45,13 @@ public:
};
class FmCore {
public:
virtual ~FmCore() {};
static void dump();
virtual void render(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int32_t feedback_gain, const Controllers *controller);
protected:
AlignedBuf<int32_t, N>buf_[2];
const static FmAlgorithm algorithms[32];
public:
virtual ~FmCore() {};
static void dump();
virtual void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_gain);
protected:
AlignedBuf<int32_t, N>buf_[2];
const static FmAlgorithm algorithms[32];
};
#endif // __FM_CORE_H

+ 61
- 61
ports/dexed/source/msfa/lfo.cc View File

@@ -1,12 +1,12 @@
/*
* Copyright 2013 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,74 +24,74 @@
uint32_t Lfo::unit_;
void Lfo::init(double sample_rate) {
// constant is 1 << 32 / 15.5s / 11
Lfo::unit_ = (int32_t)(N * 25190424 / sample_rate + 0.5);
// constant is 1 << 32 / 15.5s / 11
Lfo::unit_ = (int32_t)(N * 25190424 / sample_rate + 0.5);
}
void Lfo::reset(const char params[6]) {
int rate = params[0]; // 0..99
int sr = rate == 0 ? 1 : (165 * rate) >> 6;
sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4));
delta_ = unit_ * sr;
int a = 99 - params[1]; // LFO delay
if (a == 99) {
delayinc_ = ~0u;
delayinc2_ = ~0u;
} else {
a = (16 + (a & 15)) << (1 + (a >> 4));
delayinc_ = unit_ * a;
a &= 0xff80;
a = max(0x80, a);
delayinc2_ = unit_ * a;
}
waveform_ = params[5];
sync_ = params[4] != 0;
void Lfo::reset(const uint8_t params[6]) {
int rate = params[0]; // 0..99
int sr = rate == 0 ? 1 : (165 * rate) >> 6;
sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4));
delta_ = unit_ * sr;
int a = 99 - params[1]; // LFO delay
if (a == 99) {
delayinc_ = ~0u;
delayinc2_ = ~0u;
} else {
a = (16 + (a & 15)) << (1 + (a >> 4));
delayinc_ = unit_ * a;
a &= 0xff80;
a = max(0x80, a);
delayinc2_ = unit_ * a;
}
waveform_ = params[5];
sync_ = params[4] != 0;
}
int32_t Lfo::getsample() {
phase_ += delta_;
int32_t x;
switch (waveform_) {
case 0: // triangle
x = phase_ >> 7;
x ^= -(phase_ >> 31);
x &= (1 << 24) - 1;
return x;
case 1: // sawtooth down
return (~phase_ ^ (1U << 31)) >> 8;
case 2: // sawtooth up
return (phase_ ^ (1U << 31)) >> 8;
case 3: // square
return ((~phase_) >> 7) & (1 << 24);
case 4: // sine
return (1 << 23) + (Sin::lookup(phase_ >> 8) >> 1);
case 5: // s&h
if (phase_ < delta_) {
randstate_ = (randstate_ * 179 + 17) & 0xff;
phase_ += delta_;
int32_t x;
switch (waveform_) {
case 0: // triangle
x = phase_ >> 7;
x ^= -(phase_ >> 31);
x &= (1 << 24) - 1;
return x;
case 1: // sawtooth down
return (~phase_ ^ (1U << 31)) >> 8;
case 2: // sawtooth up
return (phase_ ^ (1U << 31)) >> 8;
case 3: // square
return ((~phase_) >> 7) & (1 << 24);
case 4: // sine
return (1 << 23) + (Sin::lookup(phase_ >> 8) >> 1);
case 5: // s&h
if (phase_ < delta_) {
randstate_ = (randstate_ * 179 + 17) & 0xff;
}
x = randstate_ ^ 0x80;
return (x + 1) << 16;
}
x = randstate_ ^ 0x80;
return (x + 1) << 16;
}
return 1 << 23;
return 1 << 23;
}
int32_t Lfo::getdelay() {
uint32_t delta = delaystate_ < (1U << 31) ? delayinc_ : delayinc2_;
uint32_t d = delaystate_ + delta;
if (d < delayinc_) {
return 1 << 24;
}
delaystate_ = d;
if (d < (1U << 31)) {
return 0;
} else {
return (d >> 7) & ((1 << 24) - 1);
}
uint32_t delta = delaystate_ < (1U << 31) ? delayinc_ : delayinc2_;
uint64_t d = ((uint64_t)delaystate_) + delta;
if (d > ~0u) {
return 1 << 24;
}
delaystate_ = d;
if (d < (1U << 31)) {
return 0;
} else {
return (d >> 7) & ((1 << 24) - 1);
}
}
void Lfo::keydown() {
if (sync_) {
phase_ = (1U << 31) - 1;
}
delaystate_ = 0;
if (sync_) {
phase_ = (1U << 31) - 1;
}
delaystate_ = 0;
}

+ 1
- 1
ports/dexed/source/msfa/lfo.h View File

@@ -19,7 +19,7 @@
class Lfo {
public:
static void init(double sample_rate);
void reset(const char params[6]);
void reset(const uint8_t params[6]);
// result is 0..1 in Q24
int32_t getsample();


+ 1
- 7
ports/dexed/source/msfa/synth.h View File

@@ -38,6 +38,7 @@ typedef __int16 SInt16;
#endif
#endif
#include "../Dexed.h"
// #undef SynthMemoryBarrier()
@@ -57,11 +58,4 @@ inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}
void dexed_trace(const char *source, const char *fmt, ...);
#define QER(n,b) ( ((float)n)/(1<<b) )
#define TRACE(fmt, ...)
#endif // __SYNTH_H

Loading…
Cancel
Save