Unified way of reading/writing sysex (and support of sysex chain that contains performance data)

pull/1/head
asb2m10 9 years ago
parent b2517b47dd
commit f89e26caef
  1. 5223
      Builds/MacOSX/Dexed.xcodeproj/project.pbxproj
  2. BIN
      Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate
  3. 22
      Builds/MacOSX/Dexed.xcodeproj/xcuserdata/asb2m10.xcuserdatad/xcschemes/Dexed.xcscheme
  4. 91
      Builds/MacOSX/Info.plist
  5. 60
      Source/CartManager.cpp
  6. 12
      Source/DXComponents.cpp
  7. 4
      Source/DXComponents.h
  8. 4
      Source/Dexed.h
  9. 2
      Source/GlobalEditor.cpp
  10. 139
      Source/PluginData.cpp
  11. 185
      Source/PluginData.h
  12. 64
      Source/PluginEditor.cpp
  13. 2
      Source/PluginParam.cpp
  14. 12
      Source/PluginProcessor.cpp
  15. 12
      Source/PluginProcessor.h
  16. 13
      Source/ProgramListBox.cpp
  17. 19
      Source/ProgramListBox.h
  18. 5
      Source/msfa/dx7note.cc
  19. 4
      Source/msfa/dx7note.h
  20. 2
      Source/msfa/lfo.cc
  21. 2
      Source/msfa/lfo.h

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0460" LastUpgradeVersion = "0700"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -23,30 +23,42 @@
</BuildActionEntries> </BuildActionEntries>
</BuildAction> </BuildAction>
<TestAction <TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES">
buildConfiguration = "Debug">
<Testables> <Testables>
</Testables> </Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CC364D6AF074DD367C3061CC"
BuildableName = "Dexed.component"
BlueprintName = "Dexed"
ReferencedContainer = "container:Dexed.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions> <AdditionalOptions>
</AdditionalOptions> </AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES"> debugDocumentVersioning = "YES">
</ProfileAction> </ProfileAction>
<AnalyzeAction <AnalyzeAction

@ -1,46 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0">
<plist> <dict>
<dict> <key>AudioComponents</key>
<key>CFBundleExecutable</key> <array>
<string>${EXECUTABLE_NAME}</string> <dict>
<key>CFBundleIconFile</key> <key>description</key>
<string></string> <string>Dexed FM Synth</string>
<key>CFBundleIdentifier</key> <key>factoryFunction</key>
<string>com.digitalsuburban.Dexed</string> <string>DexedAUFactory</string>
<key>CFBundleName</key> <key>manufacturer</key>
<string>Dexed</string> <string>DGSB</string>
<key>CFBundlePackageType</key> <key>name</key>
<string>TDMw</string> <string>Digital Suburban: Dexed</string>
<key>CFBundleSignature</key> <key>subtype</key>
<string>PTul</string> <string>Dexd</string>
<key>CFBundleShortVersionString</key> <key>type</key>
<string>1.0.0</string> <string>aumu</string>
<key>CFBundleVersion</key> <key>version</key>
<string>1.0.0</string> <integer>65536</integer>
<key>NSHumanReadableCopyright</key> </dict>
<string>Digital Suburban</string> </array>
<key>NSHighResolutionCapable</key> <key>CFBundleExecutable</key>
<true/> <string>${EXECUTABLE_NAME}</string>
<key>AudioComponents</key> <key>CFBundleIconFile</key>
<array> <string></string>
<dict> <key>CFBundleIdentifier</key>
<key>name</key> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>Digital Suburban: Dexed</string> <key>CFBundleName</key>
<key>description</key> <string>Dexed</string>
<string>Dexed FM Synth</string> <key>CFBundlePackageType</key>
<key>factoryFunction</key> <string>TDMw</string>
<string>DexedAUFactory</string> <key>CFBundleShortVersionString</key>
<key>manufacturer</key> <string>1.0.0</string>
<string>DGSB</string> <key>CFBundleSignature</key>
<key>type</key> <string>PTul</string>
<string>aumu</string> <key>CFBundleVersion</key>
<key>subtype</key> <string>1.0.0</string>
<string>Dexd</string> <key>NSHighResolutionCapable</key>
<key>version</key> <true/>
<integer>65536</integer> <key>NSHumanReadableCopyright</key>
</dict> <string>Digital Suburban</string>
</array> </dict>
</dict> </plist>
</plist>

@ -115,10 +115,8 @@ void CartManager::programSelected(ProgramListBox *source, int pos) {
mainWindow->processor->setCurrentProgram(pos); mainWindow->processor->setCurrentProgram(pos);
mainWindow->processor->updateHostDisplay(); mainWindow->processor->updateHostDisplay();
} else { } else {
if ( source->getCurrentCart() == nullptr ) uint8_t unpackPgm[161];
return; source->getCurrentCart().unpackProgram(unpackPgm, pos);
char unpackPgm[161];
unpackProgramFromSysex(unpackPgm, source->getCurrentCart(), pos);
activeCart->setSelected(-1); activeCart->setSelected(-1);
browserCart->setSelected(pos); browserCart->setSelected(pos);
repaint(); repaint();
@ -177,7 +175,7 @@ void CartManager::fileDoubleClicked(const File& file) {
if ( file.isDirectory() ) if ( file.isDirectory() )
return; return;
mainWindow->loadCart(file); mainWindow->loadCart(file);
activeCart->setCartridge(mainWindow->processor->sysex); activeCart->setCartridge(mainWindow->processor->currentCart);
} }
void CartManager::fileClicked(const File& file, const MouseEvent& e) { void CartManager::fileClicked(const File& file, const MouseEvent& e) {
@ -215,7 +213,7 @@ void CartManager::setActiveProgram(int idx, String activeName) {
} }
void CartManager::resetActiveSysex() { void CartManager::resetActiveSysex() {
activeCart->setCartridge(mainWindow->processor->sysex); activeCart->setCartridge(mainWindow->processor->currentCart);
} }
void CartManager::selectionChanged() { void CartManager::selectionChanged() {
@ -227,21 +225,14 @@ void CartManager::selectionChanged() {
if ( file.isDirectory() ) if ( file.isDirectory() )
return; return;
String f = file.getFullPathName(); Cartridge browserSysex;
uint8_t syx_data[4104]; int rc = browserSysex.load(file);
ifstream fp_in(f.toRawUTF8(), ios::binary); if ( rc < 0 ) {
if (fp_in.fail()) { AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Unable to open file");
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Unable to open: " + f);
return; return;
} }
fp_in.read((char *)syx_data, 4104); if ( rc != 0 ) {
fp_in.close();
char browserSysex[4104];
memcpy(browserSysex, syx_data+6, 4096);
int checksum = sysexChecksum(((char *) &browserSysex), 4096);
if ( checksum != syx_data[4102] ) {
browserCart->readOnly = true; browserCart->readOnly = true;
} else { } else {
browserCart->readOnly = false; browserCart->readOnly = false;
@ -260,15 +251,12 @@ void CartManager::programRightClicked(ProgramListBox *source, int pos) {
switch(menu.show()) { switch(menu.show()) {
case 1000: case 1000:
char unpackPgm[161]; uint8_t unpackPgm[161];
if ( source == activeCart ) { if ( source == activeCart ) {
unpackProgramFromSysex(unpackPgm, mainWindow->processor->sysex, pos); mainWindow->processor->currentCart.unpackProgram(unpackPgm, pos);
} else { } else {
char *sysex = source->getCurrentCart(); source->getCurrentCart().unpackProgram(unpackPgm, pos);
if ( sysex == nullptr )
return;
unpackProgramFromSysex(unpackPgm, sysex, pos);
} }
if ( mainWindow->processor->sysexComm.isOutputActive() ) if ( mainWindow->processor->sysexComm.isOutputActive() )
@ -284,7 +272,7 @@ void CartManager::programRightClicked(ProgramListBox *source, int pos) {
void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) { void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) {
if ( destListBox == activeCart ) { if ( destListBox == activeCart ) {
char *sysex = mainWindow->processor->sysex; char *sysex = mainWindow->processor->currentCart.getRawVoice();
memcpy(sysex+(dest*128), packedPgm, 128); memcpy(sysex+(dest*128), packedPgm, 128);
mainWindow->updateUI(); mainWindow->updateUI();
} else { } else {
@ -295,22 +283,14 @@ void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *pa
if ( file.isDirectory() ) if ( file.isDirectory() )
return; return;
if ( file.getSize() > 5000 ) if ( file.getSize() != 4104 )
return;
MemoryBlock block;
file.loadFileAsData(block);
if ( block.getSize() < 4104 )
return; return;
char *sysex = ((char *) block.getData()) + 6; Cartridge cart;
memcpy(sysex+(dest*128), packedPgm, 128); cart.load(file);
memcpy(cart.getRawVoice()+(dest*128), packedPgm, 128);
char exported[4104]; cart.saveVoice(file);
exportSysexCart(exported, sysex, 0); browserCart->setCartridge(cart);
file.replaceWithData(exported, 4104);
browserCart->setCartridge(sysex);
} }
} }

@ -157,13 +157,13 @@ static double getDuration(int p_rate, int p_level_l, int p_level_r) {
} }
EnvDisplay::EnvDisplay() { EnvDisplay::EnvDisplay() {
pvalues = (char *) &TMP_LEVEL_PTR; pvalues = (uint8_t *) &TMP_LEVEL_PTR;
} }
void EnvDisplay::paint(Graphics &g) { void EnvDisplay::paint(Graphics &g) {
int h = getHeight(); int h = getHeight();
char *rates = pvalues; uint8_t *rates = pvalues;
char *levels = pvalues + 4; uint8_t *levels = pvalues + 4;
double d[4]; double d[4];
double keyoff = 0.0; double keyoff = 0.0;
@ -250,15 +250,15 @@ void EnvDisplay::paint(Graphics &g) {
} }
PitchEnvDisplay::PitchEnvDisplay() { PitchEnvDisplay::PitchEnvDisplay() {
pvalues = (char *) &TMP_LEVEL_PTR; pvalues = (uint8_t *) &TMP_LEVEL_PTR;
vPos = 0; vPos = 0;
} }
void PitchEnvDisplay::paint(Graphics &g) { void PitchEnvDisplay::paint(Graphics &g) {
g.setColour(Colours::white); g.setColour(Colours::white);
char *levels = pvalues + 4; uint8_t *levels = pvalues + 4;
char *rates = pvalues; uint8_t *rates = pvalues;
float dist[4]; float dist[4];
float total = 0; float total = 0;

@ -26,7 +26,7 @@
class EnvDisplay : public Component { class EnvDisplay : public Component {
public: public:
EnvDisplay(); EnvDisplay();
char *pvalues; uint8_t *pvalues;
char vPos; char vPos;
void paint(Graphics &g); void paint(Graphics &g);
}; };
@ -35,7 +35,7 @@ class PitchEnvDisplay : public Component {
char rvalues[8]; char rvalues[8];
public: public:
PitchEnvDisplay(); PitchEnvDisplay();
char *pvalues; uint8_t *pvalues;
char vPos; char vPos;
void paint(Graphics &g); void paint(Graphics &g);
}; };

@ -24,14 +24,14 @@
void dexed_trace(const char *source, const char *fmt, ...); void dexed_trace(const char *source, const char *fmt, ...);
#ifdef DEBUG #ifdef DEBUG
#define DEXED_VERSION "0.9.0 DEBUG" #define DEXED_VERSION "0.9.1 DEBUG"
#ifdef _MSC_VER #ifdef _MSC_VER
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__) #define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
#else #else
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__) #define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#endif #endif
#else #else
#define DEXED_VERSION "0.9.0" #define DEXED_VERSION "0.9.1"
#define TRACE(fmt, ...) #define TRACE(fmt, ...)
#endif #endif

@ -542,7 +542,7 @@ void GlobalEditor::bind(DexedAudioProcessorEditor *edit) {
processor->fxCutoff->bind(cutoff); processor->fxCutoff->bind(cutoff);
processor->fxReso->bind(reso); processor->fxReso->bind(reso);
processor->output->bind(output); processor->output->bind(output);
algoDisplay->algo = &(processor->data[134]); algoDisplay->algo = (char *) &(processor->data[134]);
pitchEnvDisplay->pvalues = &(processor->data[126]); pitchEnvDisplay->pvalues = &(processor->data[126]);
editor = edit; editor = edit;

@ -28,7 +28,7 @@
#include <fstream> #include <fstream>
using namespace ::std; using namespace ::std;
uint8_t sysexChecksum(const char *sysex, int size) { uint8_t sysexChecksum(const uint8_t *sysex, int size) {
int sum = 0; int sum = 0;
int i; int i;
@ -36,64 +36,9 @@ uint8_t sysexChecksum(const char *sysex, int size) {
return sum & 0x7F; return sum & 0x7F;
} }
String normalizeSysexName(const char *sysexName) { void exportSysexPgm(uint8_t *dest, uint8_t *src) {
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) {
uint8_t header[] = { 0xF0, 0x43, 0x00, 0x00, 0x01, 0x1B }; uint8_t header[] = { 0xF0, 0x43, 0x00, 0x00, 0x01, 0x1B };
header[2] = sysexChl;
memcpy(dest, header, 6); memcpy(dest, header, 6);
// copy 1 unpacked voices // copy 1 unpacked voices
@ -110,8 +55,8 @@ void exportSysexPgm(char *dest, char *src, char sysexChl) {
/** /**
* Pack a program into a 32 packed sysex * Pack a program into a 32 packed sysex
*/ */
void packProgram(uint8_t *dest, uint8_t *src, int idx, String name) { void Cartridge::packProgram(uint8_t *src, int idx, String name) {
uint8_t *bulk = dest + (idx * 128); uint8_t *bulk = voiceData + 6 + (idx * 128);
for(int op = 0; op < 6; op++) { for(int op = 0; op < 6; op++) {
// eg rate and level, brk pt, depth, scaling // eg rate and level, brk pt, depth, scaling
@ -171,8 +116,9 @@ char normparm(char value, char max, int id) {
return v; return v;
} }
void unpackProgramFromSysex(char *unpackPgm, char *sysexCart, int idx) { void Cartridge::unpackProgram(uint8_t *unpackPgm, int idx) {
char *bulk = sysexCart + (idx * 128); // TODO put this in uint8_t :D
char *bulk = (char *)voiceData + 6 + (idx * 128);
for (int op = 0; op < 6; op++) { for (int op = 0; op < 6; op++) {
// eg rate and level, brk pt, depth, scaling // eg rate and level, brk pt, depth, scaling
@ -220,53 +166,33 @@ void unpackProgramFromSysex(char *unpackPgm, char *sysexCart, int idx) {
unpackPgm[160] = 1; unpackPgm[160] = 1;
} }
void DexedAudioProcessor::unpackProgram(int idx) { void DexedAudioProcessor::loadCartridge(Cartridge &sysex) {
unpackProgramFromSysex(data, sysex, idx); 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::updateProgramFromSysex(const uint8 *rawdata) { void DexedAudioProcessor::updateProgramFromSysex(const uint8_t *rawdata) {
memcpy(data, rawdata, 161); memcpy(data, rawdata, 161);
triggerAsyncUpdate(); triggerAsyncUpdate();
} }
void DexedAudioProcessor::setupStartupCart() { void DexedAudioProcessor::setupStartupCart() {
char syx_data[4104];
memset(&syx_data, 0, 4104);
File startup = dexedCartDir.getChildFile("Dexed_01.syx"); File startup = dexedCartDir.getChildFile("Dexed_01.syx");
if ( currentCart.load(startup) != -1 )
return;
if ( startup.exists() ) { // The user deleted the file :/, load from the builtin zip file.
FileInputStream *fis = startup.createInputStream(); MemoryInputStream *mis = new MemoryInputStream(BinaryData::builtin_pgm_zip, BinaryData::builtin_pgm_zipSize, false);
if ( fis == nullptr ) { ZipFile *builtin_pgm = new ZipFile(mis, true);
TRACE("unable to open default cartridge"); InputStream *is = builtin_pgm->createStreamForEntry(builtin_pgm->getIndexOfFileName(("Dexed_01.syx")));
return; Cartridge init;
}
fis->read(syx_data, 4104); if ( init.load(*is) != -1 )
delete fis; loadCartridge(init);
} else {
// The user deleted the file :/, load from the builtin zip file. delete is;
MemoryInputStream *mis = new MemoryInputStream(BinaryData::builtin_pgm_zip, BinaryData::builtin_pgm_zipSize, false); delete builtin_pgm;
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);
} }
void DexedAudioProcessor::resetToInitVoice() { void DexedAudioProcessor::resetToInitVoice() {
@ -305,7 +231,7 @@ void DexedAudioProcessor::pasteEnvFromClipboard(int destOp) {
void DexedAudioProcessor::sendCurrentSysexProgram() { void DexedAudioProcessor::sendCurrentSysexProgram() {
uint8_t raw[167]; uint8_t raw[167];
exportSysexPgm((char *) raw, data, sysexComm.getChl()); exportSysexPgm(raw, data);
if ( sysexComm.isOutputActive() ) { if ( sysexComm.isOutputActive() ) {
sysexComm.send(MidiMessage(raw, 163)); sysexComm.send(MidiMessage(raw, 163));
} }
@ -314,7 +240,7 @@ void DexedAudioProcessor::sendCurrentSysexProgram() {
void DexedAudioProcessor::sendCurrentSysexCartridge() { void DexedAudioProcessor::sendCurrentSysexCartridge() {
uint8_t raw[4104]; uint8_t raw[4104];
exportSysexCart((char *) raw, (char *) &sysex, sysexComm.getChl()); currentCart.saveVoice(raw);
if ( sysexComm.isOutputActive() ) { if ( sysexComm.isOutputActive() ) {
sysexComm.send(MidiMessage(raw, 4104)); sysexComm.send(MidiMessage(raw, 4104));
} }
@ -363,11 +289,8 @@ void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) {
if ( activeFileCartridge.exists() ) if ( activeFileCartridge.exists() )
dexedState.setAttribute("activeFileCartridge", activeFileCartridge.getFullPathName()); dexedState.setAttribute("activeFileCartridge", activeFileCartridge.getFullPathName());
char sysex_blob[4104];
exportSysexCart((char *) &sysex_blob, (char *) sysex, 0);
NamedValueSet blobSet; 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.set("program", var((void *) &data, 161));
blobSet.copyToXmlAttributes(*dexedBlob); blobSet.copyToXmlAttributes(*dexedBlob);
@ -415,7 +338,9 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte
return; 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); memcpy(data, program.getBinaryData()->getData(), 161);
lastStateSave = (long) time(NULL); lastStateSave = (long) time(NULL);

@ -25,46 +25,155 @@
#define SYSEX_SIZE 4104 #define SYSEX_SIZE 4104
#include <stdint.h> #include <stdint.h>
#include "Dexed.h"
enum UnpackedOffset { uint8_t sysexChecksum(const uint8_t *sysex, int size);
egRate,
egLevel = 4,
breakpoint = 8,
lScaleDepth,
rScaleDepth,
lKeyScale,
rKeyScale,
rateScaling,
keyVelocity,
outputLevel,
mode,
fCoarse,
fFine,
oscDetune,
// Global values #define SYSEX_HEADER { 0xF0, 0x43, 0x00, 0x00, 0x20, 0x00 }
pitchEgRate = 126,
pitchEgLevel = 130, class Cartridge {
algorythm = 134, uint8_t voiceData[SYSEX_SIZE];
feedback, uint8_t perfData[SYSEX_SIZE];
oscKeySync,
lfoSpeed, void setHeader() {
lfoDelay, uint8 voiceHeader[] = SYSEX_HEADER;
lfoPmDepth, memcpy(voiceData, voiceHeader, 6);
lfoAmDepth, voiceData[4102] = sysexChecksum(voiceData+6, 4096);
lfoKeySync, voiceData[4103] = 0xF7;
lfoWave, }
middleC,
pModeSens, public:
osc6state 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;
}
int load(InputStream &fis) {
uint8 buffer[65535];
int sz = fis.read(buffer, 65535);
if ( sz == 0 )
return -1;
return load(buffer, sz);
}
int load(const uint8_t *stream, int size) {
uint8 voiceHeader[] = SYSEX_HEADER;
uint8 tmp[65535];
uint8 *pos = tmp;
int status = 3;
if ( size > 65535 )
size = 65535;
memcpy(tmp, stream, size);
while(size >= 4104) {
// random data
if ( pos[0] != 0xF0 ) {
if ( status != 0 )
return status;
memcpy(voiceData, pos+6, 4096);
return 2;
}
pos[3] = 0;
if ( memcmp(pos, voiceHeader, 6) == 0 ) {
memcpy(voiceData, pos, SYSEX_SIZE);
if ( sysexChecksum(voiceData + 6, 4096) == pos[4102] )
status = 0;
else
status = 1;
size -= 4104;
pos += 4104;
continue;
}
String normalizeSysexName(const char *sysexName); // other sysex
uint8_t sysexChecksum(const char *sysex, int size); int i;
void extractProgramNames(const char *block, StringArray &dest); for(i=0;i<size-1;i++) {
void exportSysexCart(char *dest, char *src, char sysexChl); if ( pos[i] == 0xF7 )
void exportSysexPgm(char *dest, char *src, char sysexChl); break;
void packProgram(uint8_t *dest, uint8_t *src, int idx, String name); }
void unpackProgramFromSysex(char *unpackPgm, char *sysexCart, int idx); size -= i;
stream += i;
}
return status;
}
int saveVoice(File f) {
setHeader();
return f.replaceWithData(voiceData, SYSEX_SIZE);
}
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);
};
#endif // PLUGINDATA_H_INCLUDED #endif // PLUGINDATA_H_INCLUDED

@ -140,22 +140,18 @@ void DexedAudioProcessorEditor::cartShow() {
void DexedAudioProcessorEditor::loadCart(File file) { void DexedAudioProcessorEditor::loadCart(File file) {
String f = file.getFullPathName(); Cartridge cart;
uint8_t syx_data[4104];
ifstream fp_in(f.toRawUTF8(), ios::binary); if ( cart.load(file) < 0 ) {
if (fp_in.fail()) {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Error", "Error",
"Unable to open: " + f); "Unable to open: " + file.getFullPathName());
return; return;
} }
fp_in.read((char *)syx_data, 4104);
fp_in.close(); processor->loadCartridge(cart);
if ( processor->importSysex((char *) &syx_data) ) {
global.setSystemMessage(String("Unkown sysex format !?"));
}
processor->setCurrentProgram(0);
rebuildProgramCombobox(); rebuildProgramCombobox();
processor->setCurrentProgram(0);
global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification); global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification);
processor->updateHostDisplay(); processor->updateHostDisplay();
@ -167,19 +163,10 @@ void DexedAudioProcessorEditor::saveCart() {
FileChooser fc ("Export DX sysex...", processor->dexedCartDir, "*.syx", 1); FileChooser fc ("Export DX sysex...", processor->dexedCartDir, "*.syx", 1);
if ( fc.browseForFileToSave(true) ) { if ( fc.browseForFileToSave(true) ) {
String f = fc.getResults().getReference(0).getFullPathName(); if ( ! processor->currentCart.saveVoice(fc.getResults().getReference(0)) ) {
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()) {
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Error", "Error",
"Unable to write: " + f); "Unable to write: " + fc.getResults().getReference(0).getFullPathName());
} }
} }
} }
@ -250,6 +237,8 @@ void DexedAudioProcessorEditor::updateUI() {
void DexedAudioProcessorEditor::rebuildProgramCombobox() { void DexedAudioProcessorEditor::rebuildProgramCombobox() {
global.programs->clear(dontSendNotification); global.programs->clear(dontSendNotification);
processor->currentCart.getProgramNames(processor->programNames);
for(int i=0;i<processor->getNumPrograms();i++) { for(int i=0;i<processor->getNumPrograms();i++) {
String id; String id;
id << (i+1) << ". " << processor->getProgramName(i); id << (i+1) << ". " << processor->getProgramName(i);
@ -258,7 +247,7 @@ void DexedAudioProcessorEditor::rebuildProgramCombobox() {
global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification); 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); cartManager.setActiveProgram(processor->getCurrentProgram(), name);
if ( name != processor->getProgramName(processor->getCurrentProgram()) ) if ( name != processor->getProgramName(processor->getCurrentProgram()) )
global.programs->setText("**. " + name, dontSendNotification); global.programs->setText("**. " + name, dontSendNotification);
@ -267,11 +256,9 @@ void DexedAudioProcessorEditor::rebuildProgramCombobox() {
} }
void DexedAudioProcessorEditor::storeProgram() { void DexedAudioProcessorEditor::storeProgram() {
String currentName = normalizeSysexName((const char *) processor->data+145); String currentName = Cartridge::normalizePgmName((const char *) processor->data+145);
char destSysex[4096]; Cartridge destSysex = processor->currentCart;
File *externalFile = NULL; File *externalFile = NULL;
memcpy(&destSysex, processor->sysex, 4096);
bool activeCartridgeFound = processor->activeFileCartridge.exists(); bool activeCartridgeFound = processor->activeFileCartridge.exists();
@ -292,7 +279,7 @@ void DexedAudioProcessorEditor::storeProgram() {
// TODO: fix the name length to 10 // TODO: fix the name length to 10
StringArray programs; StringArray programs;
extractProgramNames((char *) &destSysex, programs); destSysex.getProgramNames(programs);
dialog.addComboBox("Dest", programs, "Program Destination"); dialog.addComboBox("Dest", programs, "Program Destination");
@ -318,12 +305,9 @@ void DexedAudioProcessorEditor::storeProgram() {
if ( externalFile != NULL ) if ( externalFile != NULL )
delete externalFile; delete externalFile;
MemoryBlock block;
externalFile = new File(fc.getResults().getReference(0)); externalFile = new File(fc.getResults().getReference(0));
if ( externalFile->loadFileAsData(block) ) { if ( destSysex.load(*externalFile) )
block.copyTo(destSysex, 6, 4096);
continue; continue;
}
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Read error", "Unable to read file"); AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Read error", "Unable to read file");
} }
} }
@ -340,8 +324,7 @@ void DexedAudioProcessorEditor::storeProgram() {
} }
if ( externalFile == NULL ) { if ( externalFile == NULL ) {
packProgram((uint8_t *) processor->sysex, (uint8_t *) processor->data, programNum, programName); processor->currentCart.packProgram((uint8_t *) processor->data, programNum, programName);
processor->programNames.set(programNum, programName);
rebuildProgramCombobox(); rebuildProgramCombobox();
processor->setCurrentProgram(programNum); processor->setCurrentProgram(programNum);
processor->updateHostDisplay(); processor->updateHostDisplay();
@ -355,18 +338,13 @@ void DexedAudioProcessorEditor::storeProgram() {
break; break;
destination = fc.getResult(); destination = fc.getResult();
} }
char sysexFile[4104];
exportSysexCart((char *) &sysexFile, (char *) &processor->sysex, 0); processor->currentCart.saveVoice(destination);
if ( ! destination.replaceWithData(sysexFile, 4104) ) {
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file");
}
processor->activeFileCartridge = destination; processor->activeFileCartridge = destination;
} }
} else { } else {
packProgram((uint8_t *) &destSysex, (uint8_t *) processor->data, programNum, programName); destSysex.packProgram((uint8_t *) processor->data, programNum, programName);
char sysexFile[4104]; if ( ! destSysex.saveVoice(*externalFile)) {
exportSysexCart((char *) &sysexFile, (char *) &destSysex, 0);
if ( ! externalFile->replaceWithData(sysexFile, 4104) ) {
AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file"); AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file");
} }
} }

@ -512,7 +512,7 @@ void DexedAudioProcessor::setCurrentProgram(int index) {
panic(); panic();
index = index > 31 ? 31 : index; index = index > 31 ? 31 : index;
unpackProgram(index); currentCart.unpackProgram(data, index);
lfo.reset(data + 137); lfo.reset(data + 137);
currentProgram = index; currentProgram = index;
triggerAsyncUpdate(); triggerAsyncUpdate();

@ -152,7 +152,7 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi
MidiBuffer::Iterator it(midiMessages); MidiBuffer::Iterator it(midiMessages);
hasMidiMessage = it.getNextEvent(*nextMidi,midiEventPos); hasMidiMessage = it.getNextEvent(*nextMidi,midiEventPos);
float *channelData = buffer.getSampleData(0); float *channelData = buffer.getWritePointer(0);
// flush first events // flush first events
for (i=0; i < numSamples && i < extra_buf_size; i++) { for (i=0; i < numSamples && i < extra_buf_size; i++) {
@ -443,13 +443,11 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid
// 32 voice dump // 32 voice dump
if ( buf[3] == 9 ) { if ( buf[3] == 9 ) {
if ( sz < 4104 ) { Cartridge received;
TRACE("wrong 32 voice datasize %d", sz); if ( received.load(buf, sz) ) {
return; loadCartridge(received);
setCurrentProgram(0);
} }
TRACE("update 32bulk voice");
importSysex((const char *)buf);
setCurrentProgram(0);
} }
updateHostDisplay(); updateHostDisplay();

@ -115,14 +115,14 @@ class DexedAudioProcessor : public AudioProcessor, public AsyncUpdater, public
char clipboardContent; char clipboardContent;
void resolvAppDir(); void resolvAppDir();
public : public :
// in MIDI units (0x4000 is neutral) // in MIDI units (0x4000 is neutral)
Controllers controllers; Controllers controllers;
StringArray programNames; StringArray programNames;
char sysex[4096]; Cartridge currentCart;
char data[161]; uint8_t data[161];
//CartridgeManager cartManager;
SysexComm sysexComm; SysexComm sysexComm;
VoiceStatus voiceStatus; VoiceStatus voiceStatus;
File activeFileCartridge; File activeFileCartridge;
@ -154,7 +154,8 @@ public :
ScopedPointer<CtrlFloat> fxReso; ScopedPointer<CtrlFloat> fxReso;
ScopedPointer<CtrlFloat> output; ScopedPointer<CtrlFloat> output;
int importSysex(const char *imported); //int importSysex(const char *imported);
void loadCartridge(Cartridge &cart);
void setDxValue(int offset, int v); void setDxValue(int offset, int v);
//============================================================================== //==============================================================================
@ -184,7 +185,6 @@ public :
bool hasEditor() const; bool hasEditor() const;
void updateUI(); void updateUI();
bool peekVoiceStatus(); bool peekVoiceStatus();
void unpackProgram(int idx);
void updateProgramFromSysex(const uint8 *rawdata); void updateProgramFromSysex(const uint8 *rawdata);
void setupStartupCart(); void setupStartupCart();

@ -30,6 +30,7 @@ ProgramListBox::ProgramListBox(const String name, int numCols) : Component(name)
hasContent = false; hasContent = false;
dragCandidate = -1; dragCandidate = -1;
readOnly = false; readOnly = false;
programNames.clear();
} }
void ProgramListBox::paint(Graphics &g) { void ProgramListBox::paint(Graphics &g) {
@ -85,9 +86,9 @@ void ProgramListBox::resized() {
cellHeight = getHeight() / rows; cellHeight = getHeight() / rows;
} }
void ProgramListBox::setCartridge(char *sysex) { void ProgramListBox::setCartridge(Cartridge &cart) {
extractProgramNames((const char *)sysex, programNames); cartContent = cart;
memcpy(cartContent, sysex, 4104); cartContent.getProgramNames(programNames);
hasContent = true; hasContent = true;
repaint(); repaint();
} }
@ -138,7 +139,7 @@ void ProgramListBox::mouseDrag(const MouseEvent &event) {
g.fillRect(0,0,cellWidth, cellHeight); g.fillRect(0,0,cellWidth, cellHeight);
g.setColour(Colours::white); g.setColour(Colours::white);
g.drawFittedText(programNames[position], 0, 0, cellWidth, cellHeight, Justification::centred, true); 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); var description = var(src, 128);
dragContainer->startDragging(description, this, snapshot, false); dragContainer->startDragging(description, this, snapshot, false);
} }
@ -148,9 +149,7 @@ void ProgramListBox::setSelected(int idx) {
selectedPgm = idx; selectedPgm = idx;
} }
char* ProgramListBox::getCurrentCart() { Cartridge &ProgramListBox::getCurrentCart() {
if ( ! hasContent )
return nullptr;
return cartContent; return cartContent;
} }

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

@ -139,7 +139,7 @@ Dx7Note::Dx7Note() {
} }
} }
void Dx7Note::init(const char patch[156], int midinote, int velocity) { void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) {
int rates[4]; int rates[4];
int levels[4]; int levels[4];
for (int op = 0; op < 6; op++) { for (int op = 0; op < 6; op++) {
@ -244,7 +244,7 @@ void Dx7Note::keyup() {
} }
} }
void Dx7Note::update(const char patch[156], int midinote) { void Dx7Note::update(const uint8_t patch[156], int midinote) {
for (int op = 0; op < 6; op++) { for (int op = 0; op < 6; op++) {
int off = op * 21; int off = op * 21;
int mode = patch[off + 17]; int mode = patch[off + 17];
@ -285,7 +285,6 @@ void Dx7Note::transferSignal(Dx7Note &src) {
for (int i=0;i<6;i++) { for (int i=0;i<6;i++) {
params_[i].gain_out = src.params_[i].gain_out; params_[i].gain_out = src.params_[i].gain_out;
params_[i].phase = src.params_[i].phase; params_[i].phase = src.params_[i].phase;
//params_[i].phase = 0;
} }
} }

@ -36,7 +36,7 @@ struct VoiceStatus {
class Dx7Note { class Dx7Note {
public: public:
Dx7Note(); Dx7Note();
void init(const char patch[156], int midinote, int velocity); void init(const uint8_t patch[156], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's // Note: this _adds_ to the buffer. Interesting question whether it's
// worth it... // worth it...
@ -51,7 +51,7 @@ class Dx7Note {
// keyup, that won't work. // keyup, that won't work.
// PG:add the update // PG:add the update
void update(const char patch[156], int midinote); void update(const uint8_t patch[156], int midinote);
void peekVoiceStatus(VoiceStatus &status); void peekVoiceStatus(VoiceStatus &status);
void transferState(Dx7Note& src); void transferState(Dx7Note& src);
void transferSignal(Dx7Note &src); void transferSignal(Dx7Note &src);

@ -28,7 +28,7 @@ void Lfo::init(double sample_rate) {
Lfo::unit_ = (int32_t)(N * 25190424 / sample_rate + 0.5); Lfo::unit_ = (int32_t)(N * 25190424 / sample_rate + 0.5);
} }
void Lfo::reset(const char params[6]) { void Lfo::reset(const uint8_t params[6]) {
int rate = params[0]; // 0..99 int rate = params[0]; // 0..99
int sr = rate == 0 ? 1 : (165 * rate) >> 6; int sr = rate == 0 ? 1 : (165 * rate) >> 6;
sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4)); sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4));

@ -19,7 +19,7 @@
class Lfo { class Lfo {
public: public:
static void init(double sample_rate); 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 // result is 0..1 in Q24
int32_t getsample(); int32_t getsample();

Loading…
Cancel
Save