How to create and add a new function to the LXR
Where to get the source: https://github.com/SonicPotions/LXR
This is a DRAFT !!!! I will correct it during the next days :-)
- GOAL:
- We will implement a function that change the length of all the tracks at once. A sort of loop/divide (like on the arturia spark machine). We will add a special knob in the PERF menu: when this knob is turned, the length of the tracks changes from 1 to 16 making the whole pattern ROLL :-)
- another aim is to make you look at the difrent files, inspect them and start undestand the logic of the LXR drum system that Julian made :-)
- First step, we start by adding a new knob 'LEN' (for Length) in the PERF menu on the LCD display.
- Open the file 'Parameters.h', Add in the section 'enum ParamEnums' the new parameter. In our case : PAR_LOOP
example: enum ParamEnums = { ... PAR_MIDI_NOTE1, PAR_MIDI_NOTE2, PAR_MIDI_NOTE3, PAR_MIDI_NOTE4, PAR_MIDI_NOTE5, PAR_MIDI_NOTE6, PAR_MIDI_NOTE7, // <--- YOUR parameters comes right after thoses lines... // You have to add all the new parameters right after a dedicated section in the code declaration //(otherwise you want link later the knob with the function you want to execute): PAR_LOOP, ...
- We need to open the menu.c file. we declare our parameter and its type :
menu.c //--------------------------------------------------------------- // Parameter types. These correspond with ParamEnums and entries in parameter_values const enum Datatypes PROGMEM parameter_dtypes[NUM_PARAMS] = { /*PAR_NONE*/ DTYPE_0B127, // 0 /*PAR_OSC_WAVE_DRUM1*/ DTYPE_MENU | (MENU_WAVEFORM<<4), /*PAR_OSC_WAVE_DRUM2*/ DTYPE_MENU | (MENU_WAVEFORM<<4), /*PAR_OSC_WAVE_DRUM3*/ DTYPE_MENU | (MENU_WAVEFORM<<4), /*PAR_OSC_WAVE_SNARE*/ DTYPE_MENU | (MENU_WAVEFORM<<4), ....
- Place your parameter at a special place, right after the section:
/*PAR_MIDI_NOTE1*/ DTYPE_NOTE_NAME, /*PAR_MIDI_NOTE2*/ DTYPE_NOTE_NAME, /*PAR_MIDI_NOTE3*/ DTYPE_NOTE_NAME, /*PAR_MIDI_NOTE4*/ DTYPE_NOTE_NAME, /*PAR_MIDI_NOTE5*/ DTYPE_NOTE_NAME, /*PAR_MIDI_NOTE6*/ DTYPE_NOTE_NAME, /*PAR_MIDI_NOTE7*/ DTYPE_NOTE_NAME, // YOUR PARAMETER comes right here and not elsewhere!!!!! /*PAR_LOOP*/ DTYPE_1B16, // Values goes from 1 to 16 steps /*PAR_ROLL*/ DTYPE_MENU | (MENU_ROLL_RATES<<4), /*PAR_MORPH*/ DTYPE_0B255, ...
Here the list of parameters you can choose from...
Type of parameters: //----------------------------------------------------------------- enum Datatypes { DTYPE_0B255, //0:255 DTYPE_0B127, //0-127 DTYPE_PM100, //-100:100 DTYPE_MENU, //text type outputs DTYPE_PM63, //-63 <-> 63 DTYPE_1B16, //1-16 DTYPE_ON_OFF, //0-1 DTYPE_MIX_FM, //0-1 DTYPE_TARGET_SELECTION_LFO, DTYPE_TARGET_SELECTION_VELO, DTYPE_VOICE_LFO, DTYPE_AUTOM_TARGET, DTYPE_0b1, DTYPE_NOTE_NAME, // --AS eg C#0, D 1 for note name DTYPE_0B15, //0-15 /*15*/ /*16*/ // --AS warning, we can only have 16 on this list the way things are laid out };
- oki, once this done, we need to fill other information in the code
- Open the file menuPages.h
- This array hold the whole menu structure. each voice has different pages. The pages hold the top and bottom enums to indicate which values and descriptors to show
- in the section: const Page menuPages[NUM_PAGES][NUM_SUB_PAGES] PROGMEM ... add your MENU (we want that our knob display a name 'LEN')
- in our case, we add TEXT_LOOP and PAR_ROLL:
const Page menuPages[NUM_PAGES][NUM_SUB_PAGES] PROGMEM = { //page 1 - Voice 1 { //sub page 1 - Oscillator { TEXT_COARSE, TEXT_FINE, TEXT_WAVEFORM, TEXT_RND_VOICE,TEXT_EMPTY,TEXT_EMPTY,TEXT_EMPTY,TEXT_EMPTY, PAR_COARSE1, PAR_FINE1, PAR_OSC_WAVE_DRUM1, PAR_RND_VOICE1,PAR_NONE,PAR_NONE,PAR_NONE,PAR_NONE }, ... //Page 10 //---------------- Performance Mode Menu pages --------------------------- //These pages are shown when the selected mode is the "mute/pattern select" page //there is only one page with subpages shown for the whole mode { { TEXT_SHUFFLE, TEXT_ROLL_SPEED, TEXT_X_FADE, TEXT_SAMPLE_RATE, TEXT_LOOP, TEXT_EMPTY, PAR_SHUFFLE, PAR_ROLL, PAR_MORPH, PAR_VOICE_DECIMATION_ALL, PAR_LOOP, }, ...
- We also need to declare the TEXT_LOOP and PAR_LOOP in other files :-)
- Open file menu.h, go to section 'NamesEnum', add at the end the text 'TEXT_LOOP" :
enum NamesEnum { TEXT_EMPTY = 0, //0 TEXT_COARSE, TEXT_FINE, TEXT_ATTACK, TEXT_DECAY, TEXT_PITCH_DECAY, TEXT_MOD_AMOUNT, .... .... TEXT_BAR_RESET_MODE, TEXT_MIDI_CHAN_GLOBAL, TEXT_LOOP, // rstephane: LOOP text NUM_NAMES };
- Open the menu.c file, go to section valueNames
const Name valueNames[NUM_NAMES] PROGMEM = { {SHORT_EMPTY,CAT_EMPTY,LONG_EMPTY}, ....
- Add the following information:
.... {SHORT_BAR_RESET_MODE, CAT_SEQUENCER, LONG_BAR_RESET_MODE}, // TEXT_BAR_RESET_MODE {SHORT_CHANNEL, CAT_MIDI, LONG_MIDI_CHANNEL}, // TEXT_MIDI_CHAN_GLOBAL '{SHORT_LOOP, CAT_SEQUENCER, LONG_LOOP}, // We declare it at the end of the section :-)
- now, open the MenuText.h file to declare the short and long version of the TEXT (TEXT_LOOP)
- go to section 'const char longNames[][16] PROGMEM = ' and 'const char shortNames[][4] PROGMEM = '
- add the parameters marked in bold:
const char shortNames[][4] PROGMEM = { {""}, {"coa"}, {"fin"}, {"atk"}, {"dec"}, {"eg2"}, .... {"co2"}, // trigger clock out2 ppq {"pcr"}, // pattern change resets bar counter {"LEN"}, // rstephane: LEN for LOOP :-) };
const char longNames[][16] PROGMEM = { {""}, //01234567 {"Coarse"}, {"Fine"}, {"Attack"}, {"Decay"}, {"Amount"}, .... {"Out2 PPQ"}, {"Gate Mode"}, {"PCReset" }, // reset bar counter on manual pattern change {"Loop"}, // rstephane: LOOP LONG text };
- If you want you can already compile and see the results: under the PERF sub menu, your should see 'LEN' on the first knob.
- Now, we will create our function
- open midimessage.h file
//for all parameters above 127 <- our case enum { .... // Again, place your parameter at a special place, right after the section: CC2_MIDI_NOTE1, CC2_MIDI_NOTE2, CC2_MIDI_NOTE3, CC2_MIDI_NOTE4, CC2_MIDI_NOTE5, CC2_MIDI_NOTE6, CC2_MIDI_NOTE7, // s/b 111 i think // YOUR PARAMETER comes right here and not elsewhere!!!!! // When the knob is turned , it send the CC2_LOOP message CC2_LOOP, .... } Param2Enums;
- open MidiParser.c file
//----------------------------------------------------------- /** handle all incoming CCs and invoke action*/ void midiParser_ccHandler(MidiMsg msg, uint8_t updateOriginalValue) { ... else //MIDI_CC2 { const uint16_t paramNr = msg.data1+1 + 127;
if(updateOriginalValue) { midiParser_originalCcValues[paramNr] = msg.data2; } switch(msg.data1) {
case CC2_TRANS1_WAVE: voiceArray[0].transGen.waveform = msg.data2; break;
case CC2_TRANS1_VOL: voiceArray[0].transGen.volume = msg.data2/127.f; break;
... case CC2_MUTE_1: case CC2_MUTE_2: case CC2_MUTE_3: case CC2_MUTE_4: case CC2_MUTE_5: case CC2_MUTE_6: case CC2_MUTE_7: { const uint8_t voiceNr = msg.data1 - CC2_MUTE_1; if(msg.data2 == 0) { seq_setMute(voiceNr,0); } else { seq_setMute(voiceNr,1); }
} break; // // rstephane MY FUNCTIONS case CC2_LOOP: if(msg.data2 == 16) // reset the loop ! { // we stop the loop effect and // go back to prevoious track lenght seq_setLoopLength(16); } else { // we change the tracks lenght seq_setLoopLength(msg.data2); } break; default: break; ....
- open sequencer.h file, add the line :
void seq_setLoopLength(uint8_t length);
- open sequencer.c file, add the function:
void seq_setLoopLength(uint8_t length) { uint8_t i; // we set the new Loop Track Lenght for(i=0;i<NUM_TRACKS;i++) { seq_setTrackLength(i, length); } }
- compile !
- upload on the machine with the new firmware
- test!