Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 2368 lines (2151 sloc) 88.7 KB
/*
* Copyright 2013 Xavier Hosxe
*
* Author: Xavier Hosxe (xavier <dot> hosxe (at) g m a i l <dot> com)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "stm32f4xx_rng.h"
#include "SynthState.h"
#include "Hexter.h"
#include "LiquidCrystal.h"
extern LiquidCrystal lcd;
#include "Synth.h"
extern Synth synth;
#define NULL 0
// FLASH : __attribute__ ((section (".USER_FLASH")))
// Ex : const char* nullNames [] __attribute__ ((section (".USER_FLASH")))= {};
// DISPLAY structures
const char* allChars = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 .,;:<>&*$";
const char* nullNames []= {};
const unsigned char* nullNamesOrder = NULL;
const char* algoNames [] = { "alg1", "alg2", "alg3", "alg4", "alg5", "alg6", "alg7", "alg8", "alg9",
"al10", "al11", "al12", "al13", "al14", "al15", "al16", "al17", "al18", "al19",
"al20", "al21", "al22", "al23", "al24", "al25", "al26", "al27", "al28" };
struct ParameterRowDisplay engine1ParameterRow = {
"Engine" ,
{ "Algo", "Velo", "Voic", "Glid" },
{
{ALGO1, ALGO_END-1, ALGO_END, DISPLAY_TYPE_STRINGS, algoNames, nullNamesOrder, nullNamesOrder},
{0, 16, 17, DISPLAY_TYPE_INT, nullNames,nullNamesOrder, nullNamesOrder },
{0, 16, 17, DISPLAY_TYPE_VOICES, nullNames, nullNamesOrder, nullNamesOrder },
{0, 10, 11, DISPLAY_TYPE_INT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
const char* clockName[] = { "Off ", "Int ", "Ext " };
const char* dirName[] = { "Up ", "Down", "U&D ", "Play", "Rand", "Chrd", "RtUp", "RtDn", "RtUD", "ShUp", "ShDn", "ShUD" };
struct ParameterRowDisplay engineArp1ParameterRow = {
"Arpeggiator" ,
{ "Clk ", "BPM ", "Dire" , "Octv"},
{
{0, 2, 3, DISPLAY_TYPE_STRINGS, clockName, nullNamesOrder, nullNamesOrder },
{10, 240, 231, DISPLAY_TYPE_INT, nullNames,nullNamesOrder, nullNamesOrder },
{0, 11, 12, DISPLAY_TYPE_STRINGS, dirName, nullNamesOrder, nullNamesOrder },
{1, 3, 3, DISPLAY_TYPE_INT, nullNames,nullNamesOrder, nullNamesOrder }
}
};
// 192, 144, 96, 72, 64, 48, 36, 32, 24, 16, 12, 8, 6, 4, 3, 2, 1
const char* divNames[] = { "2/1 ", "3/2 ", "1/1 ", "3/4 ", "2/3 ", "1/2 ", "3/8 ", "1/3 ", "1/4 ", "1/6 ", "1/8 ",
"1/12", "1/16", "1/24", "1/32", "1/48", "1/96"};
const char* activeName[] = { "Off ", "On " };
const char* patternName[] = { "1 ", "2 ", "3 ", "4 ", "5 ", "6 ", "7 ", "8 ", "9 ",
"10 ", "11 ", "12 ", "13 ", "14 ", "15 ", "16 ", "17 ", "18 ", "19 ",
"20 ", "21 ", "22 ",
"Usr1", "Usr2", "Usr3", "Usr4" };
struct ParameterRowDisplay engineArp2ParameterRow = {
"Arpeggiator",
{ "Ptrn", "Divi", "Dura", "Latc" },
{
{0, ARPEGGIATOR_PATTERN_COUNT-1, ARPEGGIATOR_PATTERN_COUNT, DISPLAY_TYPE_STRINGS, patternName, nullNamesOrder, nullNamesOrder},
{0, 16, 17, DISPLAY_TYPE_STRINGS, divNames, nullNamesOrder, nullNamesOrder },
{0, 16, 17, DISPLAY_TYPE_STRINGS, divNames, nullNamesOrder, nullNamesOrder },
{0, 1, 2, DISPLAY_TYPE_STRINGS, activeName, nullNamesOrder, nullNamesOrder },
}
};
struct ParameterRowDisplay engineArpPatternRow = {
"Pattern ",
{ " ", " ", " ", " " },
{
{0, 0, 0, DISPLAY_TYPE_ARP_PATTERN, nullNames, nullNamesOrder, nullNamesOrder },
{0, 0, 0, DISPLAY_TYPE_ARP_PATTERN, nullNames, nullNamesOrder, nullNamesOrder },
{0, 0, 0, DISPLAY_TYPE_ARP_PATTERN, nullNames, nullNamesOrder, nullNamesOrder },
{0, 0, 0, DISPLAY_TYPE_ARP_PATTERN, nullNames, nullNamesOrder, nullNamesOrder },
}
};
struct ParameterRowDisplay engineIM1ParameterRow = {
"Modulation" ,
{ "IM1 ", "v ", "IM2 ", "v "},
{
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay engineIM2ParameterRow = {
"Modulation" ,
{ "IM3 ", "v ", "IM4 ", "v "},
{
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay engineIM3ParameterRow = {
"Modulation" ,
{ "IM5 ", "v ", "IM6 ", "v "},
{
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay engineMix1ParameterRow = {
"Mixer" ,
{ "Mix1", "Pan1", "Mix2", "Pan2" },
{
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{-1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{-1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
}
};
struct ParameterRowDisplay engineMix2ParameterRow = {
"Mixer" ,
{ "Mix3", "Pan3", "Mix4", "Pan4" },
{
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{-1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{-1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay engineMix3ParameterRow = {
"Mixer" ,
{ "Mix5", "Pan5", "Mix6", "Pan6" },
{
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{-1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{-1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
const char* fxName []= { "Off ", "Mix ", "LP ", "HP ", "Bass", "BP ", "Crsh" } ;
struct ParameterRowDisplay effectParameterRow = {
"Filter" ,
{ "Type", " ", " ", "Gain" },
{
{0, FILTER_LAST - 1, FILTER_LAST, DISPLAY_TYPE_STRINGS, fxName, nullNamesOrder, nullNamesOrder },
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{0, 2, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct FilterRowDisplay filterRowDisplay[FILTER_LAST] = {
{ NULL, NULL, "Gain" },
{ "Pan ", NULL, "Gain" },
{ "Freq", "Res ", "Gain" },
{ "Freq", "Res ", "Gain" },
{ "LoFr", "Boos", "Gain" },
{ "Freq", "Q ", "Gain" },
{ "Samp", "Bits", "Gain" },
};
const char* oscShapeNames []= {"sin ", "saw ", "squa", "s^2 ", "szer", "spos", "rand", "off ", "Usr1", "Usr2", "Usr3", "Usr4", "Usr5", "Usr6"} ;
const unsigned char oscShapeNamesOrder[] = { 7, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13};
// { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
const unsigned char oscShapeNamesPosition[] = { 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13};
const char* oscTypeNames [] = { "keyb", "fixe", "kehz"};
const unsigned char oscTypeNamesOrder[] = { 0, 2 ,1};
const unsigned char oscTypeNamesPosition[] = { 0, 2, 1};
struct ParameterRowDisplay oscParameterRow = {
"Oscillator",
{ "Shap", "FTyp", "Freq", "FTun" },
{
{ OSC_SHAPE_SIN, OSC_SHAPE_LAST -1, OSC_SHAPE_LAST, DISPLAY_TYPE_STRINGS, oscShapeNames, oscShapeNamesOrder, oscShapeNamesPosition},
{ OSC_FT_KEYBOARD, OSC_FT_KEYHZ, 3, DISPLAY_TYPE_STRINGS, oscTypeNames, oscTypeNamesOrder, oscTypeNamesPosition},
{ 0, 16, 193, DISPLAY_TYPE_FLOAT_OSC_FREQUENCY , nullNames, nullNamesOrder, nullNamesOrder },
{ -9, 9, 1801, DISPLAY_TYPE_FLOAT_OSC_FREQUENCY, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay envParameterRow1 = {
"Env A",
{ "Attk", "lv ", "Deca", "lv " },
{
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay envParameterRow2 = {
"Env B",
{ "Sust", "lv ", "Rele", "lv " },
{
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay lfoEnvParameterRow = {
"Free Env",
{ "Attk", "Deca", "Sust", "Rele" },
{
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder }
}
};
const char* lofEnv2Loop [] = { "No ", "Sile", "Attk"};
struct ParameterRowDisplay lfoEnv2ParameterRow = {
"Free Env",
{ "Sile", "Attk", "Deca", "Loop" },
{
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 16, 1601, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ LFO_ENV2_NOLOOP, LFO_ENV2_LOOP_ATTACK, 3, DISPLAY_TYPE_STRINGS, lofEnv2Loop, nullNamesOrder, nullNamesOrder }
}
};
const char* matrixSourceNames [] = { "None", "lfo1", "lfo2", "lfo3", "env1", "env2", "seq1", "seq2",
"ModW", "PitB", "AftT", "Velo", "Not1", "p1 ", "p2 ", "p3 ", "p4 ", "Not2", "Brth" } ;
const unsigned char matrixSourceOrder[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 13, 14, 15, 16 };
const unsigned char matrixSourcePosition[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 13, 14};
const char* matrixDestNames [] = {
"None", "Gate", "IM1 ", "IM2 ", "IM3 ", "IM4 ", "IM* ",
"Mix1", "Pan1", "Mix2", "Pan2", "Mix3", "Pan3", "Mix4", "Pan4", "Mix*", "Pan*",
"o1Fq", "o2Fq", "o3Fq", "o4Fq", "o5Fq", "o6Fq", "o*Fq",
/*24*/ "Att1", "Att2", "Att3", "Att4", "Att5", "Att6",
/*30*/ "AttC", "RelC",
/*32*/ "mx01", "mx02", "mx03", "mx04",
/*36*/ "l1Fq", "l2Fq", "l3Fq", "e2si", "s1ga", "s2ga",
/*42*/ "Fltr",
/*43*/ "o*Fh", "DecC",
/*45*/ "AttM", "DecM", "RelM"
} ;
const unsigned char matrixTargetOrder[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 43, 24, 25, 26, 27, 28, 29, 30, 45, 44, 46, 31, 47, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 };
// Order { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44 };
const unsigned char matrixTargetPosition[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 30, 31, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 24, 33, 32, 34, 36 };
struct ParameterRowDisplay matrixParameterRow = {
"Matrix",
{ "Srce", "Mult", "Dest", " " },
{
{ MATRIX_SOURCE_NONE, MATRIX_SOURCE_MAX-1, MATRIX_SOURCE_MAX, DISPLAY_TYPE_STRINGS, matrixSourceNames, matrixSourceOrder, matrixSourcePosition},
{ -10, 10, 2001, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
// We removed 8 destination target in firmware 2.0
{ DESTINATION_NONE, DESTINATION_MAX -1, DESTINATION_MAX, DISPLAY_TYPE_STRINGS, matrixDestNames, matrixTargetOrder, matrixTargetPosition},
{ 0, 0, 0, DISPLAY_TYPE_NONE, nullNames, nullNamesOrder, nullNamesOrder }
}
};
const char* lfoOscMidiClock[] = { "M/16", "MC/8", "MC/4", "MC/2", "MClk", "MC*2", "MC*3", "MC*4", "MC*8"};
const char* lfoShapeNames [] = { "Sin ", "Saw ", "Tria","Squa", "Rand" } ;
struct ParameterRowDisplay lfoParameterRow = {
"LFO",
{ "Shap", "Freq", "Bias", "KSyn" },
{
{ LFO_SIN, LFO_TYPE_MAX-1, LFO_TYPE_MAX, DISPLAY_TYPE_STRINGS, lfoShapeNames, nullNamesOrder, nullNamesOrder},
{ 0, LFO_FREQ_MAX + .9f, LFO_FREQ_MAX_TIMES_10 +1 +9, DISPLAY_TYPE_FLOAT_LFO_FREQUENCY, nullNames, nullNamesOrder, nullNamesOrder },
{ -1, 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder },
{ -0.01f, 16.0f, 1602, DISPLAY_TYPE_LFO_KSYN, nullNames, nullNamesOrder, nullNamesOrder }
}
};
const char* lfoSeqMidiClock[] = { "MC/4", "MC/2", "MC ", "MC*2", "MC*4" };
struct ParameterRowDisplay lfoStepParameterRow = {
"Step Seq",
{ "Bpm ", "Gate", " ", " " },
{
{ 10 ,245, 236, DISPLAY_TYPE_STEP_SEQ_BPM, nullNames, nullNamesOrder, nullNamesOrder},
{ 0 , 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ 0, 0, 0, DISPLAY_TYPE_STEP_SEQ1, nullNames, nullNamesOrder, nullNamesOrder },
{ 0, 0, 0, DISPLAY_TYPE_STEP_SEQ2, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay performanceParameterRow = {
" -Performance-",
{ " p1 ", " p2 ", " p3 ", " p4 " },
{
{ -1 , 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ -1 , 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ -1 , 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ -1 , 1, 201, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder}
}
};
struct ParameterRowDisplay lfoPhaseParameterRow = {
"LFO Phase",
{ "Lfo1", "Lfo2", "Lfo3", " " },
{
{ 0 , 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ 0 , 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ 0 , 1, 101, DISPLAY_TYPE_FLOAT, nullNames, nullNamesOrder, nullNamesOrder},
{ 0, 0, 0, DISPLAY_TYPE_NONE, nullNames, nullNamesOrder, nullNamesOrder }
}
};
const char* midiNoteCurves[] = { "Flat", "+Lin", "+Ln8", "+Exp", "-Lin", "-Ln8", "-Exp" };
struct ParameterRowDisplay midiNote1ParameterRow = {
"Note1 Midi Scaling",
{ "Befo", "Brk ", "Afte", " " },
{
{ MIDI_NOTE_CURVE_FLAT, MIDI_NOTE_CURVE_MAX-1, MIDI_NOTE_CURVE_MAX, DISPLAY_TYPE_STRINGS, midiNoteCurves, nullNamesOrder, nullNamesOrder},
{ 0 , 127, 128, DISPLAY_TYPE_INT, nullNames, nullNamesOrder, nullNamesOrder},
{ MIDI_NOTE_CURVE_FLAT, MIDI_NOTE_CURVE_MAX-1, MIDI_NOTE_CURVE_MAX, DISPLAY_TYPE_STRINGS, midiNoteCurves, nullNamesOrder, nullNamesOrder},
{ 0, 0, 0, DISPLAY_TYPE_NONE, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct ParameterRowDisplay midiNote2ParameterRow = {
"Note2 Midi Scaling",
{ "Befo", "Brk ", "Afte", " " },
{
{ MIDI_NOTE_CURVE_FLAT, MIDI_NOTE_CURVE_MAX-1, MIDI_NOTE_CURVE_MAX, DISPLAY_TYPE_STRINGS, midiNoteCurves, nullNamesOrder, nullNamesOrder},
{ 0 , 127, 128, DISPLAY_TYPE_INT, nullNames, nullNamesOrder, nullNamesOrder},
{ MIDI_NOTE_CURVE_FLAT, MIDI_NOTE_CURVE_MAX-1, MIDI_NOTE_CURVE_MAX, DISPLAY_TYPE_STRINGS, midiNoteCurves, nullNamesOrder, nullNamesOrder},
{ 0, 0, 0, DISPLAY_TYPE_NONE, nullNames, nullNamesOrder, nullNamesOrder }
}
};
struct AllParameterRowsDisplay allParameterRows = {
{
&engine1ParameterRow,
&engineIM1ParameterRow,
&engineIM2ParameterRow,
&engineIM3ParameterRow,
&engineMix1ParameterRow,
&engineMix2ParameterRow,
&engineMix3ParameterRow,
&engineArp1ParameterRow,
&engineArp2ParameterRow,
&engineArpPatternRow,
&effectParameterRow,
&oscParameterRow,
&oscParameterRow,
&oscParameterRow,
&oscParameterRow,
&oscParameterRow,
&oscParameterRow,
&envParameterRow1,
&envParameterRow2,
&envParameterRow1,
&envParameterRow2,
&envParameterRow1,
&envParameterRow2,
&envParameterRow1,
&envParameterRow2,
&envParameterRow1,
&envParameterRow2,
&envParameterRow1,
&envParameterRow2,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&matrixParameterRow,
&performanceParameterRow,
&lfoParameterRow,
&lfoParameterRow,
&lfoParameterRow,
&lfoPhaseParameterRow,
&lfoEnvParameterRow,
&lfoEnv2ParameterRow,
&lfoStepParameterRow,
&lfoStepParameterRow,
&midiNote1ParameterRow,
&midiNote2ParameterRow
}
};
SynthState::SynthState() {
engineRow = ROW_ENGINE;
// operator works for both osc and env
oscillatorRow = 0;
matrixRow = ROW_MATRIX1;
lfoRow = ROW_LFOOSC1;
operatorNumber = 0;
operatorView = 0;
// First default preset
fullState.synthMode = SYNTH_MODE_EDIT;
fullState.preenFMBankNumber = 0;
fullState.preenFMPresetNumber = 0;
fullState.preenFMComboNumber = 0;
fullState.preenFMComboPresetNumber = 0;
fullState.dx7BankNumber = 0;
fullState.dx7PresetNumber = 0;
fullState.scalaScaleConfig.scalaFile = NULL;
fullState.scalaScaleConfig.scalaEnabled = false;
fullState.scalaScaleConfig.keyboardMapping = true;
// https://en.wikipedia.org/wiki/C_(musical_note)
// Frequency of note 60 C4 :
fullState.scalaScaleConfig.scalaFreq = 261.6f;
fullState.loadWhat = 0;
fullState.saveWhat = 0;
fullState.toolsWhat = 0;
fullState.scalaWhat = 0;
fullState.midiConfigValue[MIDICONFIG_USB] = 2;
fullState.midiConfigValue[MIDICONFIG_GLOBAL] = 0;
fullState.midiConfigValue[MIDICONFIG_CHANNEL1] = 1;
fullState.midiConfigValue[MIDICONFIG_CHANNEL2] = 2;
fullState.midiConfigValue[MIDICONFIG_CHANNEL3] = 3;
fullState.midiConfigValue[MIDICONFIG_CHANNEL4] = 4;
fullState.midiConfigValue[MIDICONFIG_THROUGH] = 0;
fullState.midiConfigValue[MIDICONFIG_RECEIVES] = 3;
fullState.midiConfigValue[MIDICONFIG_SENDS] = 1;
fullState.midiConfigValue[MIDICONFIG_PROGRAM_CHANGE] = 1;
fullState.midiConfigValue[MIDICONFIG_BOOT_START] = 0;
fullState.midiConfigValue[MIDICONFIG_TEST_NOTE] = 60;
fullState.midiConfigValue[MIDICONFIG_TEST_VELOCITY] = 120;
fullState.midiConfigValue[MIDICONFIG_ENCODER] = 0;
fullState.midiConfigValue[MIDICONFIG_OP_OPTION] = 0;
fullState.midiConfigValue[MIDICONFIG_LED_CLOCK] = 1;
fullState.midiConfigValue[MIDICONFIG_ARPEGGIATOR_IN_PRESET] = 0;
fullState.midiConfigValue[MIDICONFIG_OLED_SAVER] = 0;
fullState.midiConfigValue[MIDICONFIG_UNLINKED_EDITING] = 0;
fullState.midiConfigValue[MIDICONFIG_BOOT_SOUND] = 0;
fullState.firstMenu = 0;
// Init randomizer values to 1
fullState.randomizer.Oper = 1;
fullState.randomizer.EnvT = 1;
fullState.randomizer.IM = 1;
fullState.randomizer.Modl = 1;
for (int k=0; k<12; k++) {
fullState.name[k] = 0;
}
// edit with timbre 0
currentTimbre = 0;
currentRow = 0;
if ( fullState.midiConfigValue[MIDICONFIG_UNLINKED_EDITING] ) {
for (int t=0; t < NUMBER_OF_TIMBRES; ++t)
lastRowForTimbre[t] = 0;
} else {
for (int t=0; t < NUMBER_OF_TIMBRES; ++t)
lastRowForTimbre[t] = -1;
}
stepSelect[0] = 0;
stepSelect[1] = 0;
patternSelect = 0;
isPlayingNote = false;
for (int row= 0; row <NUMBER_OF_ROWS; row++) {
for (int param=0; param<NUMBER_OF_ENCODERS; param++) {
struct ParameterDisplay* paramDisplay = &(allParameterRows.row[row]->params[param]);
if (paramDisplay->numberOfValues > 1.0) {
paramDisplay->incValue = ((paramDisplay->maxValue - paramDisplay->minValue) / (paramDisplay->numberOfValues - 1.0f));
} else {
paramDisplay->incValue = 0.0f;
}
}
}
}
void SynthState::encoderTurnedForStepSequencer(int row, int encoder, int ticks) {
int whichStepSeq = row - ROW_LFOSEQ1;
StepSequencerSteps * seqSteps = &((StepSequencerSteps * )(&params->lfoSteps1))[whichStepSeq];
if (encoder == 2) {
int oldPos = stepSelect[whichStepSeq];
stepSelect[whichStepSeq] += (ticks>0? 1 : -1);
if (stepSelect[whichStepSeq]>15) {
stepSelect[whichStepSeq] = 0;
} else if (stepSelect[whichStepSeq]<0) {
stepSelect[whichStepSeq] = 15;
}
propagateNewParamValue(currentTimbre, row, encoder, (ParameterDisplay*)NULL, oldPos, stepSelect[whichStepSeq]);
} else if (encoder == 3) {
char *step = &seqSteps->steps[stepSelect[whichStepSeq]];
int oldValue = (int)(*step);
(*step) += ticks;
if ((*step)>15) {
(*step) = 15;
} else if ((*step)<0) {
(*step) = 0;
}
propagateNewParamValue(currentTimbre, row, encoder, (ParameterDisplay*)NULL, oldValue, (int)(*step));
}
}
void SynthState::encoderTurnedForArpPattern(int row, int encoder, int ticks) {
if (encoder == 0) {
// Encoder 0: move cursor
int oldPos = patternSelect;
patternSelect += (ticks > 0? 1 : -1);
if (patternSelect>15) {
patternSelect = 0;
} else if (patternSelect<0) {
patternSelect = 15;
}
propagateNewParamValue(currentTimbre, row, encoder, (ParameterDisplay*)NULL, oldPos, patternSelect);
} else {
// Change value(s)
arp_pattern_t pattern = params->engineArpUserPatterns.patterns[ (int)params->engineArp2.pattern - ARPEGGIATOR_PRESET_PATTERN_COUNT ];
const uint16_t oldMask = ARP_PATTERN_GETMASK( pattern );
uint16_t newMask = oldMask;
uint16_t bitsToModify;
switch ( encoder ) {
case 3: bitsToModify = 0x1 << patternSelect; break; // modify single note
case 2: bitsToModify = 0x1111 << (patternSelect & 3); break; // modify all
case 1: bitsToModify = 0xf << ((patternSelect>>2)<<2); break; // modify entire bar
}
if (ticks > 0) {
newMask |= bitsToModify;
} else {
newMask &= ~bitsToModify;
}
if ( oldMask != newMask ) {
ARP_PATTERN_SETMASK( pattern, newMask );
params->engineArpUserPatterns.patterns[ (int)params->engineArp2.pattern - ARPEGGIATOR_PRESET_PATTERN_COUNT ] = pattern;
propagateNewParamValue(currentTimbre, row, encoder, (ParameterDisplay*)NULL, oldMask, newMask );
}
}
}
void SynthState::twoButtonsPressed(int button1, int button2) {
int oldCurrentRow = currentRow;
switch (button1) {
case BUTTON_BACK:
switch (button2) {
case BUTTON_SYNTH:
propagateNoteOn(12);
break;
case BUTTON_OSC:
propagateNoteOn(8);
break;
case BUTTON_ENV:
propagateNoteOn(0);
break;
case BUTTON_MATRIX:
propagateNoteOn(-8);
break;
case BUTTON_LFO:
propagateNoteOn(-12);
break;
case BUTTON_MENUSELECT:
propagateNoteOff();
propagateBeforeNewParamsLoad(currentTimbre);
propagateAfterNewComboLoad();
break;
}
break;
}
if ((fullState.synthMode != SYNTH_MODE_EDIT)) {
return;
}
switch (button1) {
case BUTTON_SYNTH:
switch (button2) {
case BUTTON_MENUSELECT:
if (fullState.synthMode == SYNTH_MODE_EDIT) {
propagateShowAlgo();
}
break;
case BUTTON_BACK:
if (fullState.synthMode == SYNTH_MODE_EDIT) {
propagateShowIMInformation();
}
break;
case BUTTON_OSC:
currentRow = ROW_ENGINE;
break;
case BUTTON_ENV:
currentRow = ROW_MODULATION1;
break;
case BUTTON_MATRIX:
currentRow = ROW_ARPEGGIATOR1;
break;
case BUTTON_LFO:
currentRow = ROW_EFFECT;
break;
}
break;
case BUTTON_MATRIX:
switch (button2) {
case BUTTON_SYNTH:
currentRow = ROW_MATRIX1;
break;
case BUTTON_OSC:
changeSynthModeRow(BUTTON_MATRIX , -3);
break;
case BUTTON_ENV:
changeSynthModeRow(BUTTON_MATRIX , -1);
onUserChangedRow();
break;
case BUTTON_LFO:
currentRow = ROW_MATRIX6;
break;
}
break;
case BUTTON_LFO:
switch (button2) {
case BUTTON_SYNTH:
currentRow = ROW_LFOOSC1;
break;
case BUTTON_OSC:
currentRow = ROW_LFOENV1;
break;
case BUTTON_ENV:
currentRow = ROW_LFOENV2;
break;
#ifndef DEBUG
case BUTTON_MATRIX:
currentRow = ROW_LFOSEQ1;
break;
#endif
case BUTTON_MENUSELECT:
currentRow = ROW_PERFORMANCE1;
break;
}
break;
case BUTTON_MENUSELECT:
switch (button2) {
case BUTTON_LFO:
currentRow = ROW_PERFORMANCE1;
break;
}
break;
}
#ifdef DEBUG
if (button1 == BUTTON_LFO) {
if (button2 == BUTTON_MATRIX) {
synth.debugVoice();
}
if (button2 == BUTTON_BACK) {
synth.showCycles();
}
if (button2 == BUTTON_MENUSELECT) {
storage->getPatchBank()->testMemoryPreset();
}
}
#endif
if (oldCurrentRow != currentRow) {
propagateNewCurrentRow(currentRow);
onUserChangedRow();
}
}
void SynthState::encoderTurnedWhileButtonPressed(int encoder, int ticks, int button) {
if (unlikely(fullState.synthMode == SYNTH_MODE_MENU)) {
return;
}
// SYNTH_MODE_EDIT
int oldCurrentRow = currentRow;
switch (button) {
case BUTTON_SYNTH:
case BUTTON_MATRIX:
case BUTTON_LFO:
case BUTTON_OSC:
changeSynthModeRow(button , ticks>0 ? 1 : -1);
onUserChangedRow();
break;
case BUTTON_ENV:
{
if (currentRow >= ROW_ENV1a && currentRow <= ROW_ENV6b) {
int firstRow = ROW_ENV1a;
int envBBit = ROW_ENV1b & 0x1;
if ((currentRow & 0x1) == envBBit) {
firstRow = ROW_ENV1b;
}
int currentAlgo = (int)params->engine1.algo;
int currentOpType = algoOpInformation[currentAlgo][(currentRow - ROW_ENV1a) >> 1];
for (int op = 0; op < NUMBER_OF_OPERATORS; op++) {
currentRow = firstRow + op*2;
if (currentOpType == algoOpInformation[currentAlgo][op]) {
// Same operator type : carrier or modulation
encoderTurned(encoder, ticks);
}
}
// Put back current row
currentRow = oldCurrentRow;
} else if (currentRow >= ROW_MODULATION1 && currentRow <= ROW_MODULATION3) {
int imMax = algoInformation[(int)params->engine1.algo].im;
// Im or v ?
if ((encoder & 0x1) == 0) {
for (int m = 0; m < imMax; m++) {
currentRow = ROW_MODULATION1 + (m >> 1);
encoderTurned(ENCODER_ENGINE_IM1 + (m & 0x1) * 2, ticks);
}
} else {
for (int m = 0; m < imMax; m++) {
currentRow = ROW_MODULATION1 + (m >> 1);
encoderTurned(ENCODER_ENGINE_IM1_VELOCITY + (m & 0x1) * 2, ticks);
}
}
currentRow = oldCurrentRow;
} else if (currentRow >= ROW_OSC_MIX1 && currentRow <= ROW_OSC_MIX3) {
int mixMax = algoInformation[(int)params->engine1.algo].mix;
// Mix or Pan ?
if ((encoder & 0x1) == 0) {
for (int m = 0; m < mixMax; m++) {
currentRow = ROW_OSC_MIX1 + (m >> 1);
encoderTurned(ENCODER_ENGINE_MIX1 + (m & 0x1) * 2, ticks);
}
} else {
for (int m = 0; m < mixMax; m++) {
currentRow = ROW_OSC_MIX1 + (m >> 1);
encoderTurned(ENCODER_ENGINE_PAN1 + (m & 0x1) * 2, ticks);
}
}
currentRow = oldCurrentRow;
}
break;
}
case BUTTON_BACK:
encoderTurned(encoder, ticks * 10);
break;
case BUTTON_ENCODER:
case BUTTON_MENUSELECT:
{
if (currentRow == ROW_ENGINE_FIRST) {
// Nothing if first engine row
return;
}
struct ParameterDisplay* param = &(allParameterRows.row[currentRow]->params[encoder]);
const struct OneSynthParams* defaultParams = &defaultPreset;
int num = encoder + currentRow * NUMBER_OF_ENCODERS;
float &value = ((float*)params)[num];
float oldValue = value;
float newValue = ((float*)defaultParams)[num];
value = newValue;
propagateNewParamValue(currentTimbre, currentRow, encoder, param, oldValue, newValue);
break;
}
}
if (oldCurrentRow != currentRow) {
propagateNewCurrentRow(currentRow);
}
}
bool SynthState::newRandomizerValue(int encoder, int ticks) {
bool changed = false;
char oldValue = 0;
char newValue = 0;
switch (encoder) {
case 0:
oldValue = fullState.randomizer.Oper;
fullState.randomizer.Oper += (ticks > 0 ? 1 : -1);
fullState.randomizer.Oper = fullState.randomizer.Oper > 3 ? 3 : fullState.randomizer.Oper;
fullState.randomizer.Oper = fullState.randomizer.Oper < 0 ? 0 : fullState.randomizer.Oper;
newValue = fullState.randomizer.Oper;
break;
case 1:
oldValue = fullState.randomizer.EnvT;
fullState.randomizer.EnvT += (ticks > 0 ? 1 : -1);
fullState.randomizer.EnvT = fullState.randomizer.EnvT > 3 ? 3 : fullState.randomizer.EnvT;
fullState.randomizer.EnvT = fullState.randomizer.EnvT < 0 ? 0 : fullState.randomizer.EnvT;
newValue = fullState.randomizer.EnvT;
break;
case 2:
oldValue = fullState.randomizer.IM;
fullState.randomizer.IM += (ticks > 0 ? 1 : -1);
fullState.randomizer.IM = fullState.randomizer.IM > 3 ? 3 : fullState.randomizer.IM;
fullState.randomizer.IM = fullState.randomizer.IM < 0 ? 0 : fullState.randomizer.IM;
newValue = fullState.randomizer.IM;
break;
case 3:
oldValue = fullState.randomizer.Modl;
fullState.randomizer.Modl += (ticks > 0 ? 1 : -1);
fullState.randomizer.Modl = fullState.randomizer.Modl > 3 ? 3 : fullState.randomizer.Modl;
fullState.randomizer.Modl = fullState.randomizer.Modl < 0 ? 0 : fullState.randomizer.Modl;
newValue = fullState.randomizer.Modl;
break;
}
return newValue != oldValue;
}
void SynthState::encoderTurned(int encoder, int ticks) {
if (fullState.synthMode == SYNTH_MODE_EDIT) {
// Step sequencer special case
if (unlikely(currentRow == ROW_LFOSEQ1 || currentRow == ROW_LFOSEQ2)) {
if (encoder >= 2) {
encoderTurnedForStepSequencer(currentRow, encoder, ticks);
return;
}
};
if (unlikely(currentRow == ROW_ARPEGGIATOR3)) {
encoderTurnedForArpPattern(currentRow, encoder, ticks);
return;
}
int num = encoder + currentRow * NUMBER_OF_ENCODERS;
struct ParameterDisplay* param = &(allParameterRows.row[currentRow]->params[encoder]);
float newValue;
float oldValue;
if (param->displayType == DISPLAY_TYPE_STRINGS) {
// Do not use encoder acceleration
ticks = ticks > 0 ? 1 : -1;
}
if (param->valueNameOrder == NULL) {
// Not string parameters
// floating point test to be sure numberOfValues is different from 1.
// for voices when number of voices forced to 0
if (param->numberOfValues < 1.5) {
return;
}
float &value = ((float*)params)[num];
oldValue = value;
float inc = param->incValue;
// Slow down LFO frequency
if (unlikely(param->displayType == DISPLAY_TYPE_FLOAT_LFO_FREQUENCY)) {
if (oldValue < 1.0f) {
inc = inc * .1f;
}
}
int tickIndex = (value - param->minValue) / inc + .0005f + ticks;
newValue = param->minValue + tickIndex * inc;
propagateNewParamCheck(encoder, oldValue, &newValue);
if (newValue > param->maxValue) {
newValue = param->maxValue;
}
if (newValue < param->minValue) {
newValue = param->minValue;
}
value = newValue;
} else {
float *value = &((float*)params)[num];
int index;
newValue = oldValue = (*value);
// Must use newValue (int) so that the minValue comparaison works
// Is there any other order than the default one
int pos = param->valueNameOrderReversed[(int)(*value)];
if (ticks>0 && pos < param->maxValue) {
newValue = param->valueNameOrder[pos+1];
}
if (ticks<0 && pos>param->minValue) {
newValue = param->valueNameOrder[pos-1];
}
(*value) = (float)newValue;
}
if (newValue != oldValue) {
propagateNewParamValue(currentTimbre, currentRow, encoder, param, oldValue, newValue);
}
} else if (fullState.synthMode == SYNTH_MODE_MENU) {
int oldMenuSelect = fullState.menuSelect;
if (unlikely(fullState.currentMenuItem->menuState == MENU_LOAD_RANDOMIZER)) {
if (newRandomizerValue(encoder, ticks)) {
// Only propagate if value really changed
propagateNewMenuSelect();
}
return;
}
switch (encoder) {
case 0:
if (unlikely(fullState.currentMenuItem->menuState == MENU_SCALA_FREQUENCY)) {
fullState.scalaScaleConfig.scalaFreq += .1f * (float)ticks;
propagateNewMenuSelect();
oldMenuSelect ++;
} else {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 1;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 1;
}
}
break;
case 1:
if (unlikely(fullState.currentMenuItem->menuState == MENU_SCALA_FREQUENCY)) {
fullState.scalaScaleConfig.scalaFreq += 1.0f * (float)ticks;
propagateNewMenuSelect();
fullState.menuSelect = (fullState.menuSelect == 1) ? 2 : 1;
} else if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = (fullState.name[fullState.menuSelect] + (ticks>0? 1: -1));
if (fullState.name[fullState.menuSelect]<28) {
fullState.name[fullState.menuSelect]=28;
}
if (fullState.name[fullState.menuSelect]> 53) {
fullState.name[fullState.menuSelect] = 53;
}
propagateNewMenuSelect();
} else if (fullState.currentMenuItem->maxValue >= 128) {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 5;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 5;
}
} else {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 1;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 1;
}
}
break;
case 2:
if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = (fullState.name[fullState.menuSelect] + (ticks>0? 1: -1));
if (fullState.name[fullState.menuSelect]<1) {
fullState.name[fullState.menuSelect]=1;
}
if (fullState.name[fullState.menuSelect]> 26) {
fullState.name[fullState.menuSelect] = 26;
}
propagateNewMenuSelect();
} else if (fullState.currentMenuItem->maxValue >= 128) {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 10;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 10;
}
} else {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 1;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 1;
}
}
break;
case 3:
if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = (fullState.name[fullState.menuSelect] + (ticks>0? 1: -1));
if (fullState.name[fullState.menuSelect]<0) {
fullState.name[fullState.menuSelect]=0;
}
if (fullState.name[fullState.menuSelect]>= getLength(allChars)) {
fullState.name[fullState.menuSelect]= getLength(allChars)-1;
}
propagateNewMenuSelect();
} else if (fullState.currentMenuItem->menuState == MENU_CONFIG_SETTINGS) {
fullState.midiConfigValue[fullState.menuSelect] = fullState.midiConfigValue[fullState.menuSelect] + (ticks>0? 1: -1);
if (fullState.midiConfigValue[fullState.menuSelect] >= midiConfig[fullState.menuSelect].maxValue) {
fullState.midiConfigValue[fullState.menuSelect] = midiConfig[fullState.menuSelect].maxValue - 1;
}
if (fullState.midiConfigValue[fullState.menuSelect] < 0 ) {
fullState.midiConfigValue[fullState.menuSelect] = 0;
}
propagateNewMenuSelect();
} else if (fullState.currentMenuItem->maxValue >= 128) {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 25;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 25;
}
} else {
if (ticks>0) {
fullState.menuSelect = fullState.menuSelect + 1;
} else if (ticks<0) {
fullState.menuSelect = fullState.menuSelect - 1;
}
}
break;
}
if (fullState.menuSelect> fullState.currentMenuItem->maxValue - 1) {
fullState.menuSelect = fullState.currentMenuItem->maxValue - 1;
}
if (fullState.menuSelect< 0) {
fullState.menuSelect = 0;
}
if (fullState.menuSelect != oldMenuSelect) {
switch (fullState.currentMenuItem->menuState) {
case MENU_LOAD_SELECT_DX7_BANK:
// Did we really change DX7 bank?
if (fullState.dx7Bank != storage->getDX7SysexFile()->getFile(fullState.menuSelect)) {
fullState.dx7Bank = storage->getDX7SysexFile()->getFile(fullState.menuSelect);
fullState.dx7PresetNumber = 0;
}
break;
case MENU_LOAD_SELECT_DX7_PRESET:
loadDx7Patch(currentTimbre, fullState.dx7Bank, fullState.menuSelect, params);
fullState.dx7PresetNumber = fullState.menuSelect;
break;
case MENU_LOAD_SELECT_BANK:
case MENU_SAVE_SELECT_BANK:
// Did we really change bank?
if (fullState.preenFMBank != storage->getPatchBank()->getFile(fullState.menuSelect)) {
fullState.preenFMBank = storage->getPatchBank()->getFile(fullState.menuSelect);
fullState.preenFMPresetNumber = 0;
}
break;
case MENU_RENAME_SELECT_BANK:
// Did we really change bank?
if (fullState.preenFMBank != storage->getPatchBank()->getFile(fullState.menuSelect)) {
fullState.preenFMBank = storage->getPatchBank()->getFile(fullState.menuSelect);
}
break;
case MENU_LOAD_SELECT_COMBO:
case MENU_SAVE_SELECT_COMBO:
// Did we really change bank?
if (fullState.preenFMCombo != storage->getComboBank()->getFile(fullState.menuSelect)) {
fullState.preenFMCombo = storage->getComboBank()->getFile(fullState.menuSelect);
fullState.preenFMComboPresetNumber = 0;
}
break;
case MENU_RENAME_SELECT_COMBO:
// Did we really change combo?
if (fullState.preenFMCombo != storage->getComboBank()->getFile(fullState.menuSelect)) {
fullState.preenFMCombo = storage->getComboBank()->getFile(fullState.menuSelect);
}
break;
case MENU_LOAD_SELECT_BANK_PRESET:
loadPreenFMPatch(currentTimbre, fullState.preenFMBank, fullState.menuSelect, params);
fullState.preenFMPresetNumber = fullState.menuSelect;
break;
case MENU_SAVE_SELECT_BANK_PRESET:
fullState.preenFMPresetNumber = fullState.menuSelect;
break;
case MENU_LOAD_SELECT_COMBO_PRESET:
case MENU_SAVE_SELECT_COMBO_PRESET:
fullState.preenFMComboPresetNumber = fullState.menuSelect;
break;
case MENU_SCALA_MAPPING:
fullState.scalaScaleConfig.keyboardMapping = (fullState.menuSelect == 0);
storage->getScalaFile()->applyScalaScale(&fullState.scalaScaleConfig);
break;
case MENU_SCALA_FREQUENCY:
storage->getScalaFile()->applyScalaScale(&fullState.scalaScaleConfig);
break;
case MENU_SCALA_FILENAME:
// Did we really change bank?
if (fullState.scalaScaleConfig.scalaFile != storage->getScalaFile()->getFile(fullState.menuSelect)) {
fullState.scalaScaleConfig.scalaFile = storage->getScalaFile()->getFile(fullState.menuSelect);
}
storage->getScalaFile()->loadScalaScale(&fullState.scalaScaleConfig);
break;
case MENU_SCALA_ENABLE:
fullState.scalaScaleConfig.scalaEnabled = (fullState.menuSelect > 0);
if (fullState.scalaScaleConfig.scalaEnabled) {
storage->getScalaFile()->loadScalaScale(&fullState.scalaScaleConfig);
} else {
storage->getScalaFile()->clearScalaScale();
}
break;
}
propagateNewMenuSelect();
}
}
}
void SynthState::setScalaEnable(bool enable) {
fullState.scalaScaleConfig.scalaEnabled = enable;
if (!enable) {
storage->getScalaFile()->clearScalaScale();
} else if (fullState.scalaScaleConfig.scalaFile->fileType != FILE_EMPTY) {
storage->getScalaFile()->loadScalaScale(&fullState.scalaScaleConfig);
}
}
void SynthState::setScalaScale(int scaleNumber) {
fullState.scalaScaleConfig.scalaFile = storage->getScalaFile()->getFile(scaleNumber);
if (fullState.scalaScaleConfig.scalaFile->fileType != FILE_EMPTY && fullState.scalaScaleConfig.scalaEnabled) {
storage->getScalaFile()->loadScalaScale(&fullState.scalaScaleConfig);
}
}
void SynthState::loadPreenFMPatch(int timbre, PFM2File const *bank, int patchNumber, struct OneSynthParams* params) {
storeTestNote();
propagateNoteOff();
propagateBeforeNewParamsLoad(timbre);
storage->getPatchBank()->loadPreenFMPatch(bank, patchNumber, params);
propagateAfterNewParamsLoad(timbre);
restoreTestNote();
}
void SynthState::loadDx7Patch(int timbre, PFM2File const *bank, int patchNumber, struct OneSynthParams* params) {
storeTestNote();
propagateNoteOff();
propagateBeforeNewParamsLoad(timbre);
hexter->loadHexterPatch(storage->getDX7SysexFile()->dx7LoadPatch(bank, patchNumber), params);
propagateAfterNewParamsLoad(timbre);
restoreTestNote();
}
void SynthState::loadPreenFMCombo(PFM2File const *bank, int patchNumber) {
propagateBeforeNewParamsLoad(currentTimbre);
storage->getComboBank()->loadPreenFMCombo(bank, patchNumber);
// Update and clean all timbres
this->currentTimbre = 0;
propagateNewTimbre(currentTimbre);
propagateAfterNewComboLoad();
}
void SynthState::loadPreenFMPatchFromMidi(int timbre, int bank, int bankLSB, int patchNumber, struct OneSynthParams* params) {
switch (bank) {
case 0:
{
PFM2File const *bank = storage->getPatchBank()->getFile(bankLSB);
if (bank->fileType != FILE_EMPTY) {
loadPreenFMPatch(timbre, bank, patchNumber, params);
}
}
break;
case 1:
{
PFM2File const *bank = storage->getComboBank()->getFile(bankLSB);
if (bank->fileType != FILE_EMPTY) {
loadPreenFMCombo(bank, patchNumber);
}
}
break;
case 2:
case 3:
case 4:
{
int dx7bank = bank - 2;
PFM2File const *bank = storage->getDX7SysexFile()->getFile(bankLSB + dx7bank * 128);
if (bank->fileType != FILE_EMPTY) {
loadDx7Patch(timbre, bank, patchNumber, params);
}
}
break;
}
}
void SynthState::resetDisplay() {
fullState.synthMode = SYNTH_MODE_EDIT;
currentRow = 0;
propagateNewSynthMode();
}
bool SynthState::isCurrentRowAvailable() const {
switch (currentRow) {
case ROW_OSC_MIX2:
return algoInformation[(int)params->engine1.algo].mix > 2;
case ROW_OSC_MIX3:
return algoInformation[(int)params->engine1.algo].mix > 4;
case ROW_MODULATION1:
return algoInformation[(int)params->engine1.algo].im != 0;
case ROW_MODULATION2:
return algoInformation[(int)params->engine1.algo].im > 2;
case ROW_MODULATION3:
return algoInformation[(int)params->engine1.algo].im > 4;
case ROW_ARPEGGIATOR2:
return params->engineArp1.clock > 0;
case ROW_ARPEGGIATOR3:
return params->engineArp1.clock > 0 && params->engineArp2.pattern >= ARPEGGIATOR_PRESET_PATTERN_COUNT;
}
return true;
}
int SynthState::getRowFromOperator() {
switch (operatorView) {
case 0:
return ROW_OSC_FIRST + operatorNumber;
break;
case 1:
return ROW_ENV_FIRST + operatorNumber * 2;
break;
case 2:
return ROW_ENV_FIRST + operatorNumber * 2 + 1;
break;
}
}
void SynthState::changeSynthModeRow(int button, int step) {
unsigned char lastBecauseOfAlgo;
switch (button) {
case BUTTON_SYNTH:
if (currentRow<ROW_ENGINE_FIRST || currentRow>ROW_ENGINE_LAST) {
currentRow = engineRow;
} else {
do {
currentRow += step;
if (currentRow>ROW_ENGINE_LAST) {
currentRow = ROW_ENGINE_FIRST;
} else if (currentRow<ROW_ENGINE_FIRST) {
currentRow = ROW_ENGINE_LAST;
}
} while (!isCurrentRowAvailable());
}
engineRow = currentRow;
break;
case BUTTON_OSC:
if (this->fullState.midiConfigValue[MIDICONFIG_OP_OPTION] == 0) {
// New UI - operator number
lastBecauseOfAlgo = algoInformation[(int)params->engine1.algo].osc - 1;
if (currentRow < ROW_OSC_FIRST || currentRow > ROW_ENV_LAST) {
// Nothing to do.;.
} else {
operatorNumber += step;
}
if (operatorNumber > lastBecauseOfAlgo) {
operatorNumber = 0;
} else if (operatorNumber < 0) {
operatorNumber = lastBecauseOfAlgo;
}
currentRow = getRowFromOperator();
} else {
// Old UI
lastBecauseOfAlgo = ROW_OSC_FIRST + algoInformation[(int)params->engine1.algo].osc - 1;
if (currentRow<ROW_OSC_FIRST || currentRow>lastBecauseOfAlgo) {
currentRow = ROW_OSC_FIRST + oscillatorRow;
} else {
currentRow += step;
envelopeRow = (currentRow - ROW_OSC_FIRST)*2;
}
if (currentRow>lastBecauseOfAlgo) {
currentRow = ROW_OSC_FIRST;
} else if (currentRow<ROW_OSC_FIRST) {
currentRow = lastBecauseOfAlgo;
}
oscillatorRow = currentRow - ROW_OSC_FIRST;
}
break;
case BUTTON_ENV:
if (this->fullState.midiConfigValue[MIDICONFIG_OP_OPTION] == 0) {
// New UI - op / env1 / env2 for the current operator
// New UI - operator number
lastBecauseOfAlgo = algoInformation[(int)params->engine1.algo].osc - 1;
if (currentRow < ROW_OSC_FIRST || currentRow > ROW_ENV_LAST) {
// Nothing to do.;.
} else {
operatorView += step;
if (operatorView > 2) {
operatorView = 0;
} else if (operatorView < 0) {
operatorView = 2;
}
}
if (operatorNumber > lastBecauseOfAlgo) {
operatorNumber = 0;
} else if (operatorNumber < 0) {
operatorNumber = lastBecauseOfAlgo;
}
currentRow = getRowFromOperator();
} else {
lastBecauseOfAlgo = ROW_ENV_FIRST - 1 + algoInformation[(int)params->engine1.algo].osc * 2;
if (currentRow<ROW_ENV_FIRST || currentRow>lastBecauseOfAlgo) {
currentRow = ROW_ENV_FIRST + envelopeRow;
} else {
currentRow += step;
oscillatorRow = (currentRow - ROW_ENV_FIRST) >> 1;
}
if (currentRow>lastBecauseOfAlgo) {
currentRow = ROW_ENV_FIRST;
} else if (currentRow<ROW_ENV_FIRST) {
currentRow = lastBecauseOfAlgo;
}
envelopeRow = currentRow - ROW_ENV_FIRST;
}
break;
case BUTTON_MATRIX:
if (currentRow<ROW_MATRIX_FIRST || currentRow>ROW_MATRIX_LAST) {
currentRow = matrixRow;
} else {
currentRow += step;
if (currentRow>ROW_MATRIX_LAST) {
currentRow = ROW_MATRIX_FIRST;
} else if (currentRow<ROW_MATRIX_FIRST) {
currentRow = ROW_MATRIX_LAST;
}
}
matrixRow = currentRow;
break;
case BUTTON_LFO:
if (currentRow<ROW_LFO_FIRST || currentRow>ROW_LFO_LAST) {
currentRow = lfoRow;
} else {
currentRow += step;
if (currentRow>ROW_LFO_LAST) {
currentRow = ROW_LFO_FIRST;
} else if (currentRow<ROW_LFO_FIRST) {
currentRow = ROW_LFO_LAST;
}
}
lfoRow = currentRow;
break;
}
}
bool SynthState::isEnterNameState(int currentItem) {
return currentItem == MENU_SAVE_ENTER_PRESET_NAME
|| currentItem == MENU_RENAME_PATCH
|| currentItem == MENU_SAVE_ENTER_COMBO_NAME
|| currentItem == MENU_RENAME_BANK
|| currentItem == MENU_RENAME_COMBO
|| currentItem == MENU_CREATE_BANK
|| currentItem == MENU_CREATE_COMBO;
}
void SynthState::buttonPressed(int button) {
SynthEditMode oldSynthMode = fullState.synthMode;
int oldCurrentRow = currentRow;
// int oldMenuSelect = fullState.menuSelect;
MenuState oldMenuState = fullState.currentMenuItem->menuState;
if (fullState.synthMode == SYNTH_MODE_EDIT) {
switch (button) {
case BUTTON_SYNTH:
case BUTTON_OSC:
case BUTTON_ENV:
case BUTTON_MATRIX:
case BUTTON_LFO:
changeSynthModeRow(button , 1);
onUserChangedRow();
break;
case BUTTON_ENCODER:
currentRow = ROW_PERFORMANCE1;
break;
case BUTTON_MENUSELECT:
fullState.synthMode = SYNTH_MODE_MENU;
fullState.menuSelect = fullState.firstMenu;
// allow undo event after trying some patches
copySynthParams((char*)params, (char*)&backupParams);
fullState.currentMenuItem = MenuItemUtil::getMenuItem(MAIN_MENU);
break;
case BUTTON_BACK:
{
setLastRowForTimbre( currentTimbre, currentRow ); // remember row for when we return to this timbre
currentTimbre++;
currentTimbre &= (NUMBER_OF_TIMBRES-1);
propagateNewTimbre(currentTimbre);
int last = getLastRowForTimbre( currentTimbre );
if ( last >= 0 )
currentRow = last;
if ( !isCurrentRowAvailable() && currentRow >= ROW_ENGINE_FIRST && currentRow <= ROW_ENGINE_LAST) {
changeSynthModeRow( BUTTON_SYNTH, -1 );
}
}
break;
}
} else {
// Any button when done is display makes the synth go back to edit mode.
// MENU MODE
// Special treatment for Randomizer
if (unlikely(fullState.currentMenuItem->menuState == MENU_LOAD_RANDOMIZER)) {
if (button != BUTTON_MENUSELECT && button != BUTTON_BACK) {
storeTestNote();
propagateNoteOff();
propagateBeforeNewParamsLoad(currentTimbre);
randomizePreset();
propagateAfterNewParamsLoad(currentTimbre);
restoreTestNote();
return;
}
}
switch (button) {
case BUTTON_MENUSELECT:
fullState.currentMenuItem = afterButtonPressed();
break;
case BUTTON_BACK:
{
enum MenuState oldState = fullState.currentMenuItem->menuState;
fullState.currentMenuItem = menuBack();
propagateMenuBack(oldState);
break;
}
case BUTTON_LFO:
{
if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = 0;
propagateNewMenuSelect();
} else {
propagateNoteOn(-12);
}
break;
}
case BUTTON_MATRIX:
{
if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = 27;
propagateNewMenuSelect();
} else {
propagateNoteOn(-8);
}
break;
}
case BUTTON_ENV:
{
if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = 55;
propagateNewMenuSelect();
} else {
propagateNoteOn(0);
}
break;
}
case BUTTON_OSC:
{
if (isEnterNameState(fullState.currentMenuItem->menuState)) {
fullState.name[fullState.menuSelect] = 66;
propagateNewMenuSelect();
} else {
propagateNoteOn(8);
}
break;
}
case BUTTON_SYNTH:
{
propagateNoteOn(12);
}
}
}
if (oldSynthMode != fullState.synthMode) {
propagateNewSynthMode();
return;
}
if (oldCurrentRow != currentRow) {
propagateNewCurrentRow(currentRow);
}
if (oldMenuState != fullState.currentMenuItem->menuState) {
propagateNewMenuState();
}
}
const MenuItem* SynthState::afterButtonPressed() {
const MenuItem* rMenuItem = 0;
int length, k;
const MenuItem *cmi;
if (fullState.currentMenuItem->hasSubMenu ) {
rMenuItem = MenuItemUtil::getMenuItem(fullState.currentMenuItem->subMenu[fullState.menuSelect]);
} else {
rMenuItem = MenuItemUtil::getMenuItem(fullState.currentMenuItem->subMenu[0]);
}
// ------------------------
// Previous state switch
switch (fullState.currentMenuItem->menuState) {
case MAIN_MENU:
fullState.firstMenu = fullState.menuSelect;
break;
case MENU_SAVE_SELECT_BANK:
if (fullState.preenFMBank->fileType != FILE_OK) {
return fullState.currentMenuItem;
}
fullState.preenFMBankNumber = fullState.menuSelect;
break;
case MENU_RENAME_SELECT_BANK:
case MENU_LOAD_SELECT_BANK:
if (fullState.preenFMBank->fileType == FILE_EMPTY) {
return fullState.currentMenuItem;
}
fullState.preenFMBankNumber = fullState.menuSelect;
break;
case MENU_SAVE_SELECT_COMBO:
if (fullState.preenFMCombo->fileType != FILE_OK) {
return fullState.currentMenuItem;
}
fullState.preenFMComboNumber = fullState.menuSelect;
break;
case MENU_RENAME_SELECT_COMBO:
case MENU_LOAD_SELECT_COMBO:
if (fullState.preenFMCombo->fileType == FILE_EMPTY) {
return fullState.currentMenuItem;
}
fullState.preenFMComboNumber = fullState.menuSelect;
break;
case MENU_LOAD_SELECT_DX7_BANK:
if (fullState.dx7Bank->fileType != FILE_OK) {
return fullState.currentMenuItem;
}
fullState.dx7BankNumber = fullState.menuSelect;
break;
case MENU_LOAD_SELECT_COMBO_PRESET:
loadPreenFMCombo(fullState.preenFMCombo, fullState.menuSelect);
copySynthParams((char*)params, (char*)&backupParams);
break;
case MENU_LOAD_SELECT_DX7_PRESET:
case MENU_LOAD_SELECT_BANK_PRESET:
case MENU_LOAD_RANDOMIZER:
copySynthParams((char*)params, (char*)&backupParams);
break;
case MENU_SAVE_SELECT_BANK_PRESET:
for (int k=0; k<12; k++) {
fullState.name[k] = 0;
}
for (int k=0; k<12 && params->presetName[k] != 0; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (params->presetName[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
break;
case MENU_SAVE_SELECT_COMBO_PRESET:
{
// const char* comboName = storage->readComboName(fullState.menuSelect);
const char* comboName = storage->getComboBank()->loadPreenFMComboName(fullState.preenFMCombo, fullState.preenFMComboPresetNumber);
for (int k=0; k<12 && comboName[k] != 0; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (comboName[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
break;
}
case MENU_RENAME_PATCH:
for (length=12; fullState.name[length-1] == 0; length--);
for (int k=0; k<length; k++) {
params->presetName[k] = allChars[(int)fullState.name[k]];
}
params->presetName[length] = '\0';
break;
case MENU_SAVE_ENTER_PRESET_NAME:
for (length=12; fullState.name[length-1] == 0; length--);
for (int k=0; k<length; k++) {
params->presetName[k] = allChars[(int)fullState.name[k]];
}
params->presetName[length] = '\0';
storage->getPatchBank()->savePreenFMPatch(fullState.preenFMBank, fullState.preenFMPresetNumber, params);
break;
case MENU_SAVE_ENTER_COMBO_NAME:
for (length=12; fullState.name[length-1] == 0; length--);
char comboName[12];
for (int k=0; k<length; k++) {
comboName[k] = allChars[(int)fullState.name[k]];
}
comboName[length] = '\0';
storage->getComboBank()->savePreenFMCombo(fullState.preenFMCombo, fullState.preenFMComboPresetNumber, comboName);
break;
case MENU_SAVE_SYSEX_PATCH:
//PresetUtil::sendCurrentPatchToSysex();
storage->getPatchBank()->sendPreenFMPatchAsSysex(params);
break;
case MENU_RENAME_COMBO:
for (length=0; length<8 && fullState.name[length]!=0; length++) {
fullState.name[length] = allChars[fullState.name[length]];
}
fullState.name[length++] = '.';
fullState.name[length++] = 'c';
fullState.name[length++] = 'm';
fullState.name[length++] = 'b';
fullState.name[length++] = '\0';
if (storage->getComboBank()->renameFile(fullState.preenFMCombo, fullState.name) > 0) {
rMenuItem = MenuItemUtil::getMenuItem(MENU_ERROR);
}
break;
case MENU_RENAME_BANK:
for (length=0; length<8 && fullState.name[length]!=0; length++) {
fullState.name[length] = allChars[fullState.name[length]];
}
fullState.name[length++] = '.';
fullState.name[length++] = 'b';
fullState.name[length++] = 'n';
fullState.name[length++] = 'k';
fullState.name[length++] = '\0';
if (storage->getPatchBank()->renameFile(fullState.preenFMBank, fullState.name) > 0) {
rMenuItem = MenuItemUtil::getMenuItem(MENU_ERROR);
}
break;
case MENU_CANCEL:
case MENU_DONE:
case MENU_ERROR:
fullState.synthMode = SYNTH_MODE_EDIT;
break;
case MENU_CREATE_BANK:
// Must create the bank here....
for (length=8; fullState.name[length-1] == 0; length--);
for (int k=0; k<length; k++) {
fullState.name[k] = allChars[(int)fullState.name[k]];
}
fullState.name[length++] = '.';
fullState.name[length++] = 'b';
fullState.name[length++] = 'n';
fullState.name[length++] = 'k';
fullState.name[length] = '\0';
if (!storage->getPatchBank()->nameExists(fullState.name)) {
cmi = fullState.currentMenuItem;
// Update display while formating
lcd.setRealTimeAction(true);
fullState.currentMenuItem = MenuItemUtil::getMenuItem(MENU_IN_PROGRESS);
propagateNewMenuState();
storage->getPatchBank()->create(fullState.name);
lcd.setRealTimeAction(false);
fullState.currentMenuItem = cmi;
} else {
rMenuItem = MenuItemUtil::getMenuItem(MENU_ERROR);
}
break;
case MENU_CREATE_COMBO:
// Must create the combo here....
for (length=8; fullState.name[length-1] == 0; length--);
for (int k=0; k<length; k++) {
fullState.name[k] = allChars[(int)fullState.name[k]];
}
fullState.name[length++] = '.';
fullState.name[length++] = 'c';
fullState.name[length++] = 'm';
fullState.name[length++] = 'b';
fullState.name[length] = '\0';
if (!storage->getComboBank()->nameExists(fullState.name)) {
cmi = fullState.currentMenuItem;
// Update display while formating
lcd.setRealTimeAction(true);
fullState.currentMenuItem = MenuItemUtil::getMenuItem(MENU_IN_PROGRESS);
propagateNewMenuState();
storage->getComboBank()->createComboBank(fullState.name);
lcd.setRealTimeAction(false);
fullState.currentMenuItem = cmi;
} else {
rMenuItem = MenuItemUtil::getMenuItem(MENU_ERROR);
}
break;
case MENU_CONFIG_SETTINGS_SAVE:
storage->getConfigurationFile()->saveConfig(fullState.midiConfigValue);
break;
case MENU_DEFAULT_COMBO_SAVE:
storage->getComboBank()->saveDefaultCombo();
break;
case MENU_DEFAULT_COMBO_RESET:
storage->getComboBank()->removeDefaultCombo();
break;
case MENU_LOAD:
fullState.loadWhat = fullState.menuSelect;
break;
case MENU_SAVE:
fullState.saveWhat = fullState.menuSelect;
break;
case MENU_TOOLS:
fullState.toolsWhat = fullState.menuSelect;
break;
case MENU_SCALA:
fullState.scalaWhat = fullState.menuSelect;
break;
case MENU_SCALA_FILENAME:
storage->getConfigurationFile()->saveScalaConfig(&fullState.scalaScaleConfig);
if (fullState.scalaScaleConfig.scalaFile->fileType != FILE_OK) {
return fullState.currentMenuItem;
}
break;
case MENU_SCALA_ENABLE:
storage->getConfigurationFile()->saveScalaConfig(&fullState.scalaScaleConfig);
break;
case MENU_SCALA_MAPPING:
case MENU_SCALA_FREQUENCY:
storage->getConfigurationFile()->saveScalaConfig(&fullState.scalaScaleConfig);
break;
default:
break;
}
// ---------------------
// Next state switch
switch (rMenuItem->menuState) {
case MENU_CREATE_COMBO:
{
const char *bankName = "COMBO01";
int length = 7;
for (int k=0; k<length ; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (bankName[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
for (int k=length; k<8; k++) {
fullState.name[k] = 0;
}
fullState.menuSelect = 0;
break;
}
case MENU_CREATE_BANK:
{
const char *bankName = "BANK01";
int length = 6;
for (int k=0; k<length ; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (bankName[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
for (int k=length; k<8; k++) {
fullState.name[k] = 0;
}
fullState.menuSelect = 0;
break;
}
case MENU_RENAME_COMBO:
for (int k=0; k<8; k++) {
fullState.name[k] = 0;
}
for (int k=0; k<8 && fullState.preenFMCombo->name[k]!='.'; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (fullState.preenFMCombo->name[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
fullState.menuSelect = 0;
break;
case MENU_RENAME_BANK:
for (int k=0; k<8; k++) {
fullState.name[k] = 0;
}
for (int k=0; k<8 && fullState.preenFMBank->name[k]!='.'; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (fullState.preenFMBank->name[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
fullState.menuSelect = 0;
break;
case MENU_RENAME_PATCH:
for (int k=0; k<12; k++) {
fullState.name[k] = 0;
}
for (int k=0; k<12 && params->presetName[k] != 0; k++) {
for (int j=0; j<getLength(allChars); j++) {
if (params->presetName[k] == allChars[j]) {
fullState.name[k] = j;
}
}
}
fullState.menuSelect = 0;
break;
case MENU_SAVE_SELECT_BANK_PRESET:
fullState.menuSelect = fullState.preenFMPresetNumber;
break;
case MENU_SAVE_SELECT_COMBO_PRESET:
fullState.menuSelect = fullState.preenFMComboPresetNumber;
break;
case MENU_LOAD_SELECT_BANK_PRESET:
loadPreenFMPatch(currentTimbre, fullState.preenFMBank, fullState.preenFMPresetNumber, params);
fullState.menuSelect = fullState.preenFMPresetNumber;
break;
case MENU_LOAD_SELECT_COMBO_PRESET:
fullState.menuSelect = fullState.preenFMComboPresetNumber;
break;
case MENU_LOAD_SELECT_DX7_PRESET:
loadDx7Patch(currentTimbre, fullState.dx7Bank, fullState.dx7PresetNumber, params);
fullState.menuSelect = fullState.dx7PresetNumber;
break;
case MENU_TOOLS:
fullState.menuSelect = fullState.toolsWhat;
break;
case MENU_LOAD:
fullState.menuSelect = fullState.loadWhat;
break;
case MENU_SAVE:
fullState.menuSelect = fullState.saveWhat;
break;
case MENU_SCALA:
fullState.menuSelect = fullState.scalaWhat;
break;
case MENU_RENAME_SELECT_BANK:
case MENU_LOAD_SELECT_BANK:
case MENU_SAVE_SELECT_BANK:
fullState.menuSelect = fullState.preenFMBankNumber;
fullState.preenFMBank = storage->getPatchBank()->getFile(fullState.menuSelect);
break;
case MENU_RENAME_SELECT_COMBO:
case MENU_LOAD_SELECT_COMBO:
case MENU_SAVE_SELECT_COMBO:
fullState.menuSelect = fullState.preenFMComboNumber;
fullState.preenFMCombo = storage->getComboBank()->getFile(fullState.menuSelect);
break;
case MENU_LOAD_SELECT_DX7_BANK:
fullState.menuSelect = fullState.dx7BankNumber;
fullState.dx7Bank = storage->getDX7SysexFile()->getFile(fullState.menuSelect);
break;
case MENU_SCALA_FILENAME:
fullState.menuSelect = 0;
if (fullState.scalaScaleConfig.scalaFile != NULL) {
int scalaIndex = storage->getScalaFile()->getFileIndex(fullState.scalaScaleConfig.scalaFile);
// can be <0 if file has been removed
if (scalaIndex >= 0) {
fullState.menuSelect = scalaIndex;
}
} else {
fullState.scalaScaleConfig.scalaFile = storage->getScalaFile()->getFile(0);
}
break;
case MENU_SCALA_MAPPING:
fullState.menuSelect = fullState.scalaScaleConfig.keyboardMapping ? 0 : 1;
break;
case MENU_SCALA_ENABLE:
fullState.menuSelect = fullState.scalaScaleConfig.scalaEnabled ? 1 : 0;
break;
default:
fullState.menuSelect = 0;
}
// Save menu select for menuBack Action
fullState.previousMenuSelect = fullState.menuSelect;
return rMenuItem;
}
const MenuItem* SynthState::menuBack() {
const MenuItem* rMenuItem = 0;
// default menuSelect value
fullState.menuSelect = MenuItemUtil::getParentMenuSelect(fullState.currentMenuItem->menuState);
switch (fullState.currentMenuItem->menuState) {
case MENU_SAVE_ENTER_PRESET_NAME:
fullState.menuSelect = fullState.preenFMPresetNumber;
break;
case MENU_SAVE_ENTER_COMBO_NAME:
fullState.menuSelect = fullState.preenFMComboNumber;
break;
case MENU_RENAME_BANK:
case MENU_SAVE_SELECT_BANK_PRESET:
fullState.menuSelect = fullState.preenFMBankNumber;
break;
case MENU_RENAME_COMBO:
case MENU_SAVE_SELECT_COMBO_PRESET:
fullState.menuSelect = fullState.preenFMComboNumber;
break;
case MENU_LOAD_SELECT_COMBO_PRESET:
fullState.menuSelect = fullState.preenFMComboNumber;
propagateBeforeNewParamsLoad(currentTimbre);
copySynthParams((char*)&backupParams, (char*)params);
propagateAfterNewParamsLoad(currentTimbre);
break;
case MENU_LOAD_RANDOMIZER:
// Put back original preset
propagateNoteOff();
propagateBeforeNewParamsLoad(currentTimbre);
copySynthParams((char*)&backupParams, (char*)params);
propagateAfterNewParamsLoad(currentTimbre);
break;
case MENU_LOAD_SELECT_BANK_PRESET:
propagateNoteOff();
fullState.menuSelect = fullState.preenFMBankNumber;
propagateBeforeNewParamsLoad(currentTimbre);
copySynthParams((char*)&backupParams, (char*)params);
propagateAfterNewParamsLoad(currentTimbre);
break;
case MENU_LOAD_SELECT_DX7_PRESET:
propagateNoteOff();
fullState.menuSelect = fullState.dx7BankNumber;
propagateBeforeNewParamsLoad(currentTimbre);
copySynthParams((char*)&backupParams, (char*)params);
propagateAfterNewParamsLoad(currentTimbre);
break;
case MAIN_MENU:
fullState.synthMode = SYNTH_MODE_EDIT;
// put back old patch (has been overwritten if a new patch has been loaded)
break;
}
rMenuItem = MenuItemUtil::getParentMenuItem(fullState.currentMenuItem->menuState);
return rMenuItem;
}
void SynthState::propagateAfterNewParamsLoad(int timbre) {
for (SynthParamListener* listener = firstParamListener; listener !=0; listener = listener->nextListener) {
listener->afterNewParamsLoad(timbre);
}
}
void SynthState::propagateAfterNewComboLoad() {
for (SynthParamListener* listener = firstParamListener; listener !=0; listener = listener->nextListener) {
listener->afterNewComboLoad();
}
}
void SynthState::propagateNewTimbre(int timbre) {
propagateNoteOff();
for (SynthParamListener* listener = firstParamListener; listener !=0; listener = listener->nextListener) {
listener->newTimbre(timbre);
}
}
void SynthState::tempoClick() {
if (fullState.synthMode == SYNTH_MODE_MENU) {
if (fullState.currentMenuItem->menuState == MENU_DONE
|| fullState.currentMenuItem->menuState == MENU_ERROR
|| fullState.currentMenuItem->menuState == MENU_CANCEL) {
if (doneClick > 4) {
fullState.synthMode = SYNTH_MODE_EDIT;
propagateNewSynthMode();
}
doneClick ++;
}
} else {
doneClick = 0;
}
}
void SynthState::setParamsAndTimbre(struct OneSynthParams *newParams, int newCurrentTimbre) {
this->params = newParams;
this->currentTimbre = newCurrentTimbre;
}
void SynthState::copySynthParams(char* source, char* dest) {
for (int k=0; k<sizeof(struct OneSynthParams); k++) {
dest[k] = source[k];
}
}
void SynthState::analyseSysexBuffer(uint8_t *buffer) {
propagateBeforeNewParamsLoad(currentTimbre);
storage->getPatchBank()->decodeBufferAndApplyPreset(buffer, params);
propagateAfterNewParamsLoad(currentTimbre);
}
void SynthState::onUserChangedRow() {
if ( !fullState.midiConfigValue[MIDICONFIG_UNLINKED_EDITING] ) {
// Reset the row so it uses the same for each instrument
for (int t = 0; t < NUMBER_OF_TIMBRES; ++t )
lastRowForTimbre[t] = -1;
}
}
int SynthState::getLastRowForTimbre( int timbre ) const
{
if ( fullState.midiConfigValue[MIDICONFIG_UNLINKED_EDITING] )
return lastRowForTimbre[ timbre ];
else
return lastRowForTimbre[ 0 ];
}
void SynthState::setLastRowForTimbre( int timbre, int row )
{
if ( fullState.midiConfigValue[MIDICONFIG_UNLINKED_EDITING] ) {
lastRowForTimbre[ timbre ] = row;
} else {
// Only remember row if there currently isn't one set; this means
// we are cycling through the instruments and want to return to
// the row that was set when the cycle started. This saved row is
// invalidated when the user manually changes row.
if ( lastRowForTimbre[ 0 ] < 0 )
lastRowForTimbre[ 0 ] = row;
}
}
/*
* Randomizer
*/
int getRandomInt(int max) {
while (RNG_GetFlagStatus(RNG_FLAG_DRDY)== RESET) {
};
return RNG_GetRandomNumber() % max;
}
float getRandomFloat(float min, float max) {
while (RNG_GetFlagStatus(RNG_FLAG_DRDY)== RESET) {
};
float f = ((float)(RNG_GetRandomNumber() % 100000)) / 100000.0f;
return f * (max - min) + min;
}
float getRandomShape(int operatorRandom) {
int shape = getRandomInt(7);
switch (operatorRandom) {
case 1:
if (shape > 2) {
shape = 0;
}
break;
case 2:
shape = getRandomInt(8);
if (shape == 6) { // Rand
shape = 0;
}
break;
case 3:
break;
}
return shape;
}
float getRandomFrequencyType(int operatorRandom) {
int freqType = getRandomInt(32);
if (freqType > 1) { // Keyboard 5 times out of 6
freqType = 0;
}
return freqType;
}
float getRandomFrequency(int operatorRandom) {
float random1Frequency[] = { .5f, 1.0f, 2.0f, 4.0f};
float random2Frequency[] = { .25, .5f, 1.0f, 1.5, 2.0f, 3.0f, 4.0f};
float freq = 0;
switch (operatorRandom) {
case 1:
freq = random1Frequency[getRandomInt(4)];
break;
case 2:
freq = random2Frequency[getRandomInt(7)];
break;
case 3:
freq = getRandomInt(24) * .25 + .25;
break;
}
return freq;
}
float getFineTune(int operatorRandom) {
float fineTune = 0;
switch (operatorRandom) {
case 2:
fineTune = getRandomInt(10) * .01 - .05;
if (fineTune < 0.03 || fineTune > 0.03) {
fineTune = 0;
}
break;
case 3:
fineTune = getRandomInt(20) * .01 - .05;
if (fineTune < 0.07 || fineTune > 0.07) {
fineTune = 0;
}
break;
}
return fineTune;
}
void SynthState::randomizePreset() {
int operatorRandom = fullState.randomizer.Oper;
int envelopeTypeRandom = fullState.randomizer.EnvT;
int imRandom = fullState.randomizer.IM;
int modulationRandom = fullState.randomizer.Modl;
// general
params->engine1.velocity = 8;
if (operatorRandom > 0) {
params->engineMix1.mixOsc1 = getRandomFloat(0.6f, 1.0f);
params->engineMix1.mixOsc2 = getRandomFloat(0.6f, 1.0f);
params->engineMix2.mixOsc3 = getRandomFloat(0.6f, 1.0f);
params->engineMix2.mixOsc4 = getRandomFloat(0.6f, 1.0f);
params->engineMix3.mixOsc5 = getRandomFloat(0.6f, 1.0f);
params->engineMix3.mixOsc6 = getRandomFloat(0.6f, 1.0f);
params->engineMix1.panOsc1 = 0.0;
params->engineMix1.panOsc2 = getRandomFloat(-0.3f, 0.3f);
params->engineMix2.panOsc3 = getRandomFloat(-0.3f, 0.3f);
params->engineMix2.panOsc4 = getRandomFloat(-0.5f, 0.5f);
params->engineMix3.panOsc5 = getRandomFloat(-0.5f, 0.5f);
params->engineMix3.panOsc6 = getRandomFloat(-0.7f, 0.7f);
params->engine1.algo = getRandomInt(ALGO_END);
if (getRandomInt(6) == 0) {
params->engine1.numberOfVoice = 1;
params->engine1.glide = getRandomInt(6) + 3;
} else {
params->engine1.numberOfVoice = 3;
}
for (int o = 0; o < 6; o++) {
struct OscillatorParams* currentOsc = &((struct OscillatorParams*)&params->osc1)[o];
currentOsc->shape = getRandomShape(operatorRandom);
currentOsc->frequencyMul = getRandomFrequency(operatorRandom);
currentOsc->frequencyType = getRandomFrequencyType(operatorRandom);
currentOsc->detune = getFineTune(operatorRandom);
}
// FX
params->effect.param1 = getRandomFloat(0.2f, 0.8f);
params->effect.param2 = getRandomFloat(0.2f, 0.8f);
int effect = getRandomInt(15);
if (effect == 1 || effect > 6) {
params->effect.param3 = 1.0;
params->effect.type = 0;
} else {
params->effect.param3 = 0.6;
params->effect.type= effect;
}
}
for (int e = 0; e < 6; e++) {
struct EnvelopeParamsA* enva = &((struct EnvelopeParamsA*)&params->env1a)[e * 2];
struct EnvelopeParamsB* envb = &((struct EnvelopeParamsB*)&params->env1b)[e * 2];
switch (envelopeTypeRandom) {
case 1:
enva->attackLevel = 1.0;
enva->attackTime = getRandomFloat(0, 0.3f);
enva->decayLevel= getRandomFloat(0.5f, 1.0f);
enva->decayTime = getRandomFloat(0.05, 0.5f);
envb->sustainLevel = getRandomFloat(0.0f, 1.0f);
envb->sustainTime = getRandomFloat(0.02, 1.0f);
envb->releaseLevel = 0.0f;
envb->releaseTime = getRandomFloat(0.2, 5.0f);;
break;
case 2:
enva->attackLevel = getRandomFloat(0.25f, 1.0f);
enva->attackTime = getRandomFloat(0.5f, 3.0f);
enva->decayLevel= getRandomFloat(0.5f, 1.0f);
enva->decayTime = getRandomFloat(1.0f, 5.0f);
envb->sustainLevel = getRandomFloat(0.0f, 1.0f);
envb->sustainTime = getRandomFloat(2.0f, 5.0f);
envb->releaseLevel = 0.0f;
envb->releaseTime = getRandomFloat(1.0f, 8.0f);
break;
case 3:
enva->attackLevel = getRandomFloat(0, 1.0f);
enva->attackTime = getRandomFloat(0, 1.0f);
enva->decayLevel = getRandomFloat(0, 1.0f);
enva->decayTime = getRandomFloat(0, 1.0f);
envb->sustainLevel = getRandomFloat(0, 1.0f);
envb->sustainTime = getRandomFloat(0, 1.0f);
envb->releaseLevel = getRandomFloat(0, 1.0f);
envb->releaseTime = getRandomFloat(0, 4.0f);
break;
}
}
if (imRandom > 0) {
struct EngineIm1* im1 = (struct EngineIm1*)&params->engineIm1;
struct EngineIm2* im2 = (struct EngineIm2*)&params->engineIm2;
struct EngineIm3* im3 = (struct EngineIm3*)&params->engineIm3;
float min = 0;
float max = 0;
float minVelo = 0;
float maxVelo = 0;
switch (imRandom) {
case 1:
min = 0.25f;
max = 2.0f;
minVelo = 0.2f;
maxVelo = 1.0f;
break;
case 2:
min = .5f;
max = 3.0f;
minVelo = 1.0f;
maxVelo = 2.0f;
break;
case 3:
min = 1.0f;
max = 5.0f;
minVelo = 1.0f;
maxVelo = 4.0f;
break;
}
im1->modulationIndex1 = getRandomFloat(min, max);
im1->modulationIndexVelo1 = getRandomFloat(minVelo, maxVelo);
im1->modulationIndex2 = getRandomFloat(min, max);
im1->modulationIndexVelo2 = getRandomFloat(minVelo, maxVelo);
im2->modulationIndex3 = getRandomFloat(min, max);
im2->modulationIndexVelo3 = getRandomFloat(minVelo, maxVelo);
im2->modulationIndex4 = getRandomFloat(min, max);
im2->modulationIndexVelo4 = getRandomFloat(minVelo, maxVelo);
im3->modulationIndex5 = getRandomFloat(min, max);
im3->modulationIndexVelo5 = getRandomFloat(minVelo, maxVelo);
}
if (modulationRandom > 0) {
params->matrixRowState1.source = MATRIX_SOURCE_LFO1;
params->matrixRowState2.source = MATRIX_SOURCE_LFO1;
params->matrixRowState3.source = MATRIX_SOURCE_LFO2;
params->matrixRowState4.source = MATRIX_SOURCE_LFO2;
params->matrixRowState5.source = MATRIX_SOURCE_LFO3;
params->matrixRowState6.source = MATRIX_SOURCE_LFOENV1;
params->matrixRowState7.source = MATRIX_SOURCE_LFOENV2;
params->matrixRowState8.source = MATRIX_SOURCE_LFOSEQ1;
params->matrixRowState9.source = MATRIX_SOURCE_LFOSEQ2;
params->matrixRowState10.source = MATRIX_SOURCE_MODWHEEL;
params->matrixRowState10.mul = 2.0f;
params->matrixRowState10.destination = INDEX_ALL_MODULATION;
params->matrixRowState11.source = MATRIX_SOURCE_PITCHBEND;
params->matrixRowState11.mul = 1.0f;
params->matrixRowState11.destination = ALL_OSC_FREQ;
params->matrixRowState12.source = MATRIX_SOURCE_AFTERTOUCH;
params->matrixRowState12.mul = 1.0f;
params->matrixRowState12.destination = INDEX_MODULATION1;
for (int m = 1; m<=9; m++) {
struct MatrixRowParams* matrixRow = &((struct MatrixRowParams*)&params->matrixRowState1)[m - 1];
matrixRow->mul = 0;
matrixRow->destination = 0;
}
float dest[] = { INDEX_ALL_MODULATION, OSC1_FREQ, ALL_PAN, OSC2_FREQ, INDEX_MODULATION1, PAN_OSC1, INDEX_ALL_MODULATION };
for (int i = 0; i < 2; i++) {
struct MatrixRowParams* matrixRow = &((struct MatrixRowParams*)&params->matrixRowState1)[getRandomInt(10)];
matrixRow->mul = getRandomFloat(0.2f, 3.0f);
matrixRow->destination = dest[getRandomInt(7)];
}
if (modulationRandom >= 2) {
for (int i = 0; i < 3; i++) {
struct MatrixRowParams* matrixRow = &((struct MatrixRowParams*)&params->matrixRowState1)[getRandomInt(10)];
matrixRow->mul = getRandomFloat(1.0f, 3.0f);
matrixRow->destination = getRandomInt(DESTINATION_MAX);
}
}
if (modulationRandom >=3) {
for (int i = 0; i < 6; i++) {
struct MatrixRowParams* matrixRow = &((struct MatrixRowParams*)&params->matrixRowState1)[getRandomInt(10)];
float mm = getRandomFloat(2.0f, 5.0f);
matrixRow->mul = getRandomFloat(-mm, mm);
matrixRow->destination = getRandomInt(DESTINATION_MAX);
}
}
for (int o = 0; o < 3; o++) {
struct LfoParams* osc = &((struct LfoParams*)&params->lfoOsc1)[o];
osc->shape = getRandomInt(5);
osc->freq = getRandomFloat(0.2, 3 + modulationRandom*2 );
if (getRandomInt(4) > 1) {
osc->bias = 0;
} else {
osc->bias = getRandomFloat(-1.0f, 1.0f);
}
// PAD
if (envelopeTypeRandom == 2) {
osc->keybRamp = getRandomFloat(0.0f, 4.0f);
} else {
osc->keybRamp = getRandomFloat(0.0f, 1.0f);
}
}
for (int e = 0; e < 2; e++) {
struct EnvelopeParams* env = &((struct EnvelopeParams*)&params->lfoEnv1)[e];
env->attack = getRandomFloat(0.05f, 1.0f);
env->decay = getRandomFloat(0.05f, 1.0f);
env->sustain = getRandomFloat(0.05f, 1.0f);
if (e==0) {
env->release = getRandomFloat(0.05f, 1.0f);
} else {
params->lfoEnv2.loop = getRandomInt(2) + 1;
}
}
int bpm = getRandomInt(120) + 60;
for (int s = 0; s < 2; s++) {
struct StepSequencerParams* stepSeq = &((struct StepSequencerParams*)&params->lfoSeq1)[s];
stepSeq->bpm = bpm;
stepSeq->gate = getRandomFloat(0.25, 1);
struct StepSequencerSteps* steps = &((struct StepSequencerSteps*)&params->lfoSteps1)[s];
for (int k =0; k<16; k++) {
if ((k % 4) == 0) {
steps->steps[k] = getRandomInt(5) + 11;
} else {
steps->steps[k] = getRandomInt(10) ;
}
}
}
}
}