/////////////////////////////////////////////////////////////////////////////
// Local encoder callback function
// Should return:
//   1 if value has been changed
//   0 if value hasn't been changed
//  -1 if invalid or unsupported encoder
/////////////////////////////////////////////////////////////////////////////
static s32 Encoder_Handler(seq_ui_encoder_t encoder, s32 incrementer)
{
#if 0
  // leads to: comparison is always true due to limited range of data type
  if( (encoder >= SEQ_UI_ENCODER_GP1 && encoder <= SEQ_UI_ENCODER_GP16) ) {
#else
  if( encoder <= SEQ_UI_ENCODER_GP16 ) {
#endif
    if( seq_ui_button_state.SELECT_PRESSED && !seq_ui_button_state.CHANGE_ALL_STEPS ) {
	// select button pressed: indirect MUTED flag modification (taken over when select button depressed)
	u16 mask = 1 << encoder;
	if( incrementer < 0 || (incrementer == 0 && !(latched_mute & mask)) )
	  latched_mute |= mask;
	else
	  latched_mute &= ~mask;
    } else {
      // select button not pressed: direct MUTED flag modification
      // access to seq_core_trk[] must be atomic!
      portENTER_CRITICAL();

      u8 visible_track = SEQ_UI_VisibleTrackGet();
      u16 mask = 1 << encoder;
      u16 *muted = (u16 *)&seq_core_trk_muted;

      if( seq_ui_button_state.CHANGE_ALL_STEPS ) {
	switch( encoder ) {
	case SEQ_UI_ENCODER_GP1:
	case SEQ_UI_ENCODER_GP2:
	  *muted = 0xffff;
	  SEQ_UI_Msg(SEQ_UI_MSG_USER, 1000, "All Tracks", "muted");
	  break;

	case SEQ_UI_ENCODER_GP3:
	case SEQ_UI_ENCODER_GP4:
	case SEQ_UI_ENCODER_GP5:
	  muted = (u16 *)&seq_core_trk[visible_track].layer_muted;
	  *muted = 0xffff;
	  SEQ_UI_Msg(SEQ_UI_MSG_USER, 1000, "All Layers", "of current Track muted");
	  break;

	case SEQ_UI_ENCODER_GP6:
	case SEQ_UI_ENCODER_GP7:
	case SEQ_UI_ENCODER_GP8: {
	  *muted = 0xffff;

	  int track;
	  for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) {
	    seq_core_trk[track].layer_muted = 0xffff;
	  }
	  SEQ_UI_Msg(SEQ_UI_MSG_USER, 1000, "All Layers", "and Tracks muted");
	} break;

	case SEQ_UI_ENCODER_GP9:
	case SEQ_UI_ENCODER_GP10:
	  *muted = 0x0000;
	  SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "All Tracks", "unmuted");
	  break;

	case SEQ_UI_ENCODER_GP11:
	case SEQ_UI_ENCODER_GP12:
	case SEQ_UI_ENCODER_GP13:
	  muted = (u16 *)&seq_core_trk[visible_track].layer_muted;
	  *muted = 0x0000;
	  SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "All Layers", "of current Track unmuted");
	  break;

	case SEQ_UI_ENCODER_GP14:
	case SEQ_UI_ENCODER_GP15:
	case SEQ_UI_ENCODER_GP16: {
	  *muted = 0x0000;

	  int track;
	  for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) {
	    seq_core_trk[track].layer_muted = 0x0000;
	  }
	  SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 1000, "All Layers", "and Tracks unmuted");
	} break;
	}
      } else {
	if( seq_ui_button_state.MUTE_PRESSED )
	  muted = (u16 *)&seq_core_trk[visible_track].layer_muted;
	else if( SEQ_BPM_IsRunning() ) { // Synched Mutes only when sequencer is running
	  if( !(*muted & mask) && seq_core_options.SYNCHED_MUTE && !seq_ui_button_state.FAST_ENCODERS ) { // Fast button will disable synched mute
	    muted = (u16 *)&seq_core_trk_synched_mute;
	  } else if( (*muted & mask) && seq_core_options.SYNCHED_UNMUTE && !seq_ui_button_state.FAST_ENCODERS ) { // Fast button will disable synched unmute
	    muted = (u16 *)&seq_core_trk_synched_unmute;
	  }
	} else {
	  // clear synched mutes/unmutes if sequencer not running
	  seq_core_trk_synched_mute = 0;
	  seq_core_trk_synched_unmute = 0;
	}
	  
	if( incrementer < 0 )
	  *muted |= mask;
	else if( incrementer > 0 )
	  *muted &= ~mask;
	else
	  *muted ^= mask;
      }

      portEXIT_CRITICAL();

      if( muted == ((u16 *)&seq_core_trk_muted) ) {
	// send to external
	SEQ_MIDI_IN_ExtCtrlSend(SEQ_MIDI_IN_EXT_CTRL_MUTES, (*muted & mask) ? 127 : 0, encoder);
      }
    }

    return 1; // value changed
  }

  return -1; // invalid or unsupported encoder
}


/////////////////////////////////////////////////////////////////////////////
// Local button callback function
// Should return:
//   1 if value has been changed
//   0 if value hasn't been changed
//  -1 if invalid or unsupported button
/////////////////////////////////////////////////////////////////////////////
static s32 Button_Handler(seq_ui_button_t button, s32 depressed)
{
#if 0
  // leads to: comparison is always true due to limited range of data type
  if( button >= SEQ_UI_BUTTON_GP1 && button <= SEQ_UI_BUTTON_GP16 ) {
#else
  if( button <= SEQ_UI_BUTTON_GP16 ) {
#endif
    if( depressed ) return 0; // ignore when button depressed

    // re-using encoder routine
    return Encoder_Handler(button, 0);
  }

  switch( button ) {
    case SEQ_UI_BUTTON_Select:
      portENTER_CRITICAL();
      if( depressed ) {
	// select button released: take over latched mutes
	if( seq_ui_button_state.MUTE_PRESSED ) {
	  u8 visible_track = SEQ_UI_VisibleTrackGet();
	  seq_core_trk[visible_track].layer_muted = latched_mute;
	} else {
	  u16 new_mutes = latched_mute & ~seq_core_trk_muted;
	  if( SEQ_BPM_IsRunning() && seq_core_options.SYNCHED_MUTE && !seq_ui_button_state.FAST_ENCODERS ) // Fast button will disable synched mute
	    seq_core_trk_synched_mute |= new_mutes;
	  else
	    seq_core_trk_muted |= new_mutes;

	  u16 new_unmutes = ~latched_mute & seq_core_trk_muted;
	  if( SEQ_BPM_IsRunning() && seq_core_options.SYNCHED_UNMUTE && !seq_ui_button_state.FAST_ENCODERS ) // Fast button will disable synched unmute
	    seq_core_trk_synched_unmute |= new_unmutes;
	  else
	    seq_core_trk_muted &= ~new_unmutes;
	}
      } else {
	// select pressed: init latched mutes which will be taken over once SELECT button released
	if( seq_ui_button_state.MUTE_PRESSED ) {
	  u8 visible_track = SEQ_UI_VisibleTrackGet();
	  latched_mute = seq_core_trk[visible_track].layer_muted;
	} else {
	  latched_mute = seq_core_trk_muted;
	}
      }

      portEXIT_CRITICAL();
      return 1;
  }

  return -1; // invalid or unsupported button
}


/////////////////////////////////////////////////////////////////////////////
// Local Display Handler function
// IN: <high_prio>: if set, a high-priority LCD update is requested
/////////////////////////////////////////////////////////////////////////////
static s32 LCD_Handler(u8 high_prio)
{
  // layout:
  // 00000000001111111111222222222233333333330000000000111111111122222222223333333333
  // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  // <--------------------------------------><-------------------------------------->
  //  > 1<   2    3    4    5    6    7    8    9   10   11   12   13   14   15   16 
  // ...horizontal VU meters...

  // Mute All screen:
  // 00000000001111111111222222222233333333330000000000111111111122222222223333333333
  // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
  // <--------------------------------------><-------------------------------------->
  //    Mute       Mute       Mute all Tracks  Unmute      Unmute   Unmute all Tracks
  // all Tracks G1T1 Layers    and all Layersall Tracks G1T1 Layers    and all Layers

  if( seq_ui_button_state.CHANGE_ALL_STEPS ) {
    if( high_prio )
      return 0;

    SEQ_LCD_CursorSet(0, 0);
    SEQ_LCD_PrintString("   Mute       Mute       Mute all Tracks  Unmute      Unmute   Unmute all Tracks");
    SEQ_LCD_CursorSet(0, 1);
    SEQ_LCD_PrintString("all Tracks ");
    SEQ_LCD_PrintGxTy(ui_selected_group, ui_selected_tracks);
    SEQ_LCD_PrintString(" Layers    and all Layersall Tracks ");
    SEQ_LCD_PrintGxTy(ui_selected_group, ui_selected_tracks);
    SEQ_LCD_PrintString(" Layers    and all Layers");

    return 0;
  }

  if( high_prio ) {
    ///////////////////////////////////////////////////////////////////////////
    // frequently update VU meters
    SEQ_LCD_CursorSet(0, 1);

    u8 track;
    u16 mute_flags = 0;
    u16 mute_flags_from_midi = 0;

    if( !ui_cursor_flash && seq_ui_button_state.SELECT_PRESSED ) {
      mute_flags = latched_mute;
    } else {
      if( seq_ui_button_state.MUTE_PRESSED ) {
	u8 visible_track = SEQ_UI_VisibleTrackGet();
	mute_flags = seq_core_trk[visible_track].layer_muted;
	mute_flags_from_midi = seq_core_trk[visible_track].layer_muted_from_midi;
      } else {
	mute_flags = seq_core_trk_muted;
      }
    }

    if( seq_ui_button_state.MUTE_PRESSED ) {
      u8 layer;
      u16 mask = (1 << 0);
      for(layer=0; layer<16; ++layer, mask <<= 1)
	if( mute_flags_from_midi & mask ) {
	  SEQ_LCD_PrintString("MIDI ");
	} else if( mute_flags & mask ) {
	  SEQ_LCD_PrintString("Mute ");
	} else {
	  SEQ_LCD_PrintHBar((seq_layer_vu_meter[layer] >> 3) & 0xf);
	}
    } else {
      int remaining_steps = (seq_core_steps_per_measure - seq_core_state.ref_step) + 1;
      seq_core_trk_t *t = &seq_core_trk[0];
      u16 mask = (1 << 0);
      for(track=0; track<16; ++t, ++track, mask <<= 1)
	if( mute_flags & mask ) {
	  if( !seq_ui_button_state.SELECT_PRESSED && (seq_core_trk_synched_unmute & mask) ) {
	    SEQ_LCD_PrintFormattedString("U%3d ", remaining_steps);
	  } else {
	    SEQ_LCD_PrintString("Mute ");
	  }
	} else {
	  if( !seq_ui_button_state.SELECT_PRESSED && (seq_core_trk_synched_mute & mask) ) {
	    SEQ_LCD_PrintFormattedString("M%3d ", remaining_steps);
	  } else {
	    SEQ_LCD_PrintHBar(t->vu_meter >> 3);
	  }
	}
    }
  } else {
/////////////////////////////////////////////////////////////////////////////
// Local encoder callback function
// Should return:
//   1 if value has been changed
//   0 if value hasn't been changed
//  -1 if invalid or unsupported encoder
/////////////////////////////////////////////////////////////////////////////
static s32 Encoder_Handler(seq_ui_encoder_t encoder, s32 incrementer)
{
  seq_midi_router_node_t *n = &seq_midi_router_node[selected_router_node];

  if( encoder <= SEQ_UI_ENCODER_GP8 ) {
    switch( encoder ) {
    case SEQ_UI_ENCODER_GP1:
    case SEQ_UI_ENCODER_GP2:
      selected_subpage = SUBPAGE_TRANSPOSE;
      ui_selected_item = ITEM_IN_BUS;
      break;

    case SEQ_UI_ENCODER_GP3:
    case SEQ_UI_ENCODER_GP4:
      selected_subpage = SUBPAGE_SECTIONS;
      ui_selected_item = ITEM_S_PORT;
      break;

    case SEQ_UI_ENCODER_GP5:
    case SEQ_UI_ENCODER_GP6:
      selected_subpage = SUBPAGE_ROUTER;
      ui_selected_item = ITEM_R_NODE;
      break;

    case SEQ_UI_ENCODER_GP7:
      selected_subpage = SUBPAGE_EXT_CTRL;
      ui_selected_item = ITEM_EXT_PORT;
      break;

    default:
      selected_subpage = SUBPAGE_MISC;
      ui_selected_item = ITEM_BLM_SCALAR_PORT;
    }

    return 1;
  }

  switch( selected_subpage ) {
    case SUBPAGE_TRANSPOSE:
      switch( encoder ) {
        case SEQ_UI_ENCODER_GP9:
	  ui_selected_item = ITEM_IN_BUS;
	  break;

        case SEQ_UI_ENCODER_GP10:
	  ui_selected_item = ITEM_IN_PORT;
	  break;

        case SEQ_UI_ENCODER_GP11:
	  ui_selected_item = ITEM_IN_CHN;
	  break;

        case SEQ_UI_ENCODER_GP12:
	  ui_selected_item = ITEM_IN_LOWER;
	  break;

        case SEQ_UI_ENCODER_GP13:
	  ui_selected_item = ITEM_IN_UPPER;
	  break;

        case SEQ_UI_ENCODER_GP14:
	  ui_selected_item = ITEM_IN_MODE;
	  break;

        case SEQ_UI_ENCODER_GP15:
	  return -1; // not mapped

        case SEQ_UI_ENCODER_GP16:
	  ui_selected_item = ITEM_RESET_STACKS;
	  break;
      }
      break;

    case SUBPAGE_SECTIONS:
      switch( encoder ) {
        case SEQ_UI_ENCODER_GP9:
	  ui_selected_item = ITEM_S_PORT;
	  break;

        case SEQ_UI_ENCODER_GP10:
	  ui_selected_item = ITEM_S_CHN;
	  break;

        case SEQ_UI_ENCODER_GP11:
	  ui_selected_item = ITEM_S_OCT_G1;
	  break;

        case SEQ_UI_ENCODER_GP12:
	  ui_selected_item = ITEM_S_OCT_G2;
	  break;

        case SEQ_UI_ENCODER_GP13:
	  ui_selected_item = ITEM_S_OCT_G3;
	  break;

        case SEQ_UI_ENCODER_GP14:
	  ui_selected_item = ITEM_S_OCT_G4;
	  break;

        case SEQ_UI_ENCODER_GP15:
	  ui_selected_item = ITEM_S_FWD_PORT;
	  break;

        case SEQ_UI_ENCODER_GP16:
	  ui_selected_item = ITEM_S_RESET_STACKS;
	  break;
      }
      break;

    case SUBPAGE_ROUTER:
      switch( encoder ) {
        case SEQ_UI_ENCODER_GP9:
	  ui_selected_item = ITEM_R_NODE;
	  break;

        case SEQ_UI_ENCODER_GP10:
	  ui_selected_item = ITEM_R_SRC_PORT;
	  break;

        case SEQ_UI_ENCODER_GP11:
	  ui_selected_item = ITEM_R_SRC_CHN;
	  break;

        case SEQ_UI_ENCODER_GP12:
	  ui_selected_item = ITEM_R_DST_PORT;
	  break;

        case SEQ_UI_ENCODER_GP13:
	  ui_selected_item = ITEM_R_DST_CHN;
	  break;

        case SEQ_UI_ENCODER_GP14:
	  return -1; // not used (yet)

        case SEQ_UI_ENCODER_GP15:
        case SEQ_UI_ENCODER_GP16:
	  ui_selected_item = ITEM_DEF_PORT;
	  break;
      }
      break;

    case SUBPAGE_EXT_CTRL:
      switch( encoder ) {
        case SEQ_UI_ENCODER_GP9:
	  ui_selected_item = ITEM_EXT_PORT;
	  break;

        case SEQ_UI_ENCODER_GP10:
	  ui_selected_item = ITEM_EXT_PORT_OUT;
	  break;

        case SEQ_UI_ENCODER_GP11:
	  ui_selected_item = ITEM_EXT_CHN;
	  break;

        case SEQ_UI_ENCODER_GP12:
        case SEQ_UI_ENCODER_GP13:
        case SEQ_UI_ENCODER_GP14:
	  ui_selected_item = ITEM_EXT_CTRL;
	  break;

        case SEQ_UI_ENCODER_GP15:
        case SEQ_UI_ENCODER_GP16:
	  ui_selected_item = ITEM_EXT_VALUE;
	  break;
      }
      break;

    case SUBPAGE_MISC:
      switch( encoder ) {
        case SEQ_UI_ENCODER_GP9:
        case SEQ_UI_ENCODER_GP10:
	  ui_selected_item = ITEM_BLM_SCALAR_PORT;
	  break;

        case SEQ_UI_ENCODER_GP11:
        case SEQ_UI_ENCODER_GP12:
        case SEQ_UI_ENCODER_GP13:
        case SEQ_UI_ENCODER_GP14:
	  return -1; // not used (yet)

        case SEQ_UI_ENCODER_GP15:
        case SEQ_UI_ENCODER_GP16:
	  // enter midi monitor page
	  SEQ_UI_PageSet(SEQ_UI_PAGE_MIDIMON);
	  return 1;
      }
      break;

    default:
      return -1; // unsupported subpage
  }

  // for GP encoders and Datawheel
  switch( ui_selected_item ) {
    case ITEM_IN_BUS: {
      if( SEQ_UI_Var8_Inc(&selected_bus, 0, SEQ_MIDI_IN_NUM_BUSSES-1, incrementer) >= 0 ) {
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_IN_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_InIxGet(seq_midi_in_port[selected_bus]);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_InNumGet()-1-4, incrementer) >= 0 ) { // don't allow selection of Bus1..Bus4
	seq_midi_in_port[selected_bus] = SEQ_MIDI_PORT_InPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_IN_CHN:
      if( SEQ_UI_Var8_Inc(&seq_midi_in_channel[selected_bus], 0, 16, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_IN_LOWER:
      if( SEQ_UI_Var8_Inc(&seq_midi_in_lower[selected_bus], 0, 127, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_IN_UPPER:
      if( SEQ_UI_Var8_Inc(&seq_midi_in_upper[selected_bus], 0, 127, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_IN_MODE: {
      u8 fwd = seq_midi_in_options[selected_bus].MODE_PLAY;
      if( SEQ_UI_Var8_Inc(&fwd, 0, 1, incrementer) >= 0 ) {
	seq_midi_in_options[selected_bus].MODE_PLAY = fwd;
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_RESET_STACKS: {
      SEQ_MIDI_IN_ResetTransArpStacks();
      SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 2000, "Transposer/Arp.", "Stacks cleared!");

      // send to external
      SEQ_MIDI_IN_ExtCtrlSend(SEQ_MIDI_IN_EXT_CTRL_ALL_NOTES_OFF, 127, 0);

      return 1;
    } break;


    case ITEM_S_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_InIxGet(seq_midi_in_sect_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_InNumGet()-1, incrementer) >= 0 ) {
	seq_midi_in_sect_port = SEQ_MIDI_PORT_InPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_S_CHN:
      if( SEQ_UI_Var8_Inc(&seq_midi_in_sect_channel, 0, 16, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change


    case ITEM_S_OCT_G1: {
      u8 oct = seq_midi_in_sect_note[0] / 12;
      if( SEQ_UI_Var8_Inc(&oct, 0, 11, incrementer) >= 0 ) {
	seq_midi_in_sect_note[0] = 12*oct;
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_S_OCT_G2: {
      u8 oct = seq_midi_in_sect_note[1] / 12;
      if( SEQ_UI_Var8_Inc(&oct, 0, 11, incrementer) >= 0 ) {
	seq_midi_in_sect_note[1] = 12*oct;
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_S_OCT_G3: {
      u8 oct = seq_midi_in_sect_note[2] / 12;
      if( SEQ_UI_Var8_Inc(&oct, 0, 11, incrementer) >= 0 ) {
	seq_midi_in_sect_note[2] = 12*oct;
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_S_OCT_G4: {
      u8 oct = seq_midi_in_sect_note[3] / 12;
      if( SEQ_UI_Var8_Inc(&oct, 0, 11, incrementer) >= 0 ) {
	seq_midi_in_sect_note[3] = 12*oct;
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_S_FWD_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_OutIxGet(seq_midi_in_sect_fwd_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_OutNumGet()-1, incrementer) >= 0 ) {
	seq_midi_in_sect_fwd_port = SEQ_MIDI_PORT_OutPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_S_RESET_STACKS: {
      SEQ_MIDI_IN_ResetChangerStacks();
      SEQ_UI_Msg(SEQ_UI_MSG_USER_R, 2000, "Section Changer", "Stacks cleared!");

      // send to external
      SEQ_MIDI_IN_ExtCtrlSend(SEQ_MIDI_IN_EXT_CTRL_ALL_NOTES_OFF, 127, 0);

      return 1;
    } break;



    case ITEM_R_NODE:
      if( SEQ_UI_Var8_Inc(&selected_router_node, 0, SEQ_MIDI_ROUTER_NUM_NODES-1, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_R_SRC_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_InIxGet(n->src_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_InNumGet()-1, incrementer) >= 0 ) {
	n->src_port = SEQ_MIDI_PORT_InPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_R_SRC_CHN:
      if( SEQ_UI_Var8_Inc(&n->src_chn, 0, 17, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_R_DST_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_OutIxGet(n->dst_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_OutNumGet()-1, incrementer) >= 0 ) {
	n->dst_port = SEQ_MIDI_PORT_OutPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_R_DST_CHN:
      if( SEQ_UI_Var8_Inc(&n->dst_chn, 0, 19, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change


    case ITEM_DEF_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_OutIxGet(MIOS32_MIDI_DefaultPortGet());
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_OutNumGet()-1, incrementer) >= 0 ) {
	MIOS32_MIDI_DefaultPortSet(SEQ_MIDI_PORT_OutPortGet(port_ix));
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_EXT_PORT: {
      u8 numPorts = SEQ_MIDI_PORT_InNumGet();
      u8 port_ix = (seq_midi_in_ext_ctrl_port == 0xff) ? numPorts : SEQ_MIDI_PORT_InIxGet(seq_midi_in_ext_ctrl_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, numPorts, incrementer) >= 0 ) {
	seq_midi_in_ext_ctrl_port = (port_ix == numPorts) ? 0xff : SEQ_MIDI_PORT_InPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_EXT_PORT_OUT: {
      u8 port_ix = SEQ_MIDI_PORT_OutIxGet(seq_midi_in_ext_ctrl_out_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_OutNumGet()-1, incrementer) >= 0 ) {
	seq_midi_in_ext_ctrl_out_port = SEQ_MIDI_PORT_OutPortGet(port_ix);
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;

    case ITEM_EXT_CHN:
      if( SEQ_UI_Var8_Inc(&seq_midi_in_ext_ctrl_channel, 0, 16, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_EXT_CTRL:
      if( SEQ_UI_Var8_Inc(&selected_ext_ctrl, 0, SEQ_MIDI_IN_EXT_CTRL_NUM-1, incrementer) >= 0 ) {
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change

    case ITEM_EXT_VALUE:
      if( selected_ext_ctrl == SEQ_MIDI_IN_EXT_CTRL_NRPN_ENABLED ) {
	if( SEQ_UI_Var8_Inc(&seq_midi_in_ext_ctrl_asg[selected_ext_ctrl], 0, 1, incrementer) >= 0 ) {
	  store_file_required = 1;
	  return 1; // value changed
	}
      } else if( selected_ext_ctrl == SEQ_MIDI_IN_EXT_CTRL_PC_MODE ) {
	if( SEQ_UI_Var8_Inc(&seq_midi_in_ext_ctrl_asg[selected_ext_ctrl], 0, SEQ_MIDI_IN_EXT_CTRL_PC_MODE_NUM-1, incrementer) >= 0 ) {
	  store_file_required = 1;
	  return 1; // value changed
	}
      } else if( selected_ext_ctrl <= SEQ_MIDI_IN_EXT_CTRL_NUM ) {
	if( SEQ_UI_Var8_Inc(&seq_midi_in_ext_ctrl_asg[selected_ext_ctrl], 0, 128, incrementer) >= 0 ) {
	  store_file_required = 1;
	  return 1; // value changed
	}
      }
      return 0; // no change

    case ITEM_BLM_SCALAR_PORT: {
      u8 port_ix = SEQ_MIDI_PORT_InIxGet(seq_blm_port);
      if( SEQ_UI_Var8_Inc(&port_ix, 0, SEQ_MIDI_PORT_InNumGet()-1, incrementer) >= 0 ) {
	seq_blm_port = SEQ_MIDI_PORT_InPortGet(port_ix);
	MUTEX_MIDIOUT_TAKE;
	blm_timeout_ctr = 0; // fake timeout (so that "BLM not found" message will be displayed)
	SEQ_BLM_SYSEX_SendRequest(0x00); // request layout from BLM_SCALAR
	MUTEX_MIDIOUT_GIVE;
	store_file_required = 1;
	return 1; // value changed
      }
      return 0; // no change
    } break;
  }

  return -1; // invalid or unsupported encoder
}
Example #3
0
/////////////////////////////////////////////////////////////////////////////
// fetches the pos entries of a song
// returns -1 if recursion counter reached max position
/////////////////////////////////////////////////////////////////////////////
s32 SEQ_SONG_FetchPos(u8 force_immediate_change, u8 dont_dump_mixer_map)
{
  int recursion_ctr = 0;

  u8 again;
  do {
    again = 0;

    // stop song once recursion counter reached 64 loops
    if( ++recursion_ctr >= 64 ) {
      SEQ_BPM_Stop();
      return -1; // recursion detected
    }

    // reset song position if we reached the end (loop over 128 steps)
    if( song_pos >= SEQ_SONG_NUM_STEPS )
      song_pos = 0;

    // get step entry
    seq_song_step_t *s = (seq_song_step_t *)&seq_song_steps[song_pos];

    // branch depending on action
    switch( s->action ) {
      case SEQ_SONG_ACTION_End:
#if 0
	if( song_active ) // not in phrase mode
	  SEQ_BPM_Stop();
#else
	song_finished = 1; // deactivate song incrementer
#endif
	break;

      case SEQ_SONG_ACTION_JmpPos:
	song_pos = s->action_value % SEQ_SONG_NUM_STEPS;
	again = 1;
	break;

      case SEQ_SONG_ACTION_JmpSong: {
	if( song_active ) { // not in phrase mode
	  u32 new_song_num = s->action_value % SEQ_SONG_NUM;

	  SEQ_SONG_Save(song_num);
	  SEQ_SONG_Load(new_song_num);
	  song_pos = 0;
	  again = 1;
	}
      } break;

      case SEQ_SONG_ACTION_SelMixerMap:
	SEQ_MIXER_Load(s->action_value);
	SEQ_MIDI_IN_ExtCtrlSend(SEQ_MIDI_IN_EXT_CTRL_MIXER_MAP, s->action_value, 0);
	if( !dont_dump_mixer_map )
	  SEQ_MIXER_SendAll();
	++song_pos;
	again = 1;
	break;

      case SEQ_SONG_ACTION_Tempo: {
	float bpm = (float)s->action_value;
	if( bpm < 25.0 )
	  bpm = 25.0;
	float ramp = (float)s->pattern_g1;
	SEQ_CORE_BPM_Update(bpm, ramp);
	++song_pos;
	again = 1;
      } break;

      case SEQ_SONG_ACTION_Mutes: {
	// access to seq_core_trk[] must be atomic!
	portENTER_CRITICAL();

	seq_core_trk_muted =
	  ((s->pattern_g1 & 0x0f) <<  0) |
	  ((s->pattern_g2 & 0x0f) <<  4) |
	  ((s->pattern_g3 & 0x0f) <<  8) |
	  ((s->pattern_g4 & 0x0f) << 12);

	portEXIT_CRITICAL();

	++song_pos;
	again = 1;
      } break;

      case SEQ_SONG_ACTION_GuideTrack: {
	if( s->action_value <= 16 )
	  seq_song_guide_track = s->action_value;
	++song_pos;
	again = 1;
      } break;

      default:
	if( s->action >= SEQ_SONG_ACTION_Loop1 && s->action <= SEQ_SONG_ACTION_Loop16 ) {
	  song_loop_ctr = 0;
	  song_loop_ctr_max = s->action - SEQ_SONG_ACTION_Loop1;

	  // TODO: implement prefetching until end of step!
	  
	  SEQ_SONG_FetchHlp_PatternChange(0, s->pattern_g1, s->bank_g1, force_immediate_change);
	  SEQ_SONG_FetchHlp_PatternChange(1, s->pattern_g2, s->bank_g2, force_immediate_change);
	  SEQ_SONG_FetchHlp_PatternChange(2, s->pattern_g3, s->bank_g3, force_immediate_change);
	  SEQ_SONG_FetchHlp_PatternChange(3, s->pattern_g4, s->bank_g4, force_immediate_change);
	}
    }

  } while( again );

  return 0; // no error
}