///////////////////////////////////////////////////////////////////////////// // Should be called whenever a Note event has been received. // We expect, that velocity is 0 on a Note Off event ///////////////////////////////////////////////////////////////////////////// s32 SEQ_NotifyNoteOn(u8 note, u8 velocity) { u8 clear_stack = 0; if( velocity ) // push note into note stack NOTESTACK_Push(¬estack, note, velocity); else { // remove note from note stack // function returns 2 if no note played anymore (all keys depressed) if( NOTESTACK_Pop(¬estack, note) == 2 ) clear_stack = 1; } // At least one note played? if( !clear_stack && notestack.len > 0 ) { // start sequencer if it isn't already running if( !SEQ_BPM_IsRunning() ) SEQ_BPM_Start(); } else { // clear stack NOTESTACK_Clear(¬estack); // no key is pressed anymore: stop sequencer SEQ_BPM_Stop(); } #if 1 // optional debug messages NOTESTACK_SendDebugMessage(¬estack); #endif return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // This hook is called when a MIDI package has been received ///////////////////////////////////////////////////////////////////////////// void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package) { // if note event over MIDI channel #1 controls note of both oscillators // Note On received? if( midi_package.chn == Chn1 && (midi_package.type == NoteOn || midi_package.type == NoteOff) ) { // branch depending on Note On/Off event if( midi_package.event == NoteOn && midi_package.velocity > 0 ) { // push note into note stack NOTESTACK_Push(¬estack, midi_package.note, midi_package.velocity); } else { // remove note from note stack NOTESTACK_Pop(¬estack, midi_package.note); } // still a note in stack? if( notestack.len ) { // take first note of stack u8 note = notestack_items[0].note; u8 velocity = notestack_items[0].tag; // set frequency for both oscillators int chn; for(chn=0; chn<2; ++chn) { SYNTH_FrequencySet(chn, frqtab[note]); SYNTH_VelocitySet(chn, velocity); } // set board LED MIOS32_BOARD_LED_Set(1, 1); } else { // turn off LED (can also be used as a gate output!) MIOS32_BOARD_LED_Set(1, 0); // set velocity to 0 for all oscillators int chn; for(chn=0; chn<2; ++chn) SYNTH_VelocitySet(chn, 0x00); } #if 0 // optional debug messages NOTESTACK_SendDebugMessage(¬estack); #endif // CC#1 over MIDI channel #1 controls waveform } else if( midi_package.event == CC && midi_package.chn == Chn1 ) { int chn; for(chn=0; chn<2; ++chn) SYNTH_WaveformSet(chn, midi_package.value >> 5); // print selection print_msg = PRINT_MSG_SELECTIONS; }
///////////////////////////////////////////////////////////////////////////// // For Section Changes // If velocity == 0, Note Off event has been received, otherwise Note On event ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_MIDI_IN_Receive_NoteSC(u8 note, u8 velocity) { int octave = note / 12; int octave_taken = 0; int group; for(group=0; group<4; ++group) { if( octave == (seq_midi_in_sect_note[group] / 12) ) { octave_taken = 1; notestack_t *n = §ion_changer_notestack[group]; int section = -1; if( velocity ) { // Note On NOTESTACK_Push(n, note, velocity); section = n->note_items[0].note % 12; } else { // Note Off if( NOTESTACK_Pop(n, note) > 0 && n->len ) { section = n->note_items[0].note % 12; } } // switch to new section if required if( section >= 0 && section < 12 ) { #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("Group %d Section %d\n", group, section); #endif // following operation should be atomic! u8 track; seq_core_trk_t *t = &seq_core_trk[group*SEQ_CORE_NUM_TRACKS_PER_GROUP]; MIOS32_IRQ_Disable(); for(track=0; track<SEQ_CORE_NUM_TRACKS_PER_GROUP; ++track, ++t) t->play_section = section; MIOS32_IRQ_Enable(); } #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("NOTESTACK_SECTION_CHANGER_G%d:\n", group+1); NOTESTACK_SendDebugMessage(n); #endif } } return octave_taken; // return 1 if octave has been taken, otherwise 0 }
static s32 SEQ_MIDI_IN_Receive_NotePC(u8 note, u8 velocity) { int octave = note / 12; int octave_taken = 0; int group; for(group=0; group<4; ++group) { if( octave == (seq_midi_in_sect_note[group] / 12) ) { octave_taken = 1; notestack_t *n = &patch_changer_notestack[group]; int patch = -1; if( velocity ) { // Note On NOTESTACK_Push(n, note, velocity); patch = n->note_items[0].note % 12; } else { // Note Off if( NOTESTACK_Pop(n, note) > 0 && n->len ) { patch = n->note_items[0].note % 12; } } // switch to new patch if required if( patch >= 0 && patch < 8 ) { seq_pattern_t pattern = seq_pattern[group]; if( pattern.num != patch ) { pattern.num = patch; pattern.DISABLED = 0; pattern.SYNCHED = 0; SEQ_PATTERN_Change(group, pattern); } } #if DEBUG_VERBOSE_LEVEL >= 0 DEBUG_MSG("NOTESTACK_PATCH_CHANGER_G%d:\n", group+1); NOTESTACK_SendDebugMessage(n); #endif } } return octave_taken; // return 1 if octave has been taken, otherwise 0 }
///////////////////////////////////////////////////////////////////////////// // Arpeggiator Notestack Handling // Note On ///////////////////////////////////////////////////////////////////////////// void MbSidArp::noteOn(MbSidVoice *v, u8 note, u8 velocity) { MbSidMidiVoice *mv = (MbSidMidiVoice *)v->midiVoicePtr; // store current notestack mode notestack_mode_t saved_mode = mv->midivoiceNotestack.mode; if( arpEasyChordMode && !arpHoldMode ) { // easy chord entry: // even when HOLD mode not active, a note off doesn't remove notes in stack // the notes of released keys will be removed from stack once a *new* note is played NOTESTACK_RemoveNonActiveNotes(&mv->midivoiceNotestack); } // if no note is played anymore, clear stack again (so that new notes can be added in HOLD mode) if( NOTESTACK_CountActiveNotes(&mv->midivoiceNotestack) == 0 ) { // clear stack NOTESTACK_Clear(&mv->midivoiceNotestack); // synchronize the arpeggiator restartReq = 1; } // push note into stack - select mode depending on sort/hold mode if( arpHoldMode ) mv->midivoiceNotestack.mode = arpSortedNotes ? NOTESTACK_MODE_SORT_HOLD : NOTESTACK_MODE_PUSH_TOP_HOLD; else mv->midivoiceNotestack.mode = arpSortedNotes ? NOTESTACK_MODE_SORT : NOTESTACK_MODE_PUSH_TOP; NOTESTACK_Push(&mv->midivoiceNotestack, note, velocity); // activate note v->voiceActive = 1; // remember note v->voicePlayedNote = note; // restore notestack mode mv->midivoiceNotestack.mode = saved_mode; }
///////////////////////////////////////////////////////////////////////////// // If velocity == 0, Note Off event has been received, otherwise Note On event ///////////////////////////////////////////////////////////////////////////// static s32 SEQ_MIDI_IN_Receive_Note(u8 bus, u8 note, u8 velocity) { notestack_t *n; if( bus >= SEQ_MIDI_IN_NUM_BUSSES ) return -1; /////////////////////////////////////////////////////////////////////////// // Transposer /////////////////////////////////////////////////////////////////////////// n = &bus_notestack[bus][BUS_NOTESTACK_TRANSPOSER]; if( velocity ) { // Note On NOTESTACK_Push(n, note, velocity); transposer_hold_note[bus] = n->note_items[0].note; // will only be used for Bus1 and if enabled in OPT menu if( bus == 0 ) seq_core_keyb_scale_root = note % 12; } else { // Note Off if( NOTESTACK_Pop(n, note) > 0 && n->len ) { transposer_hold_note[bus] = n->note_items[0].note; } } #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("NOTESTACK_TRANSPOSER[%d]:\n", bus); NOTESTACK_SendDebugMessage(&bus_notestack[bus][BUS_NOTESTACK_TRANSPOSER]); #endif /////////////////////////////////////////////////////////////////////////// // Arpeggiator /////////////////////////////////////////////////////////////////////////// if( velocity ) { // Note On // if no note in note stack anymore, reset position of all tracks with RESTART flag set if( !bus_notestack[bus][BUS_NOTESTACK_ARP_UNSORTED].len ) { u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) if( seq_cc_trk[track].mode.RESTART ) { portENTER_CRITICAL(); seq_core_trk[track].state.POS_RESET = 1; portEXIT_CRITICAL(); } // and invalidate hold stacks int i; for(i=0; i<4; ++i) arp_sorted_hold[bus][i].ALL = arp_unsorted_hold[bus][i].ALL = 0; } // add to stacks NOTESTACK_Push(&bus_notestack[bus][BUS_NOTESTACK_ARP_SORTED], note, velocity); NOTESTACK_Push(&bus_notestack[bus][BUS_NOTESTACK_ARP_UNSORTED], note, velocity); // copy to hold stack int i; for(i=0; i<4; ++i) { arp_unsorted_hold[bus][i].ALL = (i < bus_notestack[bus][BUS_NOTESTACK_ARP_UNSORTED].len) ? bus_notestack[bus][BUS_NOTESTACK_ARP_UNSORTED].note_items[i].ALL : 0; arp_sorted_hold[bus][i].ALL = (i < bus_notestack[bus][BUS_NOTESTACK_ARP_SORTED].len) ? bus_notestack[bus][BUS_NOTESTACK_ARP_SORTED].note_items[i].ALL : 0; } } else { // Note Off // remove note from sorted/unsorted stack (not hold stacks) NOTESTACK_Pop(&bus_notestack[bus][BUS_NOTESTACK_ARP_SORTED], note); NOTESTACK_Pop(&bus_notestack[bus][BUS_NOTESTACK_ARP_UNSORTED], note); } #if DEBUG_VERBOSE_LEVEL >= 1 DEBUG_MSG("NOTESTACK_ARP_SORTED[%d]:\n", bus); NOTESTACK_SendDebugMessage(&bus_notestack[bus][BUS_NOTESTACK_ARP_SORTED]); DEBUG_MSG("NOTESTACK_ARP_UNSORTED[%d]:\n", bus); NOTESTACK_SendDebugMessage(&bus_notestack[bus][BUS_NOTESTACK_ARP_UNSORTED]); #endif return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // This hook is called when a MIDI package has been received ///////////////////////////////////////////////////////////////////////////// void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package) { // if note event over MIDI channel #1 controls note of both oscillators // Note On received? if( midi_package.chn == Chn1 && (midi_package.type == NoteOn || midi_package.type == NoteOff) ) { // branch depending on Note On/Off event if( midi_package.event == NoteOn && midi_package.velocity > 0 ) { // push note into note stack NOTESTACK_Push(¬estack, midi_package.note, midi_package.velocity); } else { // remove note from note stack NOTESTACK_Pop(¬estack, midi_package.note); } // still a note in stack? if( notestack.len ) { // take first note of stack u8 note = notestack_items[0].note; u8 velocity = notestack_items[0].tag; #if 0 // set frequency for both oscillators int chn; for(chn=0; chn<2; ++chn) { SYNTH_FrequencySet(chn, frqtab[note]); SYNTH_VelocitySet(chn, velocity); } #endif // play note int phrase_num = note - PHRASE_NOTE_OFFSET; while( phrase_num < 0) phrase_num += SYNTH_NUM_PHRASES; while( phrase_num > SYNTH_NUM_PHRASES ) phrase_num -= SYNTH_NUM_PHRASES; // legato... if( notestack.len == 1 ) SYNTH_PhraseStop(phrase_num); SYNTH_PhrasePlay(phrase_num, velocity); // set board LED MIOS32_BOARD_LED_Set(1, 1); } else { // turn off LED (can also be used as a gate output!) MIOS32_BOARD_LED_Set(1, 0); #if 0 // set velocity to 0 for all oscillators int chn; for(chn=0; chn<2; ++chn) SYNTH_VelocitySet(chn, 0x00); #endif } #if 0 // optional debug messages NOTESTACK_SendDebugMessage(¬estack); #endif } else if( midi_package.type == CC ) { u8 phrase_num = 0; u8 phoneme_ix = midi_package.chn; u32 value = midi_package.value; if( midi_package.cc_number < 16 ) { } else if( midi_package.cc_number < 32 ) { u8 phoneme_par = midi_package.cc_number - 16; SYNTH_PhonemeParSet(phrase_num, phoneme_ix, phoneme_par, value); } else if( midi_package.cc_number < 48 ) { u8 phrase_par = midi_package.cc_number - 32; SYNTH_PhraseParSet(phrase_num, phrase_par, value); } else if( midi_package.cc_number < 64 ) { u8 global_par = midi_package.cc_number - 48; SYNTH_GlobalParSet(global_par, value); } } }
void MbCvMidiVoice::notestackPush(u8 note, u8 velocity) { NOTESTACK_Push(&midivoiceNotestack, note, velocity); }
///////////////////////////////////////////////////////////////////////////// //! This function is called by MBNG_EVENT_ItemReceive when a matching value //! has been received ///////////////////////////////////////////////////////////////////////////// s32 MBNG_CV_NotifyReceivedValue(mbng_event_item_t *item) { u16 hw_id = item->hw_id; if( debug_verbose_level >= DEBUG_VERBOSE_LEVEL_INFO ) { DEBUG_MSG("MBNG_CV_NotifyReceivedValue(%d, %d)\n", hw_id & 0xfff, item->value); } // forward to CV channel u16 hw_id_ix = hw_id & 0xfff; if( hw_id_ix && hw_id_ix <= MBNG_PATCH_NUM_CV_CHANNELS ) { u8 cv_ix = hw_id_ix - 1; u16 value = item->value; u8 set_value = 1; u8 gate_value = 0; if( item->flags.type == MBNG_EVENT_TYPE_NOTE_ON ) { if( item->flags.use_key_or_cc ) { if( item->secondary_value > 0 ) { // push note into note stack NOTESTACK_Push(&cv_notestack[cv_ix], value, item->secondary_value); } else { // remove note from note stack NOTESTACK_Pop(&cv_notestack[cv_ix], value); } // still a note in stack? if( cv_notestack[cv_ix].len ) { // take first note of stack value = cv_notestack_items[cv_ix][0].note; u8 velocity = cv_notestack_items[cv_ix][0].tag; // TODO: should we provide an option to forward the notestack based velocity to the appr. EVENT_CV item? gate_value = 1; if( debug_verbose_level >= DEBUG_VERBOSE_LEVEL_INFO ) { DEBUG_MSG("-> Note %3d with velocity %3d\n", value, velocity); } } else { gate_value = 0; if( debug_verbose_level >= DEBUG_VERBOSE_LEVEL_INFO ) { DEBUG_MSG("-> Note %3d released the gate\n", value); } } } else { if( item->value == 0 ) { if( debug_verbose_level >= DEBUG_VERBOSE_LEVEL_INFO ) { DEBUG_MSG("-> Velocity 0 won't change this channel!\n"); } set_value = 0; } else { if( debug_verbose_level >= DEBUG_VERBOSE_LEVEL_INFO ) { DEBUG_MSG("-> Velocity set to %3d\n", item->value); } } } } if( set_value ) { // scale value to 16bit u16 value16 = value; s32 mapped_value; if( (mapped_value=MBNG_EVENT_MapValue(item->map, value16, 65535, 0)) >= 0 ) { value16 = mapped_value; } else if( item->min <= item->max ) { int range = item->max - item->min + 1; value16 = (value - item->min) * (65536 / range); } else { int range = item->min - item->max + 1; value16 = (value - item->max)* (65536 / range); } if( value16 < 0 ) value16 = 0; else if( value16 > 65535 ) value16 = 65535; // change output curve AOUT_ConfigChannelInvertedSet(cv_ix, item->custom_flags.CV.cv_inverted); AOUT_ConfigChannelHzVSet(cv_ix, item->custom_flags.CV.cv_hz_v); // set CV value MBNG_CV_PinSet(cv_ix, value16); // gate inverted? if( item->custom_flags.CV.cv_gate_inverted ) { gate_value ^= 1; } // set gates AOUT_DigitalPinSet(cv_ix, gate_value); if( item->custom_flags.CV.fwd_gate_to_dout_pin ) { if( debug_verbose_level >= DEBUG_VERBOSE_LEVEL_INFO ) { DEBUG_MSG("-> Setting DOUT Pin #%d=%d\n", item->custom_flags.CV.fwd_gate_to_dout_pin-1, gate_value); } MIOS32_DOUT_PinSet(item->custom_flags.CV.fwd_gate_to_dout_pin-1, gate_value); } } } return 0; // no error }