mirror of https://github.com/dcoredump/dexed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
8.9 KiB
293 lines
8.9 KiB
/**
|
|
*
|
|
* Copyright (c) 2014 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 2 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 <time.h>
|
|
|
|
#include "PluginParam.h"
|
|
#include "PluginProcessor.h"
|
|
#include "PluginData.h"
|
|
|
|
|
|
uint8_t sysexChecksum(const char *sysex) {
|
|
uint8_t sum = 0;
|
|
for (int i=0; i<4096; i++)
|
|
sum = (sum + sysex[i]) % (1 << 8);
|
|
return ((1 << 8) - sum);
|
|
}
|
|
|
|
|
|
void extractProgramNames(const char *block, StringArray &dest) {
|
|
char programName[11];
|
|
|
|
dest.clear();
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
memcpy(programName, block + ((i * 128) + 118), 11);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
char c = (unsigned char) programName[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;
|
|
}
|
|
programName[j] = c;
|
|
}
|
|
programName[10] = 0;
|
|
|
|
dest.add(String(programName));
|
|
}
|
|
}
|
|
|
|
int DexedAudioProcessor::importSysex(const char *imported) {
|
|
memcpy(sysex, imported + 6, 4096);
|
|
|
|
uint8_t checksum = sysexChecksum(sysex);
|
|
extractProgramNames(sysex, programNames);
|
|
|
|
if ( checksum != imported[4102] ) {
|
|
TRACE("sysex import checksum doesnt match");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void DexedAudioProcessor::exportSysex(char *dest) {
|
|
uint8_t header[] = { 0xF0, 0x43, 0x00, 0x09, 0x20, 0x00 };
|
|
memcpy(dest, header, 6);
|
|
|
|
// copy 32 voices
|
|
memcpy(dest+6, sysex, 4096);
|
|
|
|
// make checksum for dump
|
|
uint8_t footer[] = { sysexChecksum(sysex), 0xF7 };
|
|
|
|
memcpy(dest+4102, footer, 2);
|
|
}
|
|
|
|
/**
|
|
* This function normalize data that comes from corrupted sysex data.
|
|
* It used to avoid engine crashing upon extrem values
|
|
*/
|
|
char normparm(char value, char max) {
|
|
if ( value <= max )
|
|
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.
|
|
|
|
return ((float)value)/255 * max;
|
|
}
|
|
|
|
void DexedAudioProcessor::unpackProgram(int idx) {
|
|
char *bulk = sysex + (idx * 128);
|
|
|
|
for (int op = 0; op < 6; op++) {
|
|
// eg rate and level, brk pt, depth, scaling
|
|
memcpy(data + op * 21, bulk + op * 17, 11);
|
|
char leftrightcurves = bulk[op * 17 + 11];
|
|
data[op * 21 + 11] = leftrightcurves & 3;
|
|
data[op * 21 + 12] = (leftrightcurves >> 2) & 3;
|
|
char detune_rs = bulk[op * 17 + 12];
|
|
data[op * 21 + 13] = detune_rs & 7;
|
|
char kvs_ams = bulk[op * 17 + 13];
|
|
data[op * 21 + 14] = kvs_ams & 3;
|
|
data[op * 21 + 15] = kvs_ams >> 2;
|
|
data[op * 21 + 16] = bulk[op * 17 + 14]; // output level
|
|
char fcoarse_mode = bulk[op * 17 + 15];
|
|
data[op * 21 + 17] = fcoarse_mode & 1;
|
|
data[op * 21 + 18] = fcoarse_mode >> 1;
|
|
data[op * 21 + 19] = bulk[op * 17 + 16]; // fine freq
|
|
data[op * 21 + 20] = detune_rs >> 3;
|
|
}
|
|
memcpy(data + 126, bulk + 102, 9); // pitch env, algo
|
|
char oks_fb = bulk[111];
|
|
data[135] = oks_fb & 7;
|
|
data[136] = oks_fb >> 3;
|
|
memcpy(data + 137, bulk + 112, 4); // lfo
|
|
char lpms_lfw_lks = bulk[116];
|
|
data[141] = lpms_lfw_lks & 1;
|
|
data[142] = (lpms_lfw_lks >> 1) & 7;
|
|
data[143] = lpms_lfw_lks >> 4;
|
|
memcpy(data + 144, bulk + 117, 11); // transpose, name
|
|
data[155] = 1; // operator on/off
|
|
data[156] = 1;
|
|
data[157] = 1;
|
|
data[158] = 1;
|
|
data[159] = 1;
|
|
data[160] = 1;
|
|
}
|
|
|
|
void DexedAudioProcessor::packProgram(int idx, const char *name) {
|
|
char *bulk = sysex + (idx * 128);
|
|
|
|
for(int op = 0; op < 6; op++) {
|
|
// eg rate and level, brk pt, depth, scaling
|
|
memcpy(bulk + op * 17, data + op * 21, 11);
|
|
int pp = op*17;
|
|
int up = op*21;
|
|
|
|
// left curves
|
|
bulk[pp+11] = (data[up+11]&0x03) | ((data[up+12]&0x03) << 2);
|
|
bulk[pp+12] = (data[up+13]&0x07) | ((data[up+20]&0x0f) << 3);
|
|
// kvs_ams
|
|
bulk[pp+13] = (data[up+14]&0x03) | ((data[up+15]&0x07) << 2);
|
|
// output lvl
|
|
bulk[pp+14] = data[up+16];
|
|
// fcoarse_mode
|
|
bulk[pp+15] = (data[up+17]&0x01) | ((data[up+18]&0x1f) << 1);
|
|
// fine freq
|
|
bulk[pp+16] = data[up+19];
|
|
}
|
|
memcpy(bulk + 102, data + 126, 9); // pitch env, algo
|
|
bulk[111] = (data[135]&0x07) | ((data[136]&0x01) << 3);
|
|
memcpy(bulk + 112, data + 137, 4); // lfo
|
|
bulk[116] = (data[141]&0x01) | (((data[142]&0x07) << 1) | ((data[143]&0x07) << 4));
|
|
bulk[117] = data[144];
|
|
int eos = 0;
|
|
for(int i=0; i < 10; i++) {
|
|
char c = name[i];
|
|
if ( c == 0 )
|
|
eos = 1;
|
|
if ( eos ) {
|
|
bulk[117+i] = ' ';
|
|
continue;
|
|
}
|
|
c = c < 32 ? ' ' : c;
|
|
c = c > 127 ? ' ' : c;
|
|
bulk[117+i] = c;
|
|
}
|
|
|
|
char programName[11];
|
|
memcpy(programName, bulk+117, 10);
|
|
programName[10] = 0;
|
|
programNames.set(idx, String(programName));
|
|
}
|
|
|
|
void DexedAudioProcessor::updateProgramFromSysex(const uint8 *rawdata) {
|
|
memcpy(data, rawdata, 160);
|
|
triggerAsyncUpdate();
|
|
}
|
|
|
|
void DexedAudioProcessor::loadBuiltin(int idx) {
|
|
InputStream *is = builtin_pgm->createStreamForEntry(idx);
|
|
|
|
if ( is == NULL ) {
|
|
TRACE("ENTRY IN ZIP NOT FOUND");
|
|
return;
|
|
}
|
|
|
|
uint8_t syx_data[4104];
|
|
is->read(&syx_data, 4104);
|
|
delete is;
|
|
|
|
importSysex((char *) &syx_data);
|
|
}
|
|
|
|
#define CURRENT_PLUGINSTATE_VERSION 2
|
|
struct PluginState {
|
|
int version;
|
|
uint8_t sysex[4104];
|
|
uint8_t program[161];
|
|
float cutoff;
|
|
float reso;
|
|
int programNum;
|
|
};
|
|
|
|
//==============================================================================
|
|
void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) {
|
|
// You should use this method to store your parameters in the memory block.
|
|
// You could do that either as raw data, or use the XML or ValueTree classes
|
|
// as intermediaries to make it easy to save and load complex data.
|
|
|
|
// used to SAVE plugin state
|
|
|
|
PluginState state;
|
|
|
|
state.version = CURRENT_PLUGINSTATE_VERSION;
|
|
|
|
exportSysex((char *)(&state.sysex));
|
|
memcpy(state.program, data, 161);
|
|
state.cutoff = fx.uiCutoff;
|
|
state.reso = fx.uiReso;
|
|
state.programNum = currentProgram;
|
|
|
|
destData.insert(&state, sizeof(PluginState), 0);
|
|
}
|
|
|
|
void DexedAudioProcessor::setStateInformation(const void* source, int sizeInBytes) {
|
|
// You should use this method to restore your parameters from this memory block,
|
|
// whose contents will have been created by the getStateInformation() call.
|
|
|
|
// used to LOAD plugin state
|
|
|
|
PluginState state;
|
|
|
|
if ( sizeInBytes < sizeof(PluginState) ) {
|
|
TRACE("too small plugin state size %d", sizeInBytes);
|
|
return;
|
|
}
|
|
|
|
if ( sizeInBytes > sizeof(PluginState) ) {
|
|
TRACE("too big plugin state size %d", sizeInBytes);
|
|
sizeInBytes = sizeof(PluginState);
|
|
}
|
|
|
|
memcpy((void *) &state, source, sizeInBytes);
|
|
|
|
if ( state.version != CURRENT_PLUGINSTATE_VERSION ) {
|
|
TRACE("version of VST chunk is not compatible, bailing out");
|
|
return;
|
|
}
|
|
|
|
importSysex((char *) state.sysex);
|
|
memcpy(data, state.program, 161);
|
|
|
|
fx.uiCutoff = state.cutoff;
|
|
fx.uiReso = state.reso;
|
|
currentProgram = state.programNum;
|
|
|
|
lastStateSave = (long) time(NULL);
|
|
TRACE("setting VST STATE");
|
|
updateUI();
|
|
}
|
|
|
|
//==============================================================================
|
|
/*void DexedAudioProcessor::getCurrentProgramStateInformation(
|
|
MemoryBlock& destData) {
|
|
destData.insert(data, 161, 0);
|
|
}
|
|
|
|
void DexedAudioProcessor::setCurrentProgramStateInformation(const void* source,
|
|
int sizeInBytes) {
|
|
memcpy((void *) data, source, sizeInBytes);
|
|
updateUI();
|
|
}*/
|
|
|