/* MicroDexed MicroDexed is a port of the Dexed sound engine (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield. Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android (c)2018-2021 M. Koslowski H. Wirtz 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 (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 "config.h" #include "sequencer.h" #include #include extern LCDMenuLib2 LCDML; extern LiquidCrystal_I2C lcd; extern config_t configuration; extern uint8_t drum_midi_channel; extern uint8_t activesample; extern uint8_t get_sample_note(uint8_t sample); extern void handleNoteOn(byte , byte , byte ); extern void handleNoteOff(byte , byte , byte ); extern void UI_func_seq_pattern_editor(uint8_t); extern void UI_func_arpeggio(uint8_t); extern const char* seq_find_shortname(uint8_t); extern void set_sample_pitch (uint8_t, float); //float32_t not working extern float get_sample_vol_max(uint8_t); extern float get_sample_p_offset(uint8_t); boolean interrupt_swapper = false; sequencer_t seq; void seq_live_recording(void) { //record to sequencer if sequencer menu is active and recording is active if (seq.seq_note_in > 0 && seq.seq_recording == true && LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_seq_pattern_editor)) { seq.seq_data[seq.seq_active_track][seq.seq_step] = seq.seq_note_in; if ( get_sample_note(activesample) > 209 ) // pitched sample { seq.seq_vel[seq.seq_active_track][seq.seq_step] = get_sample_note(activesample); } else seq.seq_vel[seq.seq_active_track][seq.seq_step] = seq.seq_note_in_velocity; seq.seq_note_in = 0; seq.seq_note_in_velocity = 0; } } void sequencer_part1(void) { //if (seq.seq_note_in > 0 && seq.seq_note_in < 62 && seq.seq_recording == false ) { //handleNoteOff(configuration.dexed[0].midi_channel, seq.seq_data[3][seq.seq_step] + seq.seq_transpose , 0); //handleNoteOff(configuration.dexed[0].midi_channel, seq.seq_data[3][seq.seq_step - 1] + seq.seq_transpose , 0); //if (seq.seq_note_in>65)seq.seq_note_in=seq.seq_note_in-12; //seq.seq_transpose = seq.seq_note_in % 12 ; //seq.seq_transpose=seq.seq_transpose-12; //seq.seq_note_in = 0; //} seq_live_recording(); for (uint8_t d = 0; d < NUM_SEQ_TRACKS; d++) { if (seq.seq_patternchain[seq.seq_chain_active_step][d] < NUM_SEQ_PATTERN ) // sequence not empty or muted { if ( seq.seq_track_type[d] == 0) { // drum track (drum samples and pitched one-shot samples) if (seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] > 0 ) { if (seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] > 209) // it is a pitched sample { // Drum[slot]->setPlaybackRate( pow (2, (inNote - 72) / 12.00) * drum_config[sample].pitch ); get_sample_vol_max(sample) set_sample_pitch(seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] - 210 , (float)pow (2, (seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] - 72) / 12.00) * get_sample_p_offset( seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] - 210 ) ); handleNoteOn(drum_midi_channel, seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] , 90 ); } else // else play normal drum sample handleNoteOn(drum_midi_channel, seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] , seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step]); } } else { if (seq.seq_data[seq.seq_patternchain[seq.seq_chain_active_step][d]][seq.seq_step] > 0 ) // instrument track { if (seq.seq_track_type[d] == 1 || (seq.seq_track_type[d] == 3 && seq.arp_play_basenote) ) { if (seq.seq_data[seq.seq_patternchain[seq.seq_chain_active_step][d]][seq.seq_step] != 130 ) { handleNoteOn(configuration.dexed[seq.seq_inst_dexed[d]].midi_channel, seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step], seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step]); seq.seq_prev_note[d] = seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step]; seq.seq_prev_vel[d] = seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step]; } } else if (seq.seq_track_type[d] == 2 ) //Chords { if (seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d]][seq.seq_step] > 199) { //handleNoteOn(configuration.dexed[seq.seq_inst_dexed[d]].midi_channel, seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step], seq.seq_chord_velocity); // basenote for (uint8_t x = seq.seq_element_shift; x < seq.seq_element_shift + seq.seq_chord_key_ammount; x++) //play chord notes { handleNoteOn(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] + (seq.seq_oct_shift * 12) + seq.seq_arps[seq.seq_vel[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] - 200][x], seq.seq_chord_velocity); } seq.seq_prev_note[d] = seq.seq_data[seq.seq_patternchain[seq.seq_chain_active_step][d]][seq.seq_step] + (seq.seq_oct_shift * 12); seq.seq_prev_vel[d] = seq.seq_vel[seq.seq_patternchain[seq.seq_chain_active_step][d]][seq.seq_step]; } } if (seq.seq_track_type[d] == 3) { //Arp seq.arp_step = 0; seq.arp_counter = 0; seq.arp_note = seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] + (seq.seq_oct_shift * 12); seq.arp_chord = seq.seq_vel[seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] - 200; } } // after here not triggered by a key input - arp only if (seq.seq_track_type[d] == 3) { //Arp if (seq.arp_speed == 0 || (seq.arp_speed == 1 && seq.arp_counter == 0) ) { { if (seq.arp_style == 0) { //arp up handleNoteOn(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_step + seq.seq_element_shift], seq.seq_chord_velocity); seq.arp_note_prev = seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_step + seq.seq_element_shift] ; } else if (seq.arp_style == 1) { //arp down handleNoteOn(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_lenght - seq.arp_step + seq.seq_element_shift], seq.seq_chord_velocity); seq.arp_note_prev = seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_lenght - seq.arp_step + seq.seq_element_shift] ; } else if (seq.arp_style == 2) { //arp up & down if (seq.arp_step <= seq.arp_lenght) { handleNoteOn(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_step ], seq.seq_chord_velocity); seq.arp_note_prev = seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_step ] ; } else { handleNoteOn(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_lenght * 2 - seq.arp_step ], seq.seq_chord_velocity); seq.arp_note_prev = seq.arp_note + seq.seq_arps[seq.arp_chord][seq.arp_lenght * 2 - seq.arp_step ] ; } } else if (seq.arp_style == 3) { //arp random uint8_t rnd1 = random(seq.arp_lenght); handleNoteOn(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.arp_note + seq.seq_arps[seq.arp_chord][rnd1 + seq.seq_element_shift] + (seq.seq_oct_shift * 12), seq.seq_chord_velocity); seq.arp_note_prev = seq.arp_note + seq.seq_arps[seq.arp_chord][rnd1 + seq.seq_element_shift] + (seq.seq_oct_shift * 12); } } } } } } seq.seq_noteoffsent[d] = false; } seq.arp_counter++; seq.seq_step++; if (seq.arp_speed == 0) // Arp Speed 1/16 { seq.arp_step++; } else { // Arp Speed 1/8 if (seq.arp_counter > 1) { seq.arp_counter = 0; seq.arp_step++; } } if (seq.arp_style != 2) { if ( (seq.arp_step > 1 && seq.seq_arps[seq.arp_chord][seq.arp_step] == 0) || seq.arp_step == seq.arp_lenght) { seq.arp_step = 0; } } if (seq.arp_style == 1 || seq.arp_style == 2 ) { if (seq.arp_lenght == 0)seq.arp_lenght = 9; } if ( seq.arp_style == 2 ) //only for up&down { if ( (seq.arp_step > 1 && seq.seq_arps[seq.arp_chord][seq.arp_step] == 0) || seq.arp_step == seq.arp_lenght * 2) { seq.arp_step = 0; } } if (seq.seq_step > 15) { seq.seq_step = 0; if (seq.seq_chain_lenght > 0) { seq.seq_chain_active_step++; if (seq.seq_chain_active_step > seq.seq_chain_lenght) { seq.seq_chain_active_step = 0; } } } } void sequencer_part2(void) { seq_live_recording(); for (uint8_t d = 0; d < NUM_SEQ_TRACKS; d++) { if (seq.seq_noteoffsent[d] == false) { if ( seq.seq_prev_note[d] > 0 && seq.seq_track_type[d] > 0) { if (seq.seq_data[ seq.seq_patternchain[seq.seq_chain_active_step][d] ][seq.seq_step] != 130) { handleNoteOff(configuration.dexed[seq.seq_inst_dexed[d]].midi_channel, seq.seq_prev_note[d] , 0); seq.seq_noteoffsent[d] = true; } if (seq.seq_track_type[d] == 2) { //Chords if ( seq.seq_prev_vel[d] > 199) { for (uint8_t x = seq.seq_element_shift; x < seq.seq_element_shift + seq.seq_chord_key_ammount; x++) //play chord notes { handleNoteOff(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.seq_prev_note[d] + seq.seq_arps[seq.seq_prev_vel[d] - 200][x], 0); seq.seq_noteoffsent[d] = true; } } } else if (seq.seq_track_type[d] == 3) { //Arp handleNoteOff(configuration.dexed[seq.seq_chord_dexed_inst].midi_channel, seq.arp_note_prev, 0); seq.seq_noteoffsent[d] = true; } } } } } void sequencer(void) { // Runs in Interrupt Timer. Switches between the Noteon and Noteoff Task, each cycle interrupt_swapper = !interrupt_swapper; if (interrupt_swapper) sequencer_part1(); else sequencer_part2(); }