/** * * Copyright (c) 2014-2015 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 * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "PluginParam.h" #include "PluginProcessor.h" #include "PluginData.h" #include "Dexed.h" #include using namespace ::std; uint8_t sysexChecksum(const uint8_t *sysex, int size) { int sum = 0; int i; for (i = 0; i < size; sum -= sysex[i++]); return sum & 0x7F; } void exportSysexPgm(uint8_t *dest, uint8_t *src) { uint8_t header[] = { 0xF0, 0x43, 0x00, 0x00, 0x01, 0x1B }; 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 }; memcpy(dest+161, footer, 2); } /** * Pack a program into a 32 packed sysex */ void Cartridge::packProgram(uint8_t *src, int idx, String name) { uint8_t *bulk = voiceData + 6 + (idx * 128); for(int op = 0; op < 6; op++) { // eg rate and level, brk pt, depth, scaling memcpy(bulk + op * 17, src + op * 21, 11); int pp = op*17; int up = op*21; // left curves bulk[pp+11] = (src[up+11]&0x03) | ((src[up+12]&0x03) << 2); bulk[pp+12] = (src[up+13]&0x07) | ((src[up+20]&0x0f) << 3); // kvs_ams bulk[pp+13] = (src[up+14]&0x03) | ((src[up+15]&0x07) << 2); // output lvl bulk[pp+14] = src[up+16]; // fcoarse_mode bulk[pp+15] = (src[up+17]&0x01) | ((src[up+18]&0x1f) << 1); // fine freq bulk[pp+16] = src[up+19]; } memcpy(bulk + 102, src + 126, 9); // pitch env, algo bulk[111] = (src[135]&0x07) | ((src[136]&0x01) << 3); memcpy(bulk + 112, src + 137, 4); // lfo bulk[116] = (src[141]&0x01) | (((src[142]&0x07) << 1) | ((src[143]&0x07) << 4)); bulk[117] = src[144]; int eos = 0; for(int i=0; i < 10; i++) { char c = (char) name[i]; if ( c == 0 ) eos = 1; if ( eos ) { bulk[118+i] = ' '; continue; } c = c < 32 ? ' ' : c; c = c > 127 ? ' ' : c; bulk[118+i] = c; } } /** * This function normalize data that comes from corrupted sysex. * It used to avoid engine crashing upon extrem values */ char normparm(char value, char max, int id) { if ( value <= max && value >= 0 ) return value; // if this is beyond the max, we expect a 0-255 range, normalize this // to the expected return value; and this value as a random data. value = abs(value); char v = ((float)value)/255 * max; return v; } 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 for(int i=0; i<11; i++) { unpackPgm[op * 21 + i] = normparm(bulk[op * 17 + i], 99, i); } memcpy(unpackPgm + op * 21, bulk + op * 17, 11); char leftrightcurves = bulk[op * 17 + 11]; unpackPgm[op * 21 + 11] = leftrightcurves & 3; unpackPgm[op * 21 + 12] = (leftrightcurves >> 2) & 3; char detune_rs = bulk[op * 17 + 12]; unpackPgm[op * 21 + 13] = detune_rs & 7; char kvs_ams = bulk[op * 17 + 13]; unpackPgm[op * 21 + 14] = kvs_ams & 3; unpackPgm[op * 21 + 15] = kvs_ams >> 2; unpackPgm[op * 21 + 16] = bulk[op * 17 + 14]; // output level char fcoarse_mode = bulk[op * 17 + 15]; unpackPgm[op * 21 + 17] = fcoarse_mode & 1; unpackPgm[op * 21 + 18] = fcoarse_mode >> 1; unpackPgm[op * 21 + 19] = bulk[op * 17 + 16]; // fine freq unpackPgm[op * 21 + 20] = detune_rs >> 3; } for (int i=0; i<8; i++) { unpackPgm[126+i] = normparm(bulk[102+i], 99, 126+i); } unpackPgm[134] = normparm(bulk[110], 31, 134); char oks_fb = bulk[111]; unpackPgm[135] = oks_fb & 7; unpackPgm[136] = oks_fb >> 3; memcpy(unpackPgm + 137, bulk + 112, 4); // lfo char lpms_lfw_lks = bulk[116]; unpackPgm[141] = lpms_lfw_lks & 1; 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; } void DexedAudioProcessor::loadCartridge(Cartridge &sysex) { currentCart = sysex; currentCart.getProgramNames(programNames); } void DexedAudioProcessor::updateProgramFromSysex(const uint8_t *rawdata) { memcpy(data, rawdata, 161); triggerAsyncUpdate(); } void DexedAudioProcessor::setupStartupCart() { File startup = dexedCartDir.getChildFile("Dexed_01.syx"); if ( currentCart.load(startup) != -1 ) return; // 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() { const char init_voice[] = { 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 99, 99, 99, 99, 99, 99, 99, 00, 39, 0, 0, 0, 0, 0, 0, 0, 99, 0, 1, 0, 7, 99, 99, 99, 99, 50, 50, 50, 50, 0, 0, 1, 35, 0, 0, 0, 1, 0, 3, 24, 73, 78, 73, 84, 32, 86, 79, 73, 67, 69 }; for(int i=0;i root(getXmlFromBinary(source, sizeInBytes)); if (root == nullptr) { TRACE("unkown state format"); return; } fx.uiCutoff = root->getDoubleAttribute("cutoff"); fx.uiReso = root->getDoubleAttribute("reso"); fx.uiGain = root->getDoubleAttribute("gain"); currentProgram = root->getIntAttribute("currentProgram"); 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()); setEngineType(root->getIntAttribute("engineType", 0)); monoMode = root->getIntAttribute("monoMode", 0); File possibleCartridge = File(root->getStringAttribute("activeFileCartridge")); if ( possibleCartridge.exists() ) activeFileCartridge = possibleCartridge; XmlElement *dexedBlob = root->getChildByName("dexedBlob"); if ( dexedBlob == NULL ) { TRACE("dexedBlob element not found"); return; } NamedValueSet blobSet; blobSet.setFromXmlAttributes(*dexedBlob); var sysex_blob = blobSet["sysex"]; var program = blobSet["program"]; if ( sysex_blob.isVoid() || program.isVoid() ) { TRACE("unkown serialized blob data"); return; } Cartridge cart; cart.load((uint8 *)sysex_blob.getBinaryData()->getData(), 4104); loadCartridge(cart); memcpy(data, program.getBinaryData()->getData(), 161); lastStateSave = (long) time(NULL); TRACE("setting VST STATE"); updateUI(); } 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 ( 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(); // ========================================================================== // For older versions, we move the Dexed.xml config file // This code will be removed in 0.9.0 File cfgFile = dexedAppDir.getParentDirectory().getChildFile("Dexed.xml"); if ( cfgFile.exists() ) cfgFile.moveFileTo(dexedAppDir.getChildFile("Dexed.xml")); // *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- // ========================================================================== } dexedCartDir = dexedAppDir.getChildFile("Cartridges"); if ( ! dexedCartDir.exists() ) { // Initial setup, we unzip the built-in cartridges dexedCartDir.createDirectory(); File synprezFmDir = dexedCartDir.getChildFile("SynprezFM"); synprezFmDir.createDirectory(); MemoryInputStream *mis = new MemoryInputStream(BinaryData::builtin_pgm_zip, BinaryData::builtin_pgm_zipSize, false); ZipFile *builtin_pgm = new ZipFile(mis, true); for(int i=0;igetNumEntries();i++) { if ( builtin_pgm->getEntry(i)->filename == "Dexed_01.syx" ) { builtin_pgm->uncompressEntry(i, dexedCartDir); } else { builtin_pgm->uncompressEntry(i, synprezFmDir); } } delete builtin_pgm; } }