'------------------------------------------------------------------------------------------------------------
' SOUNDRA STEREO :: CONTROLS.bas
'------------------------------------------------------------------------------------------------------------
DEC FCT BOARDS_VIEW           (AS IOS) AS I32
DEC SUB BB_KEYS               (AS I32, AS _CTRL PTR,AS I32)
DEC SUB KB_KEYS               (AS I32, AS _CTRL PTR,AS I32)
'GENERIC
DEC FCT ctrl_main             (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_link             (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_text_area        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'FRACTION-AID
DEC FCT ctrl_fa_edit          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_fa_mode          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_fa_value         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_fa_binary        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_fa_unit          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'SYSTEM BAR
DEC FCT ctrl_quit             (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_min              (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_win_tabs         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_mic_power        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_play_power       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_record           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_draw_mode        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_synth_edit       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_synth_multi      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_synth_center     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
     DEC FCT ctrl_undo             (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
     DEC FCT ctrl_redo             (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_map_MIDI0        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_boards_visibility(AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_text_field       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_dialog_ok        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_dialog_x         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'TOOLS WINDOW
DEC FCT ctrl_rewind           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_seed             (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_module_flags     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
     DEC FCT ctrl_module_S0        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
     DEC FCT ctrl_module_S1        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_map_MIDI1        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_map_MIDI2        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDI_MIN         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDI_MAX         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDI_RES         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_param_tabs       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_param_global     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_param_vmap       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_param_schan      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_param_s_link     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_shape_graph      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_param_list       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_shape_list       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_list_m    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_set       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_save      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_del       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_list      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_graph     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_win       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_size      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_slope     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_HI        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_add       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_del       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_filter_set       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_melody_list      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_melody_save      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_melody_del       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_melody_set       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDIKB_CHAN      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDIKB_DEVLIST   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sp_list          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'BROWSER WINDOW
DEC FCT ctrl_browser_tabs     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_list_order       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_li_ren           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_li_copy          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_li_del           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_li_save          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_li_load          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_new_synth        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'MESSAGE CONSOLE
DEC FCT ctrl_console_tabs     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32 
DEC FCT ctrl_system_list      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_action_list      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'FILE BROWSER
DEC FCT ctrl_file_up          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_file_home        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_file_ref         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_file_drives      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_file_list        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'LIBRARIES
DEC FCT ctrl_mod_tabs         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_reference_list   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_library_list     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_mode      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_chan      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_sample_list      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDICT           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDICC_CHAN      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDI_DEVS_list   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_MIDICC_list      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'BEATBOARD
DEC FCT ctrl_bb_num           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_bb_lock          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_bb_mode          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_bb               (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'KEYBOARD
DEC FCT ctrl_kb_num           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_kb_lock          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_kb_mode          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC SUB KB_CR_MANAGER         ()
'SETTINGS :: A/V
DEC FCT ctrl_display_driver   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_display_set      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_display_cancel   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_resolution       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_monitors         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_resolution_set   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_resolution_cancel(AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_GUI_zoom         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_GUI_zoom_set     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_GUI_zoom_cancel  (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_output_driver    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_output_set       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_output_cancel    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_outputbs         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_outputbs_set     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_outputbs_cancel  (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_input_driver     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_input_set        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_input_cancel     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'SETTINGS :: SYSTEM
DEC FCT ctrl_setting_toggle   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_language         (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_language_set     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_language_cancel  (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'SETTINGS :: TUNING & TIME SCALE
DEC FCT ctrl_tuning_keys      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_sp        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_equal     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_geo       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_keym      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_keyp      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_RK        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_sp_list   (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_sp_add    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_sp_del    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_sp_ren    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_new       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_set       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_tuning_cancel    (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_measure          (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_time_base        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_ts_set           (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_ts_cancel        (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'SYNTH
DEC FCT SYNTH_OUT             ()                                      AS I32
DEC FCT ctrl_synth_plane      (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_module_group     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_module_patch     (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
DEC FCT ctrl_pp_display       (AS I32,AS I32,AS I32,AS I32,AS I32=0)  AS I32
'------------------------------------------------------------------------------------------------------------
#MACRO SLIDEL_CONTROL(ctrl,list_size,spatial,dimension,VERTICAL)
     STATIC AS I32 dx_cumul
     DIM    AS I32 lstep,center,INCREMENT
     DIM    AS IOS UPDATED
     center = .5*dimension
     IF (ACTION AND CA_SLIDE) THEN
          IF VERTICAL THEN spatial = -spatial
          dx_cumul      += spatial
          lstep          = dx_cumul / SLIDEL_RATE
          INCREMENT      = SGN(lstep)
          ctrl->text.si += INCREMENT
          dx_cumul      -= SLIDEL_RATE*lstep
          top_cycle(ctrl->text.si,list_size)
          bot_cycle(ctrl->text.si,0,list_size)
          IF INCREMENT <> 0 THEN CTRL->SLIDING = TRUE : UPDATED = TRUE
     ELSEIF (ACTION AND CA_CLICK) OR ((ACTION AND CA_MU) AND (CTRL->SLIDING = TRUE)) THEN
          INCREMENT = IIF(VERTICAL,-1,1)
          IF spatial - ctrl->spatial < center THEN
               ctrl->text.si -= INCREMENT
               bot_cycle(ctrl->text.si,0,list_size)
               top_cycle(ctrl->text.si,list_size)
               INCREMENT = -1
          ELSEIF spatial - ctrl->spatial > center THEN
               ctrl->text.si += INCREMENT
               bot_cycle(ctrl->text.si,0,list_size)
               top_cycle(ctrl->text.si,list_size)
               INCREMENT = 1
          END IF
          UPDATED  = TRUE
          dx_cumul = 0
     ELSEIF (ACTION AND CA_MD) OR (ACTION AND CA_HOVER) THEN
          IF spatial - ctrl->spatial < center THEN
               ctrl->text.hi = 1
          ELSEIF spatial - ctrl->spatial > center THEN
               ctrl->text.hi = 2
          ELSE
               ctrl->text.hi = 0
          END IF
          dx_cumul = 0
     ELSE
          UPDATED = ((ACTION AND CA_MU) = TRUE)
          ctrl->text.hi = 0
          dx_cumul = 0
     END IF
     ctrl->text.UPDATED = TRUE
#ENDMACRO

#MACRO ListControl(CTRL, list_size)
     DIM    AS I32       I, nb_lines, lstep, LINE_H
     STATIC AS I32       dy_cumul
     nb_lines = CTRL->text.nb_lines
     LINE_H   = (CTRL->H - BORDER_SIZE*2) / nb_lines
     IF (ACTION AND CA_MU) THEN
          ACTION = CA_CLICK
          IF ABS(CTRL->CY - Y) >= (.67 * LINE_H) THEN ACTION = CA_HOVER
     END IF
     IF (ACTION > CA_RESET) AND (ACTION < CA_SLIDE) THEN
          'CALCULATE INDEX
          I  = -1
          Y -= CTRL->Y + BORDER_SIZE
          IF Y > 0 AND Y < CTRL->H - BORDER_SIZE THEN
               I  = Y \ LINE_H
               floor(I,0)
               I += CTRL->TEXT.vi                                     'ADD VIEW OFFSET
               I  = IIF(CTRL->text.reversed_index,list_size-I-1,I)    'INVERSE?
               IF (I > list_size - 1) OR (I < 0) THEN I = -1          'OUT OF RANGE?
          END IF
          'UPDATE VALUE
          IF (ACTION AND CA_HOVER) OR (ACTION AND CA_MD) THEN
               CTRL->TEXT.hi = I                            '-> value to use for drag&drop, not .si!
          ELSEIF CTRL->text.selectable  THEN                          'CLICK
               IF I = CTRL->TEXT.si THEN I = -1                       'UNSELECT?
               CTRL->TEXT.si = I
          END IF
          dy_cumul = 0
     ELSEIF (ACTION AND CA_SLIDE)  THEN
          LINE_H         = .67 * LINE_H
          dy_cumul      -= Y                                'dY sent as Y
          lstep          = dy_cumul \ LINE_H                'Number of lines to add to vertical offset
          dy_cumul    MOD=LINE_H                            'Keep the dy remains
          IF ABS(lstep) > 0 THEN
               CTRL->TEXT.vi     += lstep                   'Add the lines to the vertical offset
               ceil(CTRL->TEXT.vi, list_size - nb_lines)
               floor(CTRL->TEXT.vi,0)
               CTRL->TEXT.UPDATED = TRUE
               CTRL->SLIDING      = TRUE
               CTRL->CY          += lstep * (CTRL->H - BORDER_SIZE*2) / nb_lines
          END IF
     ELSEIF (ACTION AND CA_WHEEL) THEN
          LINE_H         = .67 * LINE_H
          lstep          = Y
          CTRL->TEXT.vi += lstep                            'Add the lines to the vertical offset
          ceil(CTRL->TEXT.vi, list_size - nb_lines)
          floor(CTRL->TEXT.vi,0)
          CTRL->TEXT.UPDATED = TRUE
     ELSEIF (ACTION AND CA_KB) THEN
          DIM AS I32 DIRECTION
          IF (CR.KB_OFFSET + CR.KB_NUM = 328) THEN 'UP ARROW
               DIRECTION = 1
          ELSEIF (CR.KB_OFFSET + CR.KB_NUM = 336) THEN 'DOWN ARROW
               DIRECTION = 2
          END IF
          IF ctrl->text.reversed_index = TRUE THEN DIRECTION XOR=3
          IF DIRECTION = 1 THEN
               IF ctrl->text.si = -1 THEN ctrl->text.si = 0
               ctrl->text.si -= 1
               IF ctrl->text.si <  0 THEN ctrl->text.si = list_size - 1
          ELSEIF DIRECTION = 2 THEN
               ctrl->text.si += 1
               IF ctrl->text.si = list_size THEN ctrl->text.si = 0
          END IF
          ctrl->text.update_vi(list_size)
          ctrl->text.hi = ctrl->text.si
          dy_cumul = 0
          ACTION = CA_CLICK
          CTRL->TEXT.UPDATED = TRUE
     ELSEIF (ACTION = CA_RESET) THEN
          CTRL->TEXT.hi = -1
          dy_cumul = 0
     ELSE
          dy_cumul = 0
     END IF
#ENDMACRO

'------------------------------------------------------------------------------------------------------------
' GENERIC
          'CALLED EVERY SECOND
FUNCTION ctrl_main         (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32  CC=1,I,R,ID,MB_COUNTER
     STATIC AS SOUNDRA.MODULE PTR MOD_PTR
     STATIC AS I32  NB_OD,NB_ID,NB_LOW_FPS
     STATIC AS IOS  REFRESH_AD
     DIM AS THREAD_MESSAGE TMP_MSG
     'CALLED EVERY SECOND
     
     CTHROTTLE = NOT(PLAYING) OR SETTINGS.FORCE_CTHROTTLE
     
     IF (ACTION = CA_RESET) THEN
          DIM AS IOS NB_MIDI_DEVICES_UPDATED
          
          'MIDI DEVICES - EVERY SECOND
          MUTEXLOCK   MT_MIDI
          IF NB_MIDI_DEVICES <> midiInGetNumDevs() THEN
               MIDI_AUTO_CONNECT()
               NB_MIDI_DEVICES_UPDATED = TRUE
               REFRESH_MIDI_LISTS()
          END IF
          MUTEXUNLOCK MT_MIDI

          'CHECK MODULE POWER/PLAY/RECORD BUTTON STATE - EVERY SECOND
          MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               DIM AS I32 ID
               ID = ControlAccess[CS_MOD_POWER]
               IF MOD_PTR->FLAGS(SOUNDRA.MF_POWER) <> Controls[ID]->state.POWER      THEN
                    Controls[ID]->state.POWER = MOD_PTR->FLAGS(SOUNDRA.MF_POWER)
                    Controls[ID]->UPDATE_GSTATE()
               END IF
               ID = ControlAccess[CS_MOD_CONTROLS] + MOD_PLAY
               IF MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER) <> Controls[ID]->state.POWER THEN
                    Controls[ID]->state.POWER = MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER)
                    Controls[ID]->UPDATE_GSTATE()
               END IF
               ID = ControlAccess[CS_MOD_CONTROLS] + MOD_REC
               IF MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) <> Controls[ID]->state.POWER     THEN
                    Controls[ID]->state.POWER = MOD_PTR->FLAGS(SOUNDRA.MF_RECORD)
                    Controls[ID]->CLR = IIF(MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP)=TRUE,RGB(200,100,0),0)
                    Controls[ID]->UPDATE_GDATA()
                    SOUNDRA.REFRESH_FA(SOUNDRA.SELECTED_MODULE)
               END IF
               ID = ControlAccess[CS_MOD_CONTROLS] + MOD_LOOP
               Controls[ID]->CLR = IIF(MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_LOOPING)=TRUE,RGB(200,100,0),0)
               Controls[ID]->UPDATE_GDATA()
          END IF

          'AUDIO DEVICES
          MUTEXLOCK   MT_AUDIO
          DEVICES_UPDATED      = FALSE
          REFRESH_AD           = AUDIO_DEVICES_CHANGE
          AUDIO_DEVICES_CHANGE = FALSE
          MUTEXUNLOCK MT_AUDIO
          IF REFRESH_AD = TRUE THEN
               TMP_MSG.TIME    = TIMER
               TMP_MSG.code    = _REMOTE_CTRL
               TMP_MSG.dest_id = ControlAccess[CS_AOINFOS]+3 'CANCEL BUTTON
               TMP_MSG.ACTION  = CA_CLICK
               SENDMTMSG(TMP_MSG,,FALSE)
               TMP_MSG.dest_id = ControlAccess[CS_AIINFOS]+3 'CANCEL BUTTON
               SENDMTMSG(TMP_MSG)
          END IF

          'MONITORS - EVERY 3 SECONDS
          IF CC MOD 3 = 0 THEN
               'FB.GET_SCREEN_SIZE      ACTUAL SOUNDRA'S WINDOW SIZE
               'FB.GET_DESKTOP_SIZE     SIZE OF BIGGEST MONITOR ONLY, NOT FULL EXTENDED SIZE
               MUTEXLOCK   MT_VIDEO
               NB_MONITORS = GetSystemMetrics(80)
               ScreenControl FB.GET_DESKTOP_SIZE,DESK_W,DESK_H
               MUTEXUNLOCK MT_VIDEO
               IF (DESK_W*NB_MONITORS < SCR_W) OR (DESK_H < SCR_H) THEN 'CHANGE RES => RESET POS
                    DIM AS THREAD_MESSAGE TMP_MSG
                    TMP_MSG.CODE = _RES_CHANGE
                    TMP_MSG.TIME = TIMER
                    TMP_MSG.X    = DESK_W
                    TMP_MSG.Y    = DESK_H
                    SENDMTMSG(TMP_MSG)
               ELSE 'CHECK POSITION
                    DIM AS IOS X,Y,NEW_X,NEW_Y,DOMOVE
                    MUTEXLOCK   MT_VIDEO
                    ScreenControl FB.GET_WINDOW_POS,X,Y
                    MUTEXUNLOCK MT_VIDEO
                    NEW_X = X
                    NEW_Y = Y
                    IF DESK_W*NB_MONITORS <= X OR DESK_H <= Y THEN
                         DOMOVE = TRUE
                         NEW_X = 0
                         NEW_Y = 0
                    ELSE
                         IF X < 0 THEN NEW_X = 0 : DOMOVE = TRUE
                         IF Y < 0 THEN NEW_Y = 0 : DOMOVE = TRUE
                    END IF
                    IF DOMOVE = TRUE THEN 
                         MUTEXLOCK   MT_VIDEO
                         ScreenControl FB.SET_WINDOW_POS,NEW_X,NEW_Y
                         MUTEXUNLOCK MT_VIDEO
                    END IF
               END IF
               'CHECK SETTING EDITOR'S VALUES
               ID = ControlAccess[CS_NB_MONITORS]
               CEIL(Controls[ID]->text.si,NB_MONITORS)
               Controls[ID]->UPDATE_GTEXT(TRUE)
          END IF

          'HIDE ABOUT WINDOW AFTER 5 SECONDS
          IF WIN_SWITCH ANDALSO CC = 5 THEN 
               IF Controls[Windows[W_ABOUT]]->visible = TRUE THEN 
                    Controls[Windows[W_ABOUT]]->visible = FALSE
                    Controls[WINTABS(W_ABOUT)]->state.POWER = FALSE
                    Controls[WINTABS(W_ABOUT)]->UPDATE_GSTATE()
               END IF
               WIN_SWITCH = FALSE
          END IF
          
          'EVERY MINUTE
          IF CC MOD 60 = 0 THEN
               DIM AS TXT TMPSTR
               'WAVE LIBRARY
               IF WF_LIBRARY_UPDATED = TRUE THEN
                    WF_LIBRARY_UPDATED = FALSE
                    '¤¤¤#195: "Saving audio library..."
                    TMPSTR = LNG_SET_EXT(195,"Saving audio library...")
                    MessageBar(TMPSTR)
                    R = SaveWFLibrary()
                    IF R < 0 THEN TERMINAL("SaveWFLibrary(): " & ERR2TXT(R),MSG_ERROR)
               END IF
               'SYNTH FILE
               IF SETTINGS.AUTOSAVE = TRUE THEN
                    '¤¤¤#196: "Saving session..."
                    TMPSTR = LNG_SET_EXT(196,"Saving session...")
                    MessageBar(TMPSTR)
                    R = SaveSession(SETTINGS.SESSION_PATH, SETTINGS.SESSION_FILE)
                    IF R < 0 THEN TERMINAL("SaveSession(): " & ERR2TXT(R),MSG_ERROR)
               END IF
          END IF
          
          'MESSAGE BAR UPDATE
          IF NEW_MSG_BAR = TRUE THEN 
               MB_COUNTER  = 4
               NEW_MSG_BAR = FALSE
          END IF
          IF Controls[ControlAccess[CS_DIALOG]]->VISIBLE = FALSE THEN
               IF MB_COUNTER > -1 THEN MB_COUNTER -= 1
               IF MB_COUNTER = 0 THEN
                    Controls[ControlAccess[CS_CTRL_INFO]]->VISIBLE = TRUE
                    Controls[ControlAccess[CS_MSG_BAR]]->VISIBLE   = FALSE
               END IF
          END IF

          Controls[ControlAccess[CS_FPS]]->TEXT.CONTENT = STR(G_FPS) & " FPS"
          Controls[ControlAccess[CS_FPS]]->UPDATE_GTEXT()
          
          IF VIDEO_THREAD_ACTIVE AND ScrSet AND InterfaceSet THEN 
               IF G_FPS < 42 THEN 
                    NB_LOW_FPS += 1
                    IF NB_LOW_FPS > 5 THEN SETTINGS.GPU_ECO = TRUE
               ELSE
                    NB_LOW_FPS  = 0
               END IF
          ELSE
               NB_LOW_FPS  = 0
          END IF
          
          'IF CC MOD 20 = 0 THEN SetThreadExecutionState(ES_DISPLAY_REQUIRED)
          
          CC += 1
          IF CC > 300 THEN CC = 1
     END IF
     RETURN 0
END FUNCTION 

FUNCTION ctrl_move         (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32 MONITOR_NUM = 0
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_SLIDE) THEN
          DIM AS IOS X0,Y0
          MUTEXLOCK   MT_VIDEO
          ScreenControl FB.GET_WINDOW_POS,X0,Y0
          ScreenControl FB.SET_WINDOW_POS,X0+X,Y0+Y
          MUTEXUNLOCK MT_VIDEO
     ELSEIF (ACTION AND CA_CLICK) THEN
          MONITOR_NUM += 1
          IF MONITOR_NUM > NB_MONITORS THEN MONITOR_NUM = 1
          IF MONITOR_NUM < 2 THEN
               MUTEXLOCK   MT_VIDEO
               ScreenControl FB.SET_WINDOW_POS,NULL_X,NULL_Y
               MUTEXUNLOCK MT_VIDEO
          ELSE
               DIM AS IOS W,H
               MUTEXLOCK   MT_VIDEO
               ScreenControl FB.GET_DESKTOP_SIZE,W,H
               ScreenControl FB.SET_WINDOW_POS,(MONITOR_NUM-1)*W,NULL_Y
               MUTEXUNLOCK MT_VIDEO
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_min          (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          Dim As Hwnd handle
          MUTEXLOCK   MT_VIDEO
          Screencontrol FB.GET_WINDOW_HANDLE, Cast(Integer, handle)
          ShowWindow(handle, 2)
          MUTEXUNLOCK MT_VIDEO
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_text_area    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32  nb_lines, vlimit
     IF     (ACTION AND CA_MD)   THEN
          Controls[CurrentID]->text.scrolling = FALSE
     ELSEIF (ACTION AND CA_CLICK) OR (ACTION = CA_MU) THEN
          Controls[CurrentID]->text.scrolling = FALSE
     ELSEIF (ACTION AND CA_WHEEL) OR (ACTION AND CA_SLIDE) THEN
          'V-SCROLL
          Controls[CurrentID]->text.scrolling = TRUE
          IF ACTION AND CA_WHEEL THEN Y *= WHEEL_FACTOR
          Controls[CurrentID]->text.vpx0 -= Y
          'LIMIT
          nb_lines  = Controls[CurrentID]->text.nb_lines
          vlimit    = Controls[CurrentID]->text.nb_lines * Controls[CurrentID]->text.font_size
          vlimit   -= Controls[CurrentID]->h
          vlimit   += Controls[CurrentID]->TEXT.TM + Controls[CurrentID]->TEXT.BM + 2*BORDER_SIZE
          floor(vlimit,0)
          floor(Controls[CurrentID]->text.vpx0,0)
          ceil(Controls[CurrentID]->text.vpx0,vlimit)
          Controls[CurrentID]->text.UPDATED = TRUE
     END IF
     RETURN 0
END FUNCTION

'FRACTION-AID
FUNCTION ctrl_fa_edit      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) ANDALSO (Controls[CurrentID]->DATA > 0) THEN
          DIM AS TXT     TMPSTR
          DIM AS IOS     IS_ANSWERED
          DIM AS F32     VALUE
          DIM AS FRACTIONAID.DATA PTR FRACTION_AID
          'Check and reset the current answer status of the dialog box
          IS_ANSWERED = DB_DATA.IS_ANSWERED
          DB_DATA.is_answered = FALSE
          FRACTION_AID = CAST(FRACTIONAID.DATA PTR, Controls[CurrentID]->DATA)
          IF IS_ANSWERED THEN
               IF DB_DATA.answer = TRUE THEN
                    VALUE = CSNG(TRIM(Controls[TFAccess[TF_Dialog]]->TEXT.content))
                    MUTEXLOCK   FRACTION_AID->MUTEX
                    FRACTION_AID->SET_VALUE(VALUE,FALSE)
                    MUTEXUNLOCK FRACTION_AID->MUTEX
               END IF
               Controls[CurrentID]->state.POWER = FALSE
          ELSE
               IF DB_DATA.CALLER_ID = CurrentID THEN
                    DB_cancel()
                    Controls[CurrentID]->state.POWER = FALSE
               ELSE
                    'ASK NEW VALUE
                    MUTEXLOCK   FRACTION_AID->MUTEX
                    VALUE = FRACTION_AID->VALUE.V
                    MUTEXUNLOCK FRACTION_AID->MUTEX
                    '¤¤¤#197: "Enter a new floating point value: "
                    TMPSTR = LNG_SET_EXT(197,"Enter a new floating point value: ")
                    DB_Ask(CurrentID,TMPSTR, TRUE, STR(VALUE),DB_INPUT, -1,-1)
                    Controls[CurrentID]->state.POWER = TRUE
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_fa_mode      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION = CA_CLICK) ANDALSO (Controls[CurrentID]->DATA > 0) THEN
          DIM AS FRACTIONAID.DATA PTR FRACTION_AID
          FRACTION_AID = CAST(FRACTIONAID.DATA PTR, Controls[CurrentID]->DATA)
          MUTEXLOCK   FRACTION_AID->MUTEX
          FRACTION_AID->CONVERT_FORM()
          MUTEXUNLOCK FRACTION_AID->MUTEX
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_fa_value     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS I32     SLOT_NUM
     DIM AS FRACTIONAID.DATA PTR FA_PTR
     DIM AS IOS     ISBIN
     STATIC AS I32  dX_cumul
     Activation_BState(ACTION,Controls[CurrentID]->state)
     
     IF (Controls[CurrentID]->DATA = 0) THEN
          Controls[CurrentID]->TEXT.si = -1
          RETURN 0
     END IF
     
     FA_PTR = CAST(FRACTIONAID.DATA PTR, Controls[CurrentID]->DATA)
     ISBIN  = (FA_PTR->FORM_TYPE = BINARY_INT)
     IF (ACTION AND CA_HOVER) OR (ACTION AND CA_MD) THEN
          'FIND SLOT INDEX
          X -= Controls[CurrentID]->X + BORDER_SIZE
          IF ISBIN = TRUE THEN
               DIM AS F32 WX = (Controls[CurrentID]->W-BORDER_SIZE-BORDER_SIZE)/SOUNDRA.BIN_LINE_SIZE
               SLOT_NUM = INT(ABS(X) / WX)
               CEIL(SLOT_NUM,SOUNDRA.BIN_LEN_MAX-1)
               Y -= Controls[CurrentID]->Y
               IF Y > Controls[CurrentID]->H/2 THEN SLOT_NUM += SOUNDRA.BIN_LINE_SIZE
          ELSE
               X -= FA_PTR->LM
               MUTEXLOCK   FA_PTR->MUTEX
               SLOT_NUM = FA_PTR->FIND_SLOT(X)
               MUTEXUNLOCK FA_PTR->MUTEX
          END IF
          Controls[CurrentID]->TEXT.si = SLOT_NUM
     ELSEIF (ACTION AND CA_SLIDE) THEN  'CHANGE VALUE
          DIM AS I32     INDEX, xstep
          IF ISBIN = TRUE THEN RETURN 0
          SLOT_NUM = Controls[CurrentID]->TEXT.si
          IF SLOT_NUM > -1 THEN
               MUTEXLOCK FA_PTR->MUTEX
               IF FA_PTR->SLOTS(SLOT_NUM).TYPE = FRACTIONAID.NUMBER THEN
                    dx_cumul += x
                    xstep     = dx_cumul / 8
                    IF xstep THEN
                         dx_cumul -= 8*xstep
                         IF     FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.VSLOT THEN
                              'CHANGE FLOAT VALUE AND UPDATE SIGN
                              FA_PTR->VALUE.V += FA_PTR->VSTEP * SGN(xstep)
                              FA_PTR->VALUE.SB = SGN(FA_PTR->VALUE.V)+1
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.BSLOT THEN
                              'CHANGE BASE VALUE AND KEEP IT IN RANGE 0..9
                              FA_PTR->VALUE.B += SGN(XSTEP)
                              IF FA_PTR->VALUE.B > 9 THEN FA_PTR->VALUE.B = 1
                              IF FA_PTR->VALUE.B < 1 THEN FA_PTR->VALUE.B = 9
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.ISLOT THEN
                              'CHANGE INT VALUE AND UPDATE SIGN
                              FA_PTR->VALUE.I += SGN(XSTEP)
                              FA_PTR->VALUE.SI = SGN(FA_PTR->VALUE.I)+1
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.NSLOT THEN
                              'CHANGE NUMERATOR VALUE
                              FA_PTR->VALUE.F.N += SGN(XSTEP)
                              IF (FA_PTR->FORM_TYPE = FRAC1) OR (FA_PTR->FORM_TYPE = EXPO1) THEN
                                   IF FA_PTR->VALUE.F.N > FA_PTR->VALUE.F.D-1 THEN FA_PTR->VALUE.F.N = 0
                                   IF FA_PTR->VALUE.F.N < 0 THEN FA_PTR->VALUE.F.N = FA_PTR->VALUE.F.D-1
                              END IF
                              FA_PTR->VALUE.SF = SGN(FA_PTR->VALUE.F.N)+1
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.DSLOT THEN
                              'CHANGE DENOMINATOR VALUE, KEEP IT OVER 0, KEEP N UNDER D
                              FA_PTR->VALUE.F.D += SGN(XSTEP)
                              FLOOR(FA_PTR->VALUE.F.D,1)
                              IF (FA_PTR->FORM_TYPE = FRAC1) OR (FA_PTR->FORM_TYPE = EXPO1) THEN
                                   IF FA_PTR->VALUE.F.D <= FA_PTR->VALUE.F.N THEN _
                                      FA_PTR->VALUE.F.N = FA_PTR->VALUE.F.D - 1
                              END IF
                         END IF
                         FA_PTR->VALUE.RECALC()
                         FA_PTR->EXPORT_VALUE()
                    END IF
               END IF
               MUTEXUNLOCK FA_PTR->MUTEX
          END IF
     ELSEIF (ACTION AND CA_CLICK) THEN  'CHANGE SIGN
          SLOT_NUM = Controls[CurrentID]->TEXT.si
          IF SLOT_NUM > -1 THEN
               IF ISBIN = TRUE THEN
                    DIM AS UI32 UNO=1,VI
                    MUTEXLOCK   FA_PTR->MUTEX
                    VI = CULNG(ABS(FA_PTR->VALUE.V))
                    UNO SHL= SLOT_NUM
                    VI  XOR= UNO
                    FA_PTR->VALUE.V = VI
                    FA_PTR->EXPORT_VALUE()
                    MUTEXUNLOCK FA_PTR->MUTEX
               ELSE
                    MUTEXLOCK   FA_PTR->MUTEX
                    IF FA_PTR->SLOTS(SLOT_NUM).TYPE = FRACTIONAID.SIGN THEN
                         IF     FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.VSIGN THEN
                              FA_PTR->VALUE.V  = -FA_PTR->VALUE.V
                              FA_PTR->VALUE.SB = SGN(FA_PTR->VALUE.V)+1
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.BSIGN THEN
                              FA_PTR->VALUE.V  = -FA_PTR->VALUE.V
                              FA_PTR->VALUE.SB = SGN(FA_PTR->VALUE.V)+1
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.ISIGN THEN
                              FA_PTR->VALUE.I  = -FA_PTR->VALUE.I
                              FA_PTR->VALUE.SI = SGN(FA_PTR->VALUE.I)+1
                              FA_PTR->VALUE.RECALC()
                         ELSEIF FA_PTR->SLOTS(SLOT_NUM).INDEX = FRACTIONAID.NSIGN THEN
                              FA_PTR->VALUE.F.N = -FA_PTR->VALUE.F.N
                              FA_PTR->VALUE.SF = SGN(FA_PTR->VALUE.F.N)+1
                              FA_PTR->VALUE.RECALC()
                         END IF
                         FA_PTR->EXPORT_VALUE()
                    END IF
                    MUTEXUNLOCK FA_PTR->MUTEX
               END IF
          END IF
     ELSE
          Controls[CurrentID]->TEXT.si = -1
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_fa_binary    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION

FUNCTION ctrl_fa_unit      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION = CA_CLICK) ANDALSO (Controls[CurrentID]->DATA > 0) THEN
          DIM AS FRACTIONAID.DATA PTR FRACTION_AID
          FRACTION_AID = CAST(FRACTIONAID.DATA PTR, Controls[CurrentID]->DATA)
          MUTEXLOCK FRACTION_AID->MUTEX
          FRACTION_AID->CONVERT_UNIT()
          Controls[CurrentID]->TEXT.content = FRACTIONAID.UNIT_SYMBOLS(FRACTION_AID->UNIT_TYPE,_
             FRACTION_AID->UNIT_INDEX)
          MUTEXUNLOCK FRACTION_AID->MUTEX
     END IF
     RETURN 0
END FUNCTION
' SYSTEM BAR
FUNCTION ctrl_quit         (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT TMPSTR
          DIM AS IOS is_answered
          'Check and reset the current answer status of the dialog box
          is_answered = DB_DATA.is_answered
          DB_DATA.is_answered = FALSE
          IF is_answered THEN
               IF DB_DATA.answer = TRUE THEN    'Answer is YES - Send QUIT msg to Control
                    CTRL_MSG.code = _QUIT
                    CTRL_MSG.TIME = TIMER
                    SENDMTMSG(CTRL_MSG)
               END IF
               Controls[CurrentID]->state.POWER = FALSE
          ELSE
               IF DB_DATA.CALLER_ID = CurrentID THEN
                    DB_cancel()
                    Controls[CurrentID]->state.POWER = FALSE
               ELSE 'Ask for confirmation
                    '¤¤¤#198: "Please confirm that you want to quit."
                    TMPSTR = LNG_SET_EXT(198,"Please confirm that you want to quit.")
                    DB_Ask(CurrentID,TMPSTR,FALSE,,DB_CONFIRM,-1,-1)
                    Controls[CurrentID]->state.POWER = TRUE
               END IF
          END IF
     END IF
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION

FUNCTION ctrl_win_tabs     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_MD) THEN  'SHOW WINDOW AND ACTIVATE CURRENT TAB
          DIM AS I32 WIN_NUM = Controls[CurrentID]->INDEX
          CUR_WIN = Windows[WIN_NUM]
          IF (CUR_WIN > -1) THEN
               IF WIN_NUM = W_ABOUT THEN WIN_SWITCH = FALSE
               'TOGGLE VISIBILITY
               Controls[CUR_WIN]->visible = NOT(Controls[CUR_WIN]->visible)
               IF WINTABS(WIN_NUM) > 0 THEN
                    Controls[WINTABS(WIN_NUM)]->state.POWER = Controls[CUR_WIN]->visible
                    Controls[WINTABS(WIN_NUM)]->UPDATE_GSTATE()
               END IF
               'OPEN BROWSER FOR TUNING WINDOW
               IF WIN_NUM = W_TUNING ANDALSO Controls[CUR_WIN]->visible = TRUE THEN
                    DIM AS I32  I
                    DIM AS TXT  FILENAME
                    FILENAME = TUNINGS(TUNING_CURRENT).FILENAME
                    OpenBrowser(HOMEPATH & "\TUNINGS",FILENAME)
                    I = SelectFile(FILENAME)
                    IF I = -1 THEN 
                         SaveTuning(@TUNINGS(TUNING_CURRENT))
                         SelectFile(FILENAME)
                    END IF
               END IF
          END IF
          Controls[Windows[W_SYNTH_TOOLS]]->VISIBLE = Controls[Windows[W_SYNTH]]->VISIBLE
          Controls[ControlAccess[CS_SCOPE]]->VISIBLE = Controls[Windows[W_SYNTH]]->VISIBLE
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_link       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32     LINK_INDEX = Controls[CurrentID]->INDEX
          DIM AS TXT     LINK_URL   = ""
          SELECT CASE LINK_INDEX
          CASE LINK_SOUNDRA
               LINK_URL = "https://jimmygervais.net/soundra"
          CASE LINK_PORTAL
               LINK_URL = "https://jimmygervais.net/"
          END SELECT
          IF LINK_URL <> "" THEN SHELL("start " & LINK_URL)
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_mic_power    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT     TMPSTR
          DIM AS IOS     TMP_CP_AVAIL
          DIM AS I32     ID
          MUTEXLOCK   MT_Audio
          TMP_CP_AVAIL = CP_AVAIL
          MUTEXUNLOCK MT_Audio     
          IF NOT(TMP_CP_AVAIL) THEN
               '¤¤¤#199: "Audio capture is not available."
               TMPSTR = LNG_SET_EXT(199,"Audio capture is not available.")
               TERMINAL(TMPSTR,MSG_WARNING)
               Controls[CurrentID]->state.POWER = FALSE
          ELSE 'Send a message to mixer
               CTRL_MSG.TIME = TIMER
               IF CAPTURING THEN
                    CTRL_MSG.code = _STOP_CP
                    SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
               ELSE
                    CTRL_MSG.code = _START_CP
                    SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
               END IF
          END IF
     END IF    
     RETURN 0
END FUNCTION

FUNCTION ctrl_mic_level    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_SLIDE) THEN
          DIM AS F32  V_dB,NEW_VOLUME
          DIM AS UI32 FLAG
          NEW_VOLUME = VOLUME_IN + X / Controls[CurrentID]->W
          FLOOR(NEW_VOLUME,dU32)
          CEIL(NEW_VOLUME,2)
          VOLUME_IN = NEW_VOLUME
          V_dB = ROUND(FRACTIONAID.U2DB(VOLUME_IN,0,,FLAG),2)
          Controls[CurrentID]->text.content = STR(V_dB) & "dB"
     END IF
     RETURN 0
END FUNCTION  
     
FUNCTION ctrl_play_power   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT TMPSTR
          DIM AS IOS TMP_PB_AVAIL 
          MUTEXLOCK   MT_Audio
          TMP_PB_AVAIL = PB_AVAIL
          MUTEXUNLOCK MT_Audio 
          IF NOT(TMP_PB_AVAIL) THEN
               '¤¤¤#200: "Audio playback is not available"
               TMPSTR = LNG_SET_EXT(200,"Audio playback is not available")
               TERMINAL(TMPSTR,MSG_WARNING)
               Controls[CurrentID]->state.POWER = FALSE
               PLAYING = FALSE
          ELSE 'Send a message to audio thread
               CTRL_MSG.TIME = TIMER
               IF PLAYING THEN
                    CTRL_MSG.code = _STOP_PB
                    SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
               ELSE
                    SOUNDRA.PRECOMPILE(TRUE)
                    CTRL_MSG.code = _START_PB
                    SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_play_level   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_SLIDE) THEN
          DIM AS F32  V_dB,NEW_VOLUME
          DIM AS UI32 FLAG
          NEW_VOLUME = VOLUME_OUT + X / Controls[CurrentID]->W
          FLOOR(NEW_VOLUME,dU32)
          CEIL(NEW_VOLUME,1)
          VOLUME_OUT = NEW_VOLUME
          V_dB = ROUND(FRACTIONAID.U2DB(VOLUME_OUT,0,,FLAG),2)
          Controls[CurrentID]->text.content = STR(V_dB) & "dB"
          'INFORM AUDIO THREAD
          CTRL_MSG.code = _SET_OUTPUT_VOLUME
          CTRL_MSG.x    = VOLUME_OUT
          SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
     END IF
     RETURN 0
END FUNCTION 
FUNCTION ctrl_record      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF RECORDING = FALSE THEN
               DIM AS TXT  FILENAME
               DIM AS I32  L
               DIM AS ZTXT PTR ZFILENAME
               FILENAME = "REC_" & TemporalStamp() & "_" & SETTINGS.SESSION_NAME
               L = LEN(FILENAME)+1
               ZFILENAME = NEW UI8[L]
               *ZFILENAME = FILENAME
               CTRL_MSG.code    = _START_RECORD
               CTRL_MSG.address = ZFILENAME
               CTRL_MSG.x       = L
               SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
          ELSE
               CTRL_MSG.code    = _STOP_RECORD
               SENDMTMSG(CTRL_MSG,JIGSAW_AUDIO.THREAD_ID)
          END IF
          RECORDING = NOT(RECORDING)
          Controls[CurrentID]->state.POWER = RECORDING
     END IF
     RETURN 0
END FUNCTION 

FUNCTION ctrl_draw_mode   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I = Controls[CurrentID]->state.POWER_LEVEL
          I += 1
          IF I = SOUNDRA.DRAW_MODES.NUMBER THEN I = 0
          SETTINGS.SCOPE_MODE = I
          Controls[CurrentID]->state.POWER_LEVEL = I
          Controls[CurrentID]->CLR = SOUNDRA.SCOPE_BUTTON_COLORS(I)
          SaveSettings()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_synth_edit   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     FullPower_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 N,MOD_ID,PATCH_ID,CON_ID
          NEGATE(SOUNDRA.PATCH_MODE)
          Controls[CurrentID]->state.POWER = SOUNDRA.PATCH_MODE
          N = Controls[Windows[W_SYNTH]]->family.n
          FOR I AS I32 = 1 TO N-1
               MOD_ID = Controls[Windows[W_SYNTH]]->family.children[I]
               IF MOD_ID > 0 THEN
                    PATCH_ID = Controls[MOD_ID]->family.children[0]
                    CON_ID   = Controls[MOD_ID]->family.children[1]
                    Controls[PATCH_ID]->VISIBLE = SOUNDRA.PATCH_MODE
                    Controls[CON_ID]->VISIBLE   = NOT(SOUNDRA.PATCH_MODE)
               END IF
          NEXT I
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_synth_multi  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     FullPower_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF Controls[CurrentID]->state.POWER THEN Controls[CurrentID-1]->state.POWER = TRUE
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_synth_center (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32     N, X0,Y0,DX,DY, ID,WIN_ID = Windows[W_SYNTH]
          DIM AS _CTRL   PTR GUI_PTR
          'CLOSEST MODULE
          IF SOUNDRA.ROOT_MODULE > 0 THEN
               GUI_PTR = SOUNDRA.ROOT_MODULE->GUI_PTR
          ELSE
               ID = SOUNDRA.FIND_BOT_MODULE()
               IF ID > -1 THEN GUI_PTR = SOUNDRA.MODULES[ID]->GUI_PTR
          END IF
          ID = ControlAccess[CS_SYNTH_OUT]
          IF GUI_PTR > 0 THEN
               'CENTER BOTTOM
               X0  = GUI_PTR->X + .5*GUI_PTR->W
               Y0  = GUI_PTR->Y + GUI_PTR->H
               'DISTANCE FROM SYNTH_OUT
               DX  = X0 - (Controls[ID]->X + .5*Controls[ID]->W)
               DY  = Y0 - Controls[ID]->Y
               DY += 50
               'MOVE SYNTH_OUT
               Controls[ID]->X = Controls[ID]->X + DX
               Controls[ID]->Y = Controls[ID]->Y + DY
               ctrl_update(ID)
          END IF
          'CALCULATE DISTANCE FROM SYNTH_OUT TO BOTTOM CENTER
          DX = (Controls[WIN_ID]->X + .5*Controls[WIN_ID]->W) - Controls[ID]->X
          DY = (Controls[WIN_ID]->Y + Controls[WIN_ID]->H) - Controls[ID]->Y - Controls[ID]->H
          IF Controls[Windows[W_KB]]->VISIBLE = FALSE THEN DY -= Controls[Windows[W_KB]]->H + GRID_STEP
          N  = Controls[WIN_ID]->family.n
          'MOVE ALL CONTROLS
          FOR I AS I32 = 0 TO N - 1
               ID = Controls[WIN_ID]->family.children[I]
               IF ID > 0 THEN
                    Controls[ID]->X = Controls[ID]->X + DX
                    Controls[ID]->Y = Controls[ID]->Y + DY
                    ctrl_update(ID)
               END IF
          NEXT I
          SOUNDRA.PLANE_X = 0
          SOUNDRA.PLANE_Y = 0
          SOUNDRA.REDRAW_CONNECTORS = TRUE
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_undo         (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION
FUNCTION ctrl_redo         (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION

FUNCTION ctrl_map_MIDI0    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     IF NB_MIDI_DEVICES = 0 THEN 
          IF (ACTION AND CA_CLICK) THEN
               DIM AS TXT TMPSTR
               '¤¤¤#201: "No MIDI device connected."
               TMPSTR = LNG_SET_EXT(201,"No MIDI device connected.")
               MessageBar(TMPSTR)
          END IF
          RETURN 0
     END IF
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 ID
          SOUNDRA.MAP_MIDI = IIF(SOUNDRA.MAP_MIDI=SOUNDRA.MIDI_CONTROL_FREE,0,SOUNDRA.MIDI_CONTROL_FREE)
          Controls[CurrentID]->state.POWER = (SOUNDRA.MAP_MIDI = SOUNDRA.MIDI_CONTROL_FREE)
          'TURN OFF THE OTHER 2 BUTTONS
          ID = ControlAccess[CS_MIDI1]
          Controls[ID]->state.POWER = FALSE
          Controls[ID]->UPDATE_GSTATE()
          ID = ControlAccess[CS_MIDI2]
          Controls[ID]->UPDATE_GSTATE()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_boards_visibility(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     FullPower_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          BOARDS_VIEW(Controls[ControlAccess[CS_KB_VISIBLITY]]->state.POWER)
     END IF
     RETURN 0
END FUNCTION

FUNCTION BOARDS_VIEW(VISIBLE AS IOS) AS I32
     DIM AS I32 X,W0,W1,H0,SBH
     'VISIBILITY
     Controls[Windows[W_BB]]->VISIBLE = VISIBLE
     Controls[Windows[W_KB]]->VISIBLE = VISIBLE
     'DIMENSIONS - CALC
     SBH = Controls[Windows[W_SYSBAR]]->H + GRID_STEP
     X   = IIF(VISIBLE=TRUE,Controls[Windows[W_BB]]->W + GRID_STEP,0)
     W0  = SCR_W
     W1  = SCR_W / SETTINGS.MONITORS_CURRENT
     H0  = SCR_H - SBH
     IF Controls[Windows[W_BB]]->VISIBLE    THEN W0 -= Controls[Windows[W_BB]]->W + GRID_STEP
     IF Controls[Windows[W_KB]]->VISIBLE    THEN H0 -= Controls[Windows[W_KB]]->H + GRID_STEP
     IF Controls[Windows[W_PANEL]]->VISIBLE THEN W0 -= Controls[Windows[W_PANEL]]->W + GRID_STEP
     IF SETTINGS.MONITORS_CURRENT < 2 THEN 
          H0 -= SBH
          W1  = W0
     END IF
     'DIMENSIONS - SET for synth and scope only
     Controls[Windows[W_SYNTH]]->X = X
     Controls[Windows[W_SYNTH]]->W = W0
     Controls[Windows[W_SYNTH]]->H = H0
     ctrl_update(Windows[W_SYNTH])
     Controls[ControlAccess[CS_SCOPE]]->X = X
     Controls[ControlAccess[CS_SCOPE]]->W = W0
     Controls[ControlAccess[CS_SCOPE]]->H = H0
     ctrl_update(ControlAccess[CS_SCOPE])
     SOUNDRA.REDRAW_CONNECTORS = TRUE
     RETURN 0
END FUNCTION

FUNCTION ctrl_text_field   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS _SUPER_TEXT       PTR T
     DIM    AS I32            K, W2,HM, left_w,letter_w, R, TEXT_SIZE
     DIM    AS I32            CPX
     DIM    AS TXT            CONTENT,   TXT_VIEW
     DIM    AS IOS            EDITABLE, RECALC_VP0, RECALC_VP1
     DIM    AS _CHAR_RELAY    CR_COPY

     IF (ACTION AND CA_MD) OR (ACTION AND CA_SLIDE) OR (ACTION AND CA_KB) THEN
          Controls[CurrentID]->state.ACTIVE = TRUE
     ELSE
          RETURN 0
     END IF
     CR_COPY = CR
     
     'Shortcuts
     T = @Controls[CurrentID]->text
     TEXT_SIZE = T->font_size
     EDITABLE  = Controls[CurrentID]->text.editable
     CONTENT   = T->content
     'Sizes and margins
     HM        = BORDER_SIZE + TEXT_SIZE/2               'HORIZONTAL MARGIN  (PX)
     W2        = Controls[CurrentID]->w - 2*HM           'FIELD MARGIN       (PX)
     T->TEXT_WIDTH = GetTXT_W(MID(CONTENT,T->VP0+1,T->VP1 - T->VP0),MainFont, TEXT_SIZE) - 1
     CPX       = T->CPX
     
     IF (ACTION AND CA_MD)         THEN                'MOUSE CURSOR SELECT
          'SELECTION RESET
          CPX = X - Controls[CurrentID]->X - HM
          T->SL  = 0
          'CURSOR POSITION APPROX.
          TXT_VIEW = MID(CONTENT,T->VP0+1,T->VP1 - T->VP0)
          T->CP  = T->VP0 + SplitString(TXT_VIEW, MainFont, TEXT_SIZE, CPX, FALSE)
          'DETERMINE WHICH SIDE OF THE CHAR THE CURSOR FALLS
          left_w   = GetTXT_W(MID(TXT_VIEW,1,T->CP),MainFont, TEXT_SIZE) - 1
          letter_w = GetTXT_W(MID(TXT_VIEW,T->CP-T->VP0+1,1),MainFont, TEXT_SIZE) - 1
          IF CPX - left_w > letter_w/2 THEN T->CP += 1
          ceil(T->CP,T->LS)
          T->SP    = T->CP
          'Char relay update and recall
          CR_COPY.ID = CurrentID
          IF CR_COPY.IS_RECALL THEN 
               Controls[CR_COPY.OLD_ID]->state.ACTIVE = FALSE
               Controls[CR_COPY.OLD_ID]->UPDATE_GSTATE()
          END IF
     ELSEIF (ACTION AND CA_SLIDE)  THEN  'MOUSE SELECTION
          CPX += X '(dx)
          IF CPX < 0 THEN                         'REWIND
               T->VP0 -= 1
               floor(T->VP0,0)
               T->CP = T->VP0
               RECALC_VP1 = TRUE
          ELSEIF CPX > T->TEXT_WIDTH + 3 THEN     'FORWARD
               T->VP1 += 1
               ceil(T->VP1,T->LS)
               T->CP = T->VP1
               RECALC_VP0 = TRUE
          ELSE                                    'IN BETWEEN
               'CURSOR POSITION APPROX.
               TXT_VIEW = MID(CONTENT,T->VP0+1,T->VP1 - T->VP0)
               T->CP    = T->VP0 + SplitString(TXT_VIEW, MainFont, TEXT_SIZE, CPX, FALSE)
               'DETERMINE WHICH SIDE OF THE CHAR THE CURSOR FALLS
               left_w   = GetTXT_W(MID(TXT_VIEW,1,T->CP),MainFont, TEXT_SIZE) - 1
               letter_w = GetTXT_W(MID(TXT_VIEW,T->CP-T->VP0+1,1),MainFont, TEXT_SIZE) - 1
               IF CPX - left_w > letter_w/2 THEN T->CP += 1
               ceil(T->CP,T->LS)
          END IF
          T->SL = T->CP - T->SP
          Controls[CurrentID]->SLIDING = TRUE
     ELSEIF (ACTION AND CA_KB)     THEN
          IF NOT(CR_COPY.ASCII_MODE) THEN
               IF     CR_COPY.SC = FB.SC_END  THEN
                    T->CP  = T->LS
                    T->VP1 = T->LS
                    RECALC_VP0 = TRUE
                    IF CR_COPY.SHIFT_ENABLED = 0 THEN T->SP = T->CP
                    T->SL = T->CP - T->SP
               ELSEIF CR_COPY.SC = FB.SC_HOME THEN
                    T->CP  = 0
                    T->VP0 = 0
                    RECALC_VP1 = TRUE
                    IF CR_COPY.SHIFT_ENABLED = 0 THEN T->SP = T->CP
                    T->SL = T->CP - T->SP
               ELSEIF CR_COPY.SC = FB.SC_LEFT THEN
                    T->CP -= 1
                    floor(T->CP,0)
                    IF T->CP < T->VP0 THEN
                         T->VP0 = T->CP
                         RECALC_VP1 = TRUE
                    END IF                    
                    IF CR_COPY.SHIFT_ENABLED = 0 THEN T->SP = T->CP
                    T->SL = T->CP - T->SP
               ELSEIF (CR_COPY.SC = FB.SC_RIGHT) THEN
                    T->CP += 1
                    ceil(T->CP,T->LS)
                    IF T->CP > T->VP1 THEN
                         T->VP1 = T->CP
                         RECALC_VP0 = TRUE
                    END IF
                    IF CR_COPY.SHIFT_ENABLED = 0 THEN T->SP = T->CP
                    T->SL = T->CP - T->SP
               ELSEIF (CR_COPY.SC = FB.SC_BACKSPACE OR CR_COPY.SC = FB.SC_DELETE) AND EDITABLE THEN
                    IF T->SL THEN
                         'SELECTION DIRECTION
                         IF T->CP > T->SP THEN SWAP T->CP,T->SP
                         'DELETE SELECTION
                         CONTENT = MID(CONTENT,1,T->CP) & MID(CONTENT,T->SP+1)
                         T->content = CONTENT
                         T->SL      = 0
                    ELSE
                         IF CR_COPY.SC = FB.SC_DELETE THEN T->CP += 1
                         'DELETE PREVIOUS CHAR
                         IF T->CP > 0 THEN CONTENT = MID(CONTENT,1,T->CP-1) & MID(CONTENT,T->CP+1)
                         T->content = CONTENT
                         T->CP     -= 1
                         floor(T->CP,0)
                    END IF
                    ceil(T->VP0,T->CP)
                    T->SP = T->CP
                    RECALC_VP1 = TRUE
               ELSEIF (CR_COPY.SC = FB.SC_ENTER) AND EDITABLE THEN
                    IF NOT(T->content_type = TCT_TEXT) THEN
                         T->FILTER()
                         CONTENT = T->content
                         T->CP  = 0
                         T->VP0 = 0
                         T->SL  = 0
                         RECALC_VP1 = TRUE
                    END IF
               ELSEIF CR_COPY.ASCII = 1 THEN 'CTRL-A (SELECT ALL)
                    T->CP  = 0
                    T->VP0 = 0
                    T->SP  = T->LS
                    T->SL  = T->LS
                    RECALC_VP1 = TRUE
               ELSEIF CR_COPY.ASCII = 3 THEN 'CTRL-C (COPY)
                    IF T->SL THEN
                         IF T->CP > T->SP THEN SWAP T->CP,T->SP
                         CLIPBOARD = MID(CONTENT,T->CP+1,ABS(T->SL))
                         SetClipboardText(CLIPBOARD)
                    END IF
               ELSEIF CR_COPY.ASCII = 22 THEN 'CTRL-V (PASTE)
                    K = 0
                    IF CR_COPY.IS_INSERT THEN K = 1
                    IF T->SL THEN
                         'SELECTION DIRECTION
                         IF T->CP > T->SP THEN SWAP T->CP,T->SP
                         'DELETE SELECTION
                         CONTENT = MID(CONTENT,1,T->CP) & MID(CONTENT,T->SP+1)
                         T->SL   = 0
                         K       = 0
                    END IF
                    'INSERT CLIPBOARD & UPDATE CURSORS
                    IF CLIPBOARD = "" THEN CLIPBOARD = GetClipboardText()
                    CONTENT    = MID(CONTENT,1,T->CP) & CLIPBOARD & MID(CONTENT,T->CP+1+K)
                    T->content = CONTENT
                    T->CP     += CLIPBOARD.LENGTH
                    ceil(T->CP, T->LS)
                    T->SP      = T->CP
                    IF T->CP > T->VP1 THEN
                         T->VP1 = T->CP
                         RECALC_VP0 = TRUE
                    ELSE
                         RECALC_VP1 = TRUE
                    END IF
                    CLIPBOARD = ""
               ELSEIF CR_COPY.ASCII = 24 THEN 'CTRL-X (CUT)
                    
               END IF
          ELSEIF EDITABLE THEN 'ASCII MODE
                    'CONTEXTUAL FILTER
               IF CR_COPY.ASCII = 32 ANDALSO NOT(T->content_type = TCT_TEXT) THEN 
                    T->FILTER()
                    CONTENT = T->content
                    T->CP   = 0
                    T->VP0  = 0
                    T->SL   = 0
                    RECALC_VP1 = TRUE
               ELSE 'CHAR INPUT
                    K = 0
                    IF CR_COPY.IS_INSERT THEN K = 1
                    IF T->SL THEN
                         'SELECTION DIRECTION
                         IF T->CP > T->SP THEN SWAP T->CP,T->SP
                         'DELETE SELECTION
                         CONTENT = MID(CONTENT,1,T->CP) & MID(CONTENT,T->SP+1)
                         T->SL   = 0
                         K       = 0
                    END IF
                    'INSERT CHAR & UPDATE CURSORS
                    CONTENT    = MID(CONTENT,1,T->CP) & CR_COPY.CHARS & MID(CONTENT,T->CP+1+K)
                    T->content = CONTENT
                    T->CP     += LEN(TRIM(CR_COPY.CHARS)) + IIF(CR_COPY.CHARS = " ",1,0)
                    ceil(T->CP, T->LS)
                    T->SP      = T->CP
                    IF T->CP > T->VP1 THEN
                         T->VP1 = T->CP
                         RECALC_VP0 = TRUE
                    ELSE
                         RECALC_VP1 = TRUE
                    END IF
               END IF 'ASCII MODE
          END IF 'KB
     END IF 'ACTION
     T->CPX = CPX
     
     IF RECALC_VP0 THEN
          T->VP0 = RSplitString(CONTENT, MainFont, TEXT_SIZE, W2, T->VP1, FALSE) - 1
          T->UPDATED = TRUE
     ELSEIF RECALC_VP1 THEN
          T->VP1 = T->VP0 + SplitString(MID(CONTENT,T->VP0+1), MainFont, TEXT_SIZE,W2, FALSE)
          T->UPDATED = TRUE
     END IF

     'CONFIRM ('OK') BY PRESSING ENTER
     IF (CR_COPY.ID = CurrentID) THEN
          DIM AS I32 ID
          IF (CR_COPY.SC = FB.SC_ENTER)      THEN
               ID = T->ok_id
          ELSEIF (CR_COPY.SC = FB.SC_ESCAPE) THEN
               ID = T->cancel_id
          END IF
          IF (ID > 0) AND (ID <> CurrentID)  THEN 
               R = Controls[ID]->fct_ptr(CA_CLICK,0,0,ID,CurrentID) '!!!
               ctrl_update(ID)
          END IF
     END IF
     RETURN R
END FUNCTION
FUNCTION ctrl_dialog_ok    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF Controls[CurrentID]->VISIBLE AND (ACTION AND CA_CLICK) THEN DB_answer(CurrentID,TRUE)
     RETURN 0
END FUNCTION
FUNCTION ctrl_dialog_x     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN DB_answer(CurrentID,FALSE)
     RETURN 0
END FUNCTION

'------------------------------------------------------------------------------------------------------------
' TOOLS

FUNCTION ctrl_rewind       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     IF SOUNDRA.SELECTED_MODULE->FLAGS(SOUNDRA.MF_RECORD) = TRUE THEN RETURN 0
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF SOUNDRA.SELECTED_MODULE > 0 THEN
               IF SOUNDRA.SELECTED_MODULE->TYPE_ID > 0 THEN
                    IF     SOUNDRA.SELECTED_MODULE->FCT_ID = SOUNDRA.MM_KEYBOARD THEN
                         SOUNDRA.KB[SOUNDRA.SELECTED_MODULE->TYPE_ID-1]->REMAP = -2
                    ELSEIF SOUNDRA.SELECTED_MODULE->FCT_ID = SOUNDRA.MM_BEATBOARD THEN
                         SOUNDRA.BB[SOUNDRA.SELECTED_MODULE->TYPE_ID-1]->REMAP = -2
                    ELSE
                         ERASE SOUNDRA.SELECTED_MODULE->CLK
                         SOUNDRA.SELECTED_MODULE->SYS_FLAGS(SOUNDRA.MSF_REWINDED)  = TRUE
                         SOUNDRA.SELECTED_MODULE->SYS_FLAGS(SOUNDRA.MSF_THRESHOLD) = FALSE
                         FOR F AS I32 = 0 TO SOUNDRA.NB_VCHANNELS_MAX-1
                              SOUNDRA.SELECTED_MODULE->SYS_FLAGS(SOUNDRA.MSF_PLAYING_FC0+F) = TRUE
                         NEXT F
                    END IF
               ELSE 'RESET CLK OF ALL MODULES OF SAME TYPE
                    DIM AS I32 FID = SOUNDRA.SELECTED_MODULE->FCT_ID
                    FOR I AS I32 = 0 TO SOUNDRA.LI_MODULES.LENGTH-1
                         IF SOUNDRA.MODULES[I]->FCT_ID = FID THEN
                              ERASE SOUNDRA.MODULES[I]->CLK
                              SOUNDRA.MODULES[I]->SYS_FLAGS(SOUNDRA.MSF_REWINDED)  = TRUE
                              SOUNDRA.MODULES[I]->SYS_FLAGS(SOUNDRA.MSF_THRESHOLD) = FALSE
                              FOR F AS I32 = 0 TO SOUNDRA.NB_VCHANNELS_MAX-1
                                   SOUNDRA.MODULES[I]->SYS_FLAGS(SOUNDRA.MSF_PLAYING_FC0+F) = TRUE
                              NEXT F
                         END IF
                    NEXT I
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_seed         (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF SOUNDRA.SELECTED_MODULE > 0 THEN 
               NEGATE(SOUNDRA.SELECTED_MODULE->FLAGS(SOUNDRA.MF_KICKSTART))
               Controls[CurrentID]->state.POWER = SOUNDRA.SELECTED_MODULE->FLAGS(SOUNDRA.MF_KICKSTART)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_module_flags (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32               INDEX = Controls[CurrentID]->INDEX
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               IF     INDEX = SOUNDRA.MF_POWER THEN
                    Negate(MOD_PTR->FLAGS(INDEX))
                    LogAction(ACTION_MOD_POWER, MOD_PTR)
                    IF MOD_PTR->FLAGS(INDEX) = FALSE THEN 
                         IF     MOD_PTR->FCT_ID = SOUNDRA.MM_KEYBOARD THEN
                              SOUNDRA.KB[MOD_PTR->TYPE_ID-1]->REMAP = -2
                         ELSEIF MOD_PTR->FCT_ID = SOUNDRA.MM_BEATBOARD THEN
                              SOUNDRA.BB[MOD_PTR->TYPE_ID-1]->REMAP = -2
                         END IF
                    END IF
                    IF MOD_PTR->TYPE_ID > 0 THEN SOUNDRA.DO_PRECOMPILE()
               ELSEIF INDEX = SOUNDRA.MF_VOICE_MODE THEN
                    IF MOD_PTR->VOICE_SUPPORT = SOUNDRA.ANYPHONIC THEN
                         MOD_PTR->VOICE_MODE = 1+MOD_PTR->FLAGS(INDEX)
                         Negate(MOD_PTR->FLAGS(INDEX))
                         LogAction(ACTION_MOD_POLY, MOD_PTR)
                         IF MOD_PTR->TYPE_ID > 0 THEN SOUNDRA.DO_PRECOMPILE()
                    END IF
               ELSEIF INDEX = SOUNDRA.MF_PLAY_POWER THEN
                    IF (MOD_PTR->FLAGS(INDEX) = FALSE) ANDALSO MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND) THEN _
                        ERASE MOD_PTR->CLK
                    Negate(MOD_PTR->FLAGS(INDEX))
                    LogAction(ACTION_MOD_PLAY, MOD_PTR)
               ELSEIF INDEX = SOUNDRA.MF_LOOP THEN
                    Negate(MOD_PTR->FLAGS(INDEX))
                    Controls[CurrentID]->CLR = _
                       IIF(MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_LOOPING)=TRUE,RGB(200,100,0),0)
               ELSE
                    Negate(MOD_PTR->FLAGS(INDEX))
                    IF INDEX = SOUNDRA.MF_ENVELOPE THEN 
                         LogAction(ACTION_MOD_ENV, MOD_PTR)
                    ELSEIF INDEX = SOUNDRA.MF_SWITCH1 THEN 
                         Controls[CurrentID]->text.si = ABS(MOD_PTR->FLAGS(INDEX))
                         IF (MOD_PTR->FCT_ID = SOUNDRA.MM_KEYBOARD)  OR _
                            (MOD_PTR->FCT_ID = SOUNDRA.MM_BEATBOARD) THEN _
                             MOD_PTR->FLAGS(SOUNDRA.MF_RETRIGGER) = NOT(MOD_PTR->FLAGS(SOUNDRA.MF_SWITCH1))
                         Controls[CurrentID]->text.UPDATED = TRUE
                    END IF
               END IF
               IF INDEX <> SOUNDRA.MF_SWITCH1 THEN _
                  Controls[CurrentID]->state.POWER = MOD_PTR->FLAGS(INDEX)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_map_MIDI1    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     IF NB_MIDI_DEVICES = 0 THEN 
          IF (ACTION AND CA_CLICK) THEN
               DIM AS TXT TMPSTR
               '¤¤¤#202: "No MIDI device connected."
               TMPSTR = LNG_SET_EXT(202,"No MIDI device connected.")
               MessageBar(TMPSTR)
          END IF
          RETURN 0
     END IF
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 ID 
          SOUNDRA.MAP_MIDI = IIF(SOUNDRA.MAP_MIDI=SOUNDRA.MIDI_CONTROL_SEMI,0,SOUNDRA.MIDI_CONTROL_SEMI)
          Controls[CurrentID]->state.POWER = (SOUNDRA.MAP_MIDI = SOUNDRA.MIDI_CONTROL_SEMI)
          'TURN OFF THE OTHER 2 BUTTONS
          ID = ControlAccess[CS_MIDI0]
          Controls[ID]->state.POWER = FALSE
          Controls[ID]->UPDATE_GSTATE()
          ID = ControlAccess[CS_MIDI2]
          Controls[ID]->state.POWER = FALSE
          Controls[ID]->UPDATE_GSTATE()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_map_MIDI2    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     IF NB_MIDI_DEVICES = 0 THEN 
          IF (ACTION AND CA_CLICK) THEN
               DIM AS TXT TMPSTR
               '¤¤¤#203: "No MIDI device connected."
               TMPSTR = LNG_SET_EXT(203,"No MIDI device connected.")
               MessageBar(TMPSTR)
          END IF
          RETURN 0
     END IF
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 ID 
          SOUNDRA.MAP_MIDI = IIF(SOUNDRA.MAP_MIDI=SOUNDRA.MIDI_CONTROL_FIXED,0,SOUNDRA.MIDI_CONTROL_FIXED)
          Controls[CurrentID]->state.POWER = (SOUNDRA.MAP_MIDI = SOUNDRA.MIDI_CONTROL_FIXED)
          'TURN OFF THE OTHER 2 BUTTONS
          ID = ControlAccess[CS_MIDI0]
          Controls[ID]->state.POWER = FALSE
          Controls[ID]->UPDATE_GSTATE()
          ID = ControlAccess[CS_MIDI1]
          Controls[ID]->state.POWER = FALSE
          Controls[ID]->UPDATE_GSTATE()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_MIDI_MIN    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF SOUNDRA.SELECTED_MODULE > 0 THEN
               DIM AS I32 PID = SOUNDRA.SELECTED_MODULE->SELECTED_PID
               IF PID > -1 THEN
                    SOUNDRA.PARAM_MODE = _
                       IIF(SOUNDRA.PARAM_MODE = SOUNDRA.PARAM_MODES.MIN,0,SOUNDRA.PARAM_MODES.MIN)
                    Controls[CurrentID]->state.POWER = (SOUNDRA.PARAM_MODE = SOUNDRA.PARAM_MODES.MIN)
                    Controls[CurrentID+1]->state.POWER = FALSE
                    Controls[CurrentID+1]->UPDATE_GSTATE()
                    Controls[CurrentID+2]->state.POWER = FALSE
                    Controls[CurrentID+2]->UPDATE_GSTATE()
                    SOUNDRA.REFRESH_FA(SOUNDRA.SELECTED_MODULE)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_MIDI_MAX    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF SOUNDRA.SELECTED_MODULE > 0 THEN
               DIM AS I32 PID = SOUNDRA.SELECTED_MODULE->SELECTED_PID
               IF PID > -1 THEN
                    SOUNDRA.PARAM_MODE = _
                       IIF(SOUNDRA.PARAM_MODE = SOUNDRA.PARAM_MODES.MAX,0,SOUNDRA.PARAM_MODES.MAX)
                    Controls[CurrentID]->state.POWER = (SOUNDRA.PARAM_MODE = SOUNDRA.PARAM_MODES.MAX)
                    Controls[CurrentID-1]->state.POWER = FALSE
                    Controls[CurrentID-1]->UPDATE_GSTATE()
                    Controls[CurrentID+1]->state.POWER = FALSE
                    Controls[CurrentID+1]->UPDATE_GSTATE()
                    SOUNDRA.REFRESH_FA(SOUNDRA.SELECTED_MODULE)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_MIDI_RES    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          IF SOUNDRA.SELECTED_MODULE > 0 THEN
               DIM AS I32 PID = SOUNDRA.SELECTED_MODULE->SELECTED_PID
               IF PID > -1 THEN
                    SOUNDRA.PARAM_MODE = _
                       IIF(SOUNDRA.PARAM_MODE = SOUNDRA.PARAM_MODES.RES,0,SOUNDRA.PARAM_MODES.RES)
                    Controls[CurrentID]->state.POWER = (SOUNDRA.PARAM_MODE = SOUNDRA.PARAM_MODES.RES)
                    Controls[CurrentID-1]->state.POWER = FALSE
                    Controls[CurrentID-1]->UPDATE_GSTATE()
                    Controls[CurrentID-2]->state.POWER = FALSE
                    Controls[CurrentID-2]->UPDATE_GSTATE()
                    SOUNDRA.REFRESH_FA(SOUNDRA.SELECTED_MODULE)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_module_S0    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION   
FUNCTION ctrl_module_S1     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION

FUNCTION ctrl_param_tabs   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_MD) THEN
          DIM AS I32 INDEX = Controls[CurrentID]->INDEX
          FOR I AS I32 = 0 TO NB_PARAM_LISTS-1
               Controls[ControlAccess[CS_PARAM_TAB0]+I]->state.POWER = (INDEX = I)
               Controls[ControlAccess[CS_PARAM_TAB0]+I]->UPDATE_GSTATE()
          NEXT I
          Controls[ControlAccess[CS_PARAM]]->VISIBLE   = (INDEX = PARAM_INPUTS)
          Controls[ControlAccess[CS_SHAPE]]->VISIBLE   = (INDEX = PARAM_SHAPES)
          Controls[ControlAccess[CS_SAMPLE]]->VISIBLE  = (INDEX = PARAM_SAMPLES)
          Controls[ControlAccess[CS_FILTER]]->VISIBLE  = (INDEX = PARAM_FILTERS)
          Controls[ControlAccess[CS_MELODY]]->VISIBLE  = (INDEX = PARAM_MELODIES)
          Controls[ControlAccess[CS_MM_DEVS]]->VISIBLE = (INDEX = PARAM_DEVICES)
          Controls[ControlAccess[CS_SP]]->VISIBLE      = (INDEX = PARAM_SP)
          IF SOUNDRA.SELECTED_MODULE > 0 THEN SOUNDRA.SELECTED_MODULE->ACTIVE_TAB = INDEX
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_param_global (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               DIM AS I32 PID = MOD_PTR->SELECTED_PID
               IF PID > -1 THEN
                    DIM AS I32 LEVEL,CID
                    'TOGGLE GLOBAL MODE ON/OFF
                    NEGATE(MOD_PTR->PARAM(PID).USE_GLOBAL)
                    MOD_PTR->PLUG(PID,0,@SOUNDRA.REFERENCE_MODULE(MOD_PTR->FCT_ID)) 'VALIDATES EVERYTHING
                    LogAction(ACTION_MOD_GLOBAL,MOD_PTR,PID)
                    Controls[CurrentID]->state.POWER = MOD_PTR->PARAM(PID).USE_GLOBAL
                    IF MOD_PTR->TYPE_ID > 0 THEN SOUNDRA.DO_PRECOMPILE()
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_param_vmap   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               DIM AS I32 ID = ControlAccess[CS_PARAM_VHOST],PID = MOD_PTR->SELECTED_PID
               IF PID = -1 THEN
                    MOD_PTR->SET_VHOST_PID()
               ELSEIF PID < SOUNDRA.SELECTED_MODULE->NB_PORTS THEN
                    IF PID = MOD_PTR->VHOST_PID THEN 
                         MOD_PTR->SET_VHOST_PID()
                    ELSE
                         MOD_PTR->SET_VHOST_PID(PID)
                    END IF
               ELSE
                    Controls[ID]->state.POWER = FALSE
                    Controls[ID]->UPDATE_GSTATE()
                    RETURN 0
               END IF
               Controls[ID]->state.POWER = (PID=MOD_PTR->VHOST_PID) OR (PID=-1 AND MOD_PTR->VHOST_PID0<0)
               Controls[ID]->UPDATE_GSTATE()
               LogAction(ACTION_MOD_VMAP,MOD_PTR,PID)
               IF MOD_PTR->TYPE_ID > 0 THEN SOUNDRA.DO_PRECOMPILE()
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_param_domain (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               DIM AS I32 PID = MOD_PTR->SELECTED_PID
               IF PID > -1 THEN
                    DIM AS I32 LEVEL,CID, NEW_DOMAIN
                    NEGATE(MOD_PTR->PARAM(PID).SWITCH_DOMAIN)
                    NEW_DOMAIN = MOD_PTR->PARAM(PID).DEFAULT_DOMAIN
                    IF MOD_PTR->PARAM(PID).SWITCH_DOMAIN = TRUE THEN
                       NEW_DOMAIN += 1
                       TOP_CYCLE(NEW_DOMAIN,SOUNDRA.NB_DOMAINS)
                    END IF
                    MOD_PTR->PARAM(PID).DOMAIN = NEW_DOMAIN
                    'DISPLAY
                    Controls[CurrentID]->CLR = SOUNDRA.CONNECT_COLOR(MOD_PTR->PARAM(PID).DOMAIN)
                    Controls[CurrentID]->TEXT.CONTENT = _
                       IIF(MOD_PTR->PARAM(PID).DOMAIN = SOUNDRA.TIME_DOMAIN,"T","E")
                    LogAction(ACTION_MOD_DOMAIN,MOD_PTR,PID)
                    IF MOD_PTR->PORT(PID)->PORT_TYPE = SOUNDRA.PORT_TYPES.DEFAULT THEN _
                       SOUNDRA.REDRAW_CONNECTORS = TRUE
               ELSE
                    Controls[CurrentID]->CLR = SWATCH0.FRM_OFF
                    Controls[CurrentID]->TEXT.CONTENT = " "
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_param_schan  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],SOUNDRA.NB_SCHANNELS_MAX,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          'UPDATE TEXT
          DIM AS TXT               TMPSTR
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          'UPDATE VALUE
          IF MOD_PTR > 0 THEN
               DIM AS I32 PID = MOD_PTR->SELECTED_PID
               IF MOD_PTR->SELECTED_SCHAN <> Controls[CurrentID]->text.si THEN
                    IF PID > -1 THEN 
                         IF MOD_PTR->PARAM(PID).MONO THEN
                              MOD_PTR->SELECTED_SCHAN      = 0
                              Controls[CurrentID]->text.si = 0
                         ELSE
                              MOD_PTR->SELECTED_SCHAN      = Controls[CurrentID]->text.si
                              SETUP_PARAM_FA(MOD_PTR,PID)
                         END IF
                    END IF
               END IF
          END IF
          IF Controls[CurrentID]->text.si = 0 THEN
               TMPSTR = "L"
          ELSE
               TMPSTR = "R"
          END IF
          Controls[CurrentID]->text.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_param_s_link (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               DIM AS I32 PID = MOD_PTR->SELECTED_PID
               IF PID > -1 THEN 'APPLY TO SELECTED PARAM IF STEREO
                    IF MOD_PTR->PARAM(PID).MONO = FALSE THEN
                         DIM AS I32 I,NEW_NBSC
                         DIM AS TXT TMPSTR
                         NEGATE(MOD_PTR->PARAM(PID).LINK_SCHAN)
                         MOD_PTR->VALUES(PID)->NBSC = MOD_PTR->PARAM(PID).LINK_SCHAN + 2
                         IF MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_MONO) = TRUE THEN
                              MOD_PTR->NBSC = 1
                         ELSE 'SCAN PORTS TO GET NEW VALUE
                              FOR I = 0 TO MOD_PTR->NB_PARAMS-1
                                   IF MOD_PTR->VALUES(I)->NBSC > NEW_NBSC THEN _
                                      NEW_NBSC = MOD_PTR->VALUES(I)->NBSC
                              NEXT I
                              MOD_PTR->NBSC = NEW_NBSC
                         END IF
                         IF MOD_PTR->PARAM(PID).LINK_SCHAN = TRUE THEN _
                            MOD_PTR->SET_VALUE(PID,_
                               MOD_PTR->VALUES(PID)->OUTPUTVALUE(MOD_PTR->SELECTED_SCHAN,0,0))
                         SOUNDRA.REFRESH_FA(SOUNDRA.SELECTED_MODULE)
                         IF (MOD_PTR->PORT(PID)->PORT_TYPE > SOUNDRA.PORT_TYPES.DEFAULT) AND _
                            (MOD_PTR->TYPE_ID > 0) THEN SOUNDRA.DO_PRECOMPILE()
                         'UPDATE STATE
                         Controls[CurrentID]->state.POWER = MOD_PTR->PARAM(PID).LINK_SCHAN
                         LogAction(ACTION_MOD_SLINK,MOD_PTR,PID)
                    END IF
               ELSE 'APPLY TO ALL PARAMS
                    DIM AS I32 NEW_NBSC
                    DIM AS IOS MOD_SLINK = NOT(MOD_PTR->NBSC=1)
                    IF MOD_PTR->FCT_ID = SOUNDRA.MM_CAPTURE THEN
                         NEW_NBSC = IIF(MOD_PTR->NBSC=1,2,1)
                         MOD_PTR->NBSC0 = NEW_NBSC
                    ELSE
                         FOR I AS I32 = 0 TO MOD_PTR->NB_PARAMS-1
                              IF MOD_PTR->PARAM(I).MONO = FALSE THEN
                                   MOD_PTR->PARAM(I).LINK_SCHAN = MOD_SLINK
                                   MOD_PTR->VALUES(I)->NBSC = MOD_PTR->PARAM(I).LINK_SCHAN + 2
                                   IF MOD_PTR->VALUES(I)->NBSC > NEW_NBSC THEN _
                                      NEW_NBSC = MOD_PTR->VALUES(I)->NBSC
                              END IF
                         NEXT I
                    END IF
                    IF NEW_NBSC > 0 THEN
                         IF MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_MONO) = TRUE THEN
                              MOD_PTR->NBSC = 1
                         ELSE
                              IF MOD_PTR->NBSC0 <= NEW_NBSC THEN MOD_PTR->NBSC = NEW_NBSC
                              SOUNDRA.REFRESH_FA(SOUNDRA.SELECTED_MODULE)
                              IF (MOD_PTR->TYPE_ID > 0) THEN SOUNDRA.DO_PRECOMPILE()
                         END IF
                         LogAction(ACTION_MOD_SLINK,MOD_PTR,PID)
                    END IF
                    Controls[CurrentID]->state.POWER = (MOD_PTR->NBSC=1)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_param_list   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], SOUNDRA.PARAM_LIST_SIZE)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS IOS               DISABLED
          DIM AS I32               PID = Controls[CurrentID]->text.si
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR = 0 THEN RETURN 0
          DISABLED = SOUNDRA.PARAM_LIST(PID).DISABLED OR (PID < 0)
          IF DISABLED THEN
               MOD_PTR->SELECTED_PID = -1
          ELSE
               MOD_PTR->SELECTED_PID = PID
          END IF
          SETUP_PARAM_FA(MOD_PTR,MOD_PTR->SELECTED_PID)
          Controls[CurrentID]->text.si = MOD_PTR->SELECTED_PID
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_shape_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)   AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], SOUNDRA.NB_GENERATORS)
     IF (ACTION AND CA_CLICK) THEN
          IF Controls[CurrentID]->TEXT.si = -1 THEN Controls[CurrentID]->TEXT.si = 0
          Controls[ControlAccess[CS_SHAPE_GRAPH]]->TEXT.si = Controls[CurrentID]->TEXT.si
          Controls[ControlAccess[CS_SHAPE_GRAPH]]->UPDATE_GTEXT(TRUE)
          IF SOUNDRA.SELECTED_MODULE THEN
               IF SOUNDRA.SELECTED_MODULE->CATEGORY = SOUNDRA.MOD_CATS.GENERATOR THEN
                    DIM AS TXT TMPSTR
                    SOUNDRA.SELECTED_MODULE->GEN_ID = Controls[CurrentID]->TEXT.si
                    TMPSTR  = SOUNDRA.SELECTED_MODULE->Abbrev & " " & STR(SOUNDRA.SELECTED_MODULE->TYPE_ID)
                    TMPSTR &= " : "& Abbreviate(SOUNDRA.GENERATOR_NAME(SOUNDRA.SELECTED_MODULE->GEN_ID),TRUE)
                    IF SOUNDRA.SELECTED_MODULE->GUI_PTR > 0 THEN
                         SOUNDRA.SELECTED_MODULE->GUI_PTR->TEXT.CONTENT = TMPSTR
                         SOUNDRA.SELECTED_MODULE->GUI_PTR->UPDATE_GTEXT()
                    END IF
                    Controls[ControlAccess[CS_MODULE_LIST]+MOD_LIBRARY]->UPDATE_GTEXT(TRUE)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_MIDIKB_DEVLIST(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     DIM AS I32 LAST_SI = Controls[CurrentID]->TEXT.si
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], NB_MIDI_DEVICES_MAX+1)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 NEW_MAP_ID = Controls[CurrentID]->TEXT.si-1
          FLOOR(NEW_MAP_ID,-1)
          IF (SOUNDRA.SELECTED_MODULE > 0) ANDALSO _
             (SOUNDRA.SELECTED_MODULE->FCT_ID = SOUNDRA.MM_KEYBOARD) THEN
               DIM AS I32 LAST_MAP_ID = SOUNDRA.SELECTED_MODULE->GEN_ID-1
               DIM AS I32 CHAN = SOUNDRA.SELECTED_MODULE->MIDI_CHAN-1
               MUTEXLOCK   SOUNDRA.MT_MIDICC
               'RESET
               IF (LAST_MAP_ID > -1) ANDALSO (SOUNDRA.MIDI_MAP[LAST_MAP_ID] > 0) THEN
                    IF CHAN < 0 THEN 'ALL CHANNELS
                         FOR I = 0 TO NB_MIDI_CHANNELS_MAX-1
                              SOUNDRA.MIDI_MAP[LAST_MAP_ID]->KB_PTR(I) = 0
                         NEXT I
                    ELSE
                         SOUNDRA.MIDI_MAP[LAST_MAP_ID]->KB_PTR(CHAN) = 0
                    END IF
               END IF
               'SET
               IF (NEW_MAP_ID > -1) ANDALSO (SOUNDRA.MIDI_MAP[NEW_MAP_ID] > 0) THEN
                    IF (SOUNDRA.MIDI_MAP[NEW_MAP_ID]->DEVICE_NAME = "") THEN 
                         NEW_MAP_ID = -1
                    ELSE
                         IF CHAN < 0 THEN         'ALL [FREE] CHANNELS
                              FOR I = 0 TO NB_MIDI_CHANNELS_MAX-1
                                   IF SOUNDRA.MIDI_MAP[NEW_MAP_ID]->KB_PTR(I) > 0 THEN _
                                      SOUNDRA.MIDI_MAP[NEW_MAP_ID]->KB_PTR(I)->GEN_ID = 0
                                   SOUNDRA.MIDI_MAP[NEW_MAP_ID]->KB_PTR(I) = SOUNDRA.SELECTED_MODULE
                              NEXT I
                         ELSE
                              IF SOUNDRA.MIDI_MAP[NEW_MAP_ID]->KB_PTR(CHAN) > 0 THEN _
                                 SOUNDRA.MIDI_MAP[NEW_MAP_ID]->KB_PTR(CHAN)->GEN_ID = 0
                              SOUNDRA.MIDI_MAP[NEW_MAP_ID]->KB_PTR(CHAN) = SOUNDRA.SELECTED_MODULE
                         END IF
                    END IF
               END IF
               MUTEXUNLOCK SOUNDRA.MT_MIDICC
               SOUNDRA.SELECTED_MODULE->GEN_ID = NEW_MAP_ID+1
          END IF
          Controls[CurrentID]->TEXT.si = NEW_MAP_ID+1
          IF NEW_MAP_ID = -1 THEN Controls[CurrentID]->TEXT.UPDATED = TRUE
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_MIDIKB_CHAN   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],NB_MIDI_CHANNELS_MAX+1,X,Controls[CurrentID]->W,FALSE)
     'UPDATE TEXT
     IF UPDATED = TRUE THEN
          DIM AS I32 I = Controls[CurrentID]->text.si
          DIM AS TXT TMPSTR = Controls[CurrentID]->text.content
          'UPDATE CONTROL
          TMPSTR = MID(TMPSTR,1,LEN(TMPSTR)-2) & STR(I)
          IF I < 10 THEN TMPSTR &= " "
          Controls[CurrentID]->text.content = TMPSTR
          'UPDATE MAPPING
          IF (SOUNDRA.SELECTED_MODULE > 0) ANDALSO _
             (SOUNDRA.SELECTED_MODULE->FCT_ID = SOUNDRA.MM_KEYBOARD) THEN
               DIM AS I32 LAST_CHAN = SOUNDRA.SELECTED_MODULE->MIDI_CHAN-1, NEW_CHAN = I-1 '-1..15
               DIM AS I32 MAP_ID    = SOUNDRA.SELECTED_MODULE->GEN_ID-1
               MUTEXLOCK   SOUNDRA.MT_MIDICC
               IF (MAP_ID > -1) ANDALSO (SOUNDRA.MIDI_MAP[MAP_ID] > 0) THEN     'MAP EXISTS
                    'RESET
                    IF LAST_CHAN < 0 THEN                   'LINKED TO ALL CHANNELS
                         FOR I = 0 TO NB_MIDI_CHANNELS_MAX-1
                              SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(I) = 0
                         NEXT I
                    ELSE
                         SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(LAST_CHAN) = 0
                    END IF
                    'SET
                    IF NEW_CHAN < 0 THEN                    'LINKED TO ALL CHANNELS
                         FOR I = 0 TO NB_MIDI_CHANNELS_MAX-1
                              IF SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(I) > 0 THEN _
                                 SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(I)->GEN_ID = 0
                              SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(I) = SOUNDRA.SELECTED_MODULE
                         NEXT I
                    ELSE
                         IF SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(NEW_CHAN) > 0 THEN _
                            SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(NEW_CHAN)->GEN_ID = 0
                         SOUNDRA.MIDI_MAP[MAP_ID]->KB_PTR(NEW_CHAN) = SOUNDRA.SELECTED_MODULE
                    END IF
               END IF
               MUTEXUNLOCK SOUNDRA.MT_MIDICC
               SOUNDRA.SELECTED_MODULE->MIDI_CHAN = NEW_CHAN+1
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_shape_graph (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)   AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     RETURN 0
END FUNCTION

'------------------------------------------------------------------------------------------------------------
' BROWSER

FUNCTION ctrl_browser_tabs (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_MD) THEN
          DIM AS I32     ID,INDEX,LID
          DIM AS IOS     A,B
          'TABS
          INDEX = Controls[CurrentID]->INDEX
          ID    = ControlAccess[CS_BROWSER_TAB0]
          Controls[ID]->state.power   = (INDEX = T_MESSAGES)
          Controls[ID]->UPDATE_GSTATE()
          Controls[ID+1]->state.power = (INDEX = T_FILES)
          Controls[ID+1]->UPDATE_GSTATE()
          Controls[ID+2]->state.power = (INDEX = T_MODULES)
          Controls[ID+2]->UPDATE_GSTATE()
          Controls[ID+3]->state.power = (INDEX = T_SAMPLES)
          Controls[ID+3]->UPDATE_GSTATE()
          Controls[ID+4]->state.power = (INDEX = T_MIDICC)
          Controls[ID+4]->UPDATE_GSTATE()
          ID    = ControlAccess[CS_SESSION_TAB0]
          Controls[ID]->state.power   = (INDEX = T_SESSIONS)
          Controls[ID]->UPDATE_GSTATE()
          'WINDOWS
          Controls[ControlAccess[CS_CONSOLE]]->VISIBLE = (INDEX = T_MESSAGES)
          Controls[ControlAccess[CS_FILES]]->VISIBLE   = (INDEX = T_FILES) OR (INDEX = T_SESSIONS)
          Controls[ControlAccess[CS_SAMPLES]]->VISIBLE = (INDEX = T_SAMPLES)
          Controls[ControlAccess[CS_MODULES]]->VISIBLE = (INDEX = T_MODULES)
          Controls[ControlAccess[CS_MIDICC]]->VISIBLE  = (INDEX = T_MIDICC)
          'BUTTONS
          LID = FindActiveList()
          ID  = ControlAccess[CS_LI_ORDER]
          A   = NOT((INDEX = T_FILES) OR (INDEX = T_SAMPLES) OR (INDEX = T_SESSIONS))
          B   = (INDEX = T_MESSAGES) AND (SOUNDRA.SELECTED_MODULE = 0)
          Controls[ID+BB_RENAME]->state.DISABLED = A OR (INDEX = T_MIDICC)
          Controls[ID+BB_RENAME]->UPDATE_GSTATE()
          Controls[ID+BB_COPY]->state.DISABLED   = A
          Controls[ID+BB_COPY]->UPDATE_GSTATE()
          Controls[ID+BB_DELETE]->state.DISABLED = B
          Controls[ID+BB_DELETE]->UPDATE_GSTATE()
          Controls[ID+BB_LOAD]->state.DISABLED   = (INDEX = T_MESSAGES) OR (INDEX = T_MIDICC)
          Controls[ID+BB_LOAD]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_LI_ORDER]]->text.si = Controls[LID]->text.reversed_index + 1
          Controls[ControlAccess[CS_LI_ORDER]]->UPDATE_GTEXT()
          'BROWSE
          SELECT CASE INDEX
          CASE T_FILES
               Browse(@FOLDER0,,"*.*",FALSE,FALSE)
               Controls[ControlAccess[CS_FILE_LIST]]->UPDATE_GTEXT(TRUE)
          CASE T_SESSIONS
               Browse(@FOLDER0,SETTINGS.SESSION_PATH,".soundra",FALSE,FALSE)
               SelectFile(SETTINGS.SESSION_FILE)
          END SELECT
          ACTIVE_BROWSER_TAB = INDEX
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_list_order   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF ACTION AND CA_CLICK THEN
          DIM AS I32 ID = FindActiveList()
          'REVERSE DISPLAY ORDER
          Negate(Controls[ID]->text.reversed_index)
          Controls[CurrentID]->text.si = Controls[ID]->text.reversed_index + 1
          IF ID = ControlAccess[CS_FILE_LIST] THEN SelectFile("")
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_li_ren       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT     TMPSTR
          DIM AS IOS     IS_ANSWERED
          DIM AS I32     R,ID,LI
          IS_ANSWERED = DB_DATA.IS_ANSWERED
          DB_DATA.is_answered = FALSE
          IF IS_ANSWERED THEN
               IF DB_DATA.answer = TRUE THEN
                    DIM AS TXT     NEW_NAME,NEW_FILE_NAME
                    DIM AS I32     I,J
                    NEW_NAME = TRIM(Controls[TFAccess[TF_Dialog]]->TEXT.content)
                    NEW_FILE_NAME = NEW_NAME
                    'CHECK EXTENSION
                    J = INSTRREV(UCASE(NEW_FILE_NAME),UCASE(REN_FILE_EXT))
                    IF J = 0 THEN 
                         NEW_FILE_NAME &= LCASE(REN_FILE_EXT)
                    ELSE
                         NEW_NAME = MID(NEW_NAME,1,J-1)
                    END IF
                    ID = DB_DATA.EXT_ID
                    SELECT CASE ID
                    CASE ControlAccess[CS_FILE_LIST]
                         'RENAME
                         R = NAME(REN_FILE_PATH & "\" & REN_OLD_NAME,REN_FILE_PATH & "\" & NEW_FILE_NAME)
                         IF R THEN 
                              '¤¤¤#204: "Renaming error: "
                              TMPSTR = LNG_SET_EXT(204,"Renaming error: ")
                              TMPSTR &= ERR2TXT(R)
                              TERMINAL(TMPSTR,MSG_ERROR)
                         ELSE
                              IF (UCASE(REN_FILE_EXT) = ".WAV") OR (UCASE(REN_FILE_EXT) = ".WAVE") THEN
                                   'RENAME IN LIB?
                                   FOR I = 0 TO LI_WF_LIBRARY.LENGTH-1
                                        IF WF_LIBRARY[I]->PATH          = CAST(TXT,REN_FILE_PATH) AND _
                                           WF_LIBRARY[I]->FILENAME      = CAST(TXT,REN_OLD_NAME)  THEN 
                                             WF_LIBRARY[I]->FILENAME    = NEW_FILE_NAME
                                             WF_LIBRARY_UPDATED         = TRUE
                                             Controls[ControlAccess[CS_SAMPLE_LIST]]->UPDATE_GTEXT(TRUE)
                                           EXIT FOR
                                        END IF
                                   NEXT I
                              ELSEIF UCASE(REN_FILE_EXT) = ".SOUNDRA" THEN
                                   'RENAME SESSION?
                                   IF SETTINGS.SESSION_PATH = REN_FILE_PATH AND _
                                      SETTINGS.SESSION_FILE = REN_OLD_NAME THEN
                                        SETTINGS.SESSION_PATH = REN_FILE_PATH
                                        SETTINGS.SESSION_FILE = NEW_FILE_NAME
                                        SETTINGS.SESSION_NAME = NEW_NAME
                                        SaveSettings()
                                        I = ControlAccess[CS_SESSION_TAB0]
                                        Controls[I]->text.content = " " & NEW_NAME
                                        Controls[I]->UPDATE_GTEXT()
                                   END IF
                              ELSEIF UCASE(REN_FILE_EXT) = ".TNG" THEN
                                   IF TUNINGS(TUNING_CURRENT).FILEPATH = REN_FILE_PATH AND _
                                      TUNINGS(TUNING_CURRENT).FILENAME = REN_OLD_NAME  THEN
                                        TUNINGS(TUNING_CURRENT).FILENAME = NEW_FILE_NAME
                                        MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
                                        TUNINGS(TUNING_COPY).FILENAME    = NEW_FILE_NAME
                                        MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
                                        I = ControlAccess[CS_TNG_NAME]
                                        Controls[I]->text.content = NEW_FILE_NAME
                                        Controls[I]->UPDATE_GTEXT()
                                   END IF
                              END IF
                              'REFRESH LIST
                              Browse(@FOLDER0,,,FALSE,FALSE)
                              Controls[ControlAccess[CS_FILE_LIST]]->UPDATE_GTEXT(TRUE)
                              SelectFile(NEW_FILE_NAME)
                         END IF
                    CASE ControlAccess[CS_SAMPLE_LIST]
                         IF (LI < LI_WF_Library.LENGTH) ANDALSO (WF_Library[LI] > 0) THEN
                              'RENAME CORRESPONDING FILE
                              REN_FILE_PATH = WF_Library[LI]->Path
                              R = NAME(REN_FILE_PATH & "\" & REN_OLD_NAME,REN_FILE_PATH & "\" &NEW_FILE_NAME)
                              IF R THEN 
                                   '¤¤¤#205: "Renaming error: "
                                   TMPSTR = LNG_SET_EXT(205,"Renaming error: ")
                                   TMPSTR &= ERR2TXT(R)
                                   TERMINAL(TMPSTR,MSG_ERROR)
                              ELSE
                                   Browse(@FOLDER0,,,FALSE,FALSE)
                                   Controls[ControlAccess[CS_FILE_LIST]]->UPDATE_GTEXT(TRUE)
                              END IF
                              'RENAME LIB ENTRY
                              WF_Library[LI]->FileName = NEW_FILE_NAME
                              Controls[ControlAccess[CS_SAMPLE_LIST]]->UPDATE_GTEXT(TRUE)
                         END IF
                    END SELECT
               END IF
               Controls[CurrentID]->state.POWER = FALSE
          ELSE
               IF DB_DATA.CALLER_ID = CurrentID THEN
                    DB_cancel()
                    Controls[CurrentID]->state.POWER = FALSE
                    REN_OLD_NAME = ""
               ELSE
                    DIM AS I32 J
                    ID = FindActiveList()
                    IF ID = 0 THEN 
                         '¤¤¤#206: "No visible list from which to rename."
                         TMPSTR = LNG_SET_EXT(206,"No visible list from which to rename.")
                         MessageBar(TMPSTR)
                         RETURN 0
                    END IF
                    LI = Controls[ID]->text.si
                    IF LI < 0 THEN
                         '¤¤¤#207: "No item is selected."
                         TMPSTR = LNG_SET_EXT(207,"No item is selected.")
                         MessageBar(TMPSTR)
                         RETURN 0
                    END IF
                    SELECT CASE ID
                    CASE ControlAccess[CS_FILE_LIST]
                         REN_FILE_PATH = CURDIR
                         REN_OLD_NAME  = FOLDER0.GetName(LI)
                         J = INSTRREV(REN_OLD_NAME, ".")
                         REN_FILE_EXT  = ""
                         IF J THEN REN_FILE_EXT = MID(REN_OLD_NAME,J)
                    CASE ControlAccess[CS_SAMPLE_LIST]
                         IF (LI < LI_WF_Library.LENGTH) ANDALSO (WF_Library[LI] > 0) THEN
                              REN_OLD_NAME = WF_Library[LI]->FileName
                              J = INSTRREV(REN_OLD_NAME, ".")
                              REN_FILE_EXT = ""
                              IF J THEN REN_FILE_EXT = MID(REN_OLD_NAME,J)
                         END IF
                    CASE ELSE
                         '¤¤¤#208: "The selected item cannot be renamed."
                         TMPSTR = LNG_SET_EXT(208,"The selected item cannot be renamed.")
                         MessageBar(TMPSTR)
                         RETURN 0
                    END SELECT
                    'SETUP DIALOG
                    '¤¤¤#209: "Enter a new name: "
                    TMPSTR = LNG_SET_EXT(209,"Enter a new name: ")
                    DB_Ask(CurrentID,TMPSTR, TRUE, REN_OLD_NAME,DB_INPUT, LI,ID)
                    Controls[CurrentID]->state.POWER = TRUE
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_li_copy      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT     TMPSTR
          DIM AS I32     R, ID, LI
          ID = FindActiveList()
          IF ID = 0  THEN 
               IF SOUNDRA.SELECTED_MODULE THEN
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
               ELSE
                    '¤¤¤#210: "No visible list from which to copy."
                    TMPSTR = LNG_SET_EXT(210,"No visible list from which to copy.")
                    MessageBar(TMPSTR)
                    RETURN 0
               END IF
          END IF
          
          LI = Controls[ID]->text.si
          IF LI > -1 THEN 
               IF ID = ControlAccess[CS_MODULE_LIST] + MOD_REFERENCE THEN LI = SOUNDRA.MM_MAP(LI)
          ELSEIF SOUNDRA.SELECTED_MODULE THEN
               IF SOUNDRA.SELECTED_MODULE->PORT_TYPE = SOUNDRA.PORT_TYPES.MASTER THEN
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_REFERENCE
                    LI = SOUNDRA.SELECTED_MODULE->FCT_ID
               ELSE
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
                    LI = SOUNDRA.SELECTED_MODULE->MOD_ID
               END IF
          END IF

          IF LI < 0 THEN 
               '¤¤¤#211: "No selected item to copy."
               TMPSTR = LNG_SET_EXT(211,"No selected item to copy.")
               MessageBar(TMPSTR)
               RETURN 0
          END IF
          
          SELECT CASE ID
          CASE ControlAccess[CS_MODULE_LIST] + MOD_REFERENCE
               DIM AS I32 X,Y, MOD_ID = SOUNDRA.FIND_TOP_MODULE()
               X = Controls[ControlAccess[CS_SYNTH_OUT]]->X
               Y = Controls[ControlAccess[CS_SYNTH_OUT]]->Y
               IF MOD_ID > -1 ANDALSO SOUNDRA.MODULES[MOD_ID]->GUI_PTR > 0 THEN _
                  Y = SOUNDRA.MODULES[MOD_ID]->GUI_PTR->Y
               Y -= SOUNDRA.REFERENCE_MODULE(LI).GUI_PTR->H + GRID_STEP * 2
               MOD_ID = CreateModule(LI,X,Y,TRUE)
               IF MOD_ID > -1 THEN SelectModule(SOUNDRA.MODULES[MOD_ID])
          CASE ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
               IF LI < SOUNDRA.LI_MODULES.LENGTH THEN
                    DIM AS I32 X,Y, MOD_ID = SOUNDRA.FIND_TOP_MODULE()
                    X = Controls[ControlAccess[CS_SYNTH_OUT]]->X
                    Y = Controls[ControlAccess[CS_SYNTH_OUT]]->Y
                    IF MOD_ID > -1 ANDALSO SOUNDRA.MODULES[MOD_ID]->GUI_PTR > 0 THEN _
                       Y = SOUNDRA.MODULES[MOD_ID]->GUI_PTR->Y
                    IF SOUNDRA.MODULES[LI]->GUI_PTR THEN _
                       Y -= SOUNDRA.MODULES[LI]->GUI_PTR->H + GRID_STEP * 2
                    MOD_ID = CreateModule(LI,X,Y,FALSE)
                    IF MOD_ID > -1 THEN SelectModule(SOUNDRA.MODULES[MOD_ID])
               END IF
          CASE ControlAccess[CS_FILE_LIST]
               DIM AS TXT     COPY_NAME
               IF NOT(FOLDER0.GetAttr(LI) AND fbDirectory) THEN
                    COPY_NAME = CreateFileCopy(CURDIR, FOLDER0.GetName(LI))
                    IF COPY_NAME = "" THEN 
                         '¤¤¤#212: "File copy error: "
                         TMPSTR = LNG_SET_EXT(212,"File copy error: ")
                         TMPSTR &= ERR2TXT(R)
                         TERMINAL(TMPSTR,MSG_ERROR)
                    ELSE
                         Browse(@FOLDER0,,,FALSE,FALSE)
                         SelectFile(COPY_NAME)
                    END IF
               END IF
          CASE ELSE
               '¤¤¤#213: "The selected item cannot be copied."
               TMPSTR = LNG_SET_EXT(213,"The selected item cannot be copied.")
               MessageBar(TMPSTR)
          END SELECT
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_li_del       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32     ID,LI,R
          DIM AS TXT     FILE_PATH,FILE_NAME,TMPSTR
          DIM AS IOS     IS_ANSWERED, IS_VALID
          
          'CHECK DIALOG BOX ANSWER
          IS_ANSWERED = DB_DATA.IS_ANSWERED
          DB_DATA.is_answered = FALSE
          IF IS_ANSWERED THEN
               IF DB_DATA.answer = TRUE THEN
                    IF   DB_DATA.EXT_ID = DEL_MIDI_MAP THEN
                         IF DB_DATA.LI > -1 THEN
                              SOUNDRA.MIDI_MAP[DB_DATA.LI]->RESET_MAP()
                              REFRESH_MIDI_LISTS(TRUE)
                              SOUNDRA.REMOVE_KB_DEVICE(SOUNDRA.MIDICC_MAP_ID)
                              IF (SOUNDRA.SELECTED_MODULE > 0) ANDALSO _
                                 (SOUNDRA.SELECTED_MODULE->SELECTED_PID > -1) THEN
                                   Controls[ControlAccess[CS_MIDI2]]->state.power_level = _
                          SOUNDRA.SELECTED_MODULE->PARAM(SOUNDRA.SELECTED_MODULE->SELECTED_PID).MIDI_CONTROLS
                                   Controls[ControlAccess[CS_MIDI2]]->UPDATE_GSTATE()
                              END IF
                         END IF
                    ELSEIF DB_DATA.EXT_ID = DEL_FILE THEN
                         DIM AS I32 K
                         FILE_NAME = Controls[TFAccess[TF_Dialog]]->TEXT.content
                         R = KILL(FILE_NAME)
                         'K = INSTRREV(FILE_NAME,ANY "\/")
                         'IF K THEN FILE_NAME = MID(FILE_NAME,K+1)
                         IF R THEN 
                              '¤¤¤#214: "Deleting error: "
                              TMPSTR = LNG_SET_EXT(214,"Deleting error: ")
                              TMPSTR &= ERR2TXT(R)
                              TERMINAL(TMPSTR,MSG_ERROR)
                         ELSE
                              '¤¤¤#215: "The file has been deleted."
                              TMPSTR = LNG_SET_EXT(215,"The file has been deleted.")
                              MessageBar(TMPSTR)
                              Browse(@FOLDER0,,,FALSE,FALSE)
                              SelectFile("")
                         END IF
                    ELSEIF DB_DATA.EXT_ID = DEL_PRESETS THEN
                         SOUNDRA.RESET_MASTER_PRESETS()
                         '¤¤¤#216: "All master presets have been reset."
                         TMPSTR = LNG_SET_EXT(216,"All master presets have been reset.")
                         MessageBar(TMPSTR)
                    END IF
               END IF
               Controls[CurrentID]->state.POWER    = FALSE
               Controls[CurrentID]->state.DISABLED = FALSE
               RETURN 0
          END IF
          
          'ACTIVE LIST
          ID = FindActiveList()
          IF ID = 0  THEN 
               IF SOUNDRA.SELECTED_MODULE THEN
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
               ELSE
                    '¤¤¤#217: "No visible list from which to delete."
                    TMPSTR = LNG_SET_EXT(217,"No visible list from which to delete.")
                    MessageBar(TMPSTR)
                    RETURN 0
               END IF
          END IF
          
          'CHECK ACTION
          LI = Controls[ID]->text.si
          SELECT CASE ID
          CASE ControlAccess[CS_MODULE_LIST]                'RESET PRESET
               IF LI = -1 THEN
                    IF SOUNDRA.SELECTED_MODULE = 0 THEN
                         '¤¤¤#218: "Confirm reset of all master presets?"
                         TMPSTR = LNG_SET_EXT(218,"Confirm reset of all master presets?")
                         DB_Ask(CurrentID,TMPSTR, FALSE,,DB_CONFIRM,-1,DEL_PRESETS)
                         IS_VALID = TRUE
                    END IF
               ELSEIF (LI < SOUNDRA.CATALOG_SIZE) THEN
                    DIM AS TXT TMPSTR
                    SOUNDRA.IMPORT_MASTER_DATA(@SOUNDRA.MASTER_PRESET(SOUNDRA.MM_MAP(LI)),FALSE)
                    '¤¤¤#219: "Master preset of {$} reset."
                    TMPSTR = LNG_SET_EXT(219,"Master preset of {$} reset.")
                    TMPSTR = TXTINSERT(TMPSTR,SOUNDRA.REFERENCE_MODULE(SOUNDRA.MM_MAP(LI)).LABEL)
                    MessageBar(TMPSTR)
                    IS_VALID = TRUE
               END IF
          CASE ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY  'DELETE MODULE
               IF (LI > -1) ANDALSO (LI < SOUNDRA.LI_MODULES.LENGTH) ANDALSO (SOUNDRA.MODULES[LI] > 0) THEN
                    DeleteModule(SOUNDRA.MODULES[LI])
                    Controls[ControlAccess[CS_MIDI_DEVS_LIST]+1]->UPDATE_GTEXT(TRUE)
                    '¤¤¤#220: "Module deleted."
                    TMPSTR = LNG_SET_EXT(220,"Module deleted.")
                    MessageBar(TMPSTR)
                    IS_VALID = TRUE
               END IF
          CASE ControlAccess[CS_FILE_LIST]                  'DELETE FILE
               IF (LI > -1) THEN
                    FILE_NAME = FOLDER0.GetName(LI)
                    '¤¤¤#221: "Confirm permanent deletion of file: "
                    TMPSTR = LNG_SET_EXT(221,"Confirm permanent deletion of file: ")
                    TMPSTR &= FILE_NAME
                    FILE_NAME = FOLDER0.GetPath(LI) & "\" &  FILE_NAME
                    DB_Ask(CurrentID,TMPSTR,FALSE,FILE_NAME,DB_CONFIRM,LI,DEL_FILE)
                    Controls[CurrentID]->state.POWER             = TRUE
                    Controls[CurrentID]->state.DISABLED          = TRUE
                    IS_VALID = TRUE
               END IF
          CASE ControlAccess[CS_SAMPLE_LIST]                'DELETE SAMPLE
               IF (LI > -1) THEN 
                    DeleteSample(LI)
                    IS_VALID = TRUE
               END IF
          CASE ControlAccess[CS_MIDI_DEVS_LIST]+1           'RESET CC
               IF LI > 0 THEN  'DELETE LINK FOR SELECTED CONTROL AND CHANNEL
                    DIM AS IOS UPDATE_CONTROLS
                    DIM AS I32 CHAN = Controls[ControlAccess[CS_MIDICC_CHAN]]->text.si
                    MUTEXLOCK   SOUNDRA.MT_MIDICC
                    IF SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID] > 0 ANDALSO CHAN > -1 THEN
                         SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID]->LINK(LI-1,CHAN).RESET_LINK()
                         UPDATE_CONTROLS = TRUE
                    END IF
                    MUTEXUNLOCK SOUNDRA.MT_MIDICC
                    IF UPDATE_CONTROLS = TRUE THEN
                         Controls[ControlAccess[CS_MIDI_DEVS_LIST]+1]->UPDATE_GTEXT(TRUE)
                         Controls[ControlAccess[CS_MIDICT]]->text.si      = 0
                         Controls[ControlAccess[CS_MIDICT]]->text.content = MIDICT_TEXT(0)
                         Controls[ControlAccess[CS_MIDICT]]->UPDATE_GTEXT()
                         IF (SOUNDRA.SELECTED_MODULE > 0) ANDALSO _
                            (SOUNDRA.SELECTED_MODULE->SELECTED_PID > -1) THEN
                              Controls[ControlAccess[CS_MIDI2]]->state.power_level = _
                          SOUNDRA.SELECTED_MODULE->PARAM(SOUNDRA.SELECTED_MODULE->SELECTED_PID).MIDI_CONTROLS
                              Controls[ControlAccess[CS_MIDI2]]->UPDATE_GSTATE()
                         END IF
                    END IF
                    IS_VALID = TRUE
               ELSE
                    DIM AS IOS MAP_VALID
                    MUTEXLOCK   SOUNDRA.MT_MIDICC
                    IF SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID] > 0  THEN
                         FILE_NAME = SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID]->DEVICE_NAME
                         MAP_VALID = TRUE
                    END IF
                    MUTEXUNLOCK SOUNDRA.MT_MIDICC
                    IF MAP_VALID = TRUE THEN
                         '¤¤¤#222: "Confirm permanent deletion of MIDI Map for device: "
                         TMPSTR = LNG_SET_EXT(222,"Confirm permanent deletion of MIDI Map for device: ")
                         TMPSTR &= FILE_NAME
                         DB_Ask(CurrentID,TMPSTR,FALSE,,DB_CONFIRM,SOUNDRA.MIDICC_MAP_ID,DEL_MIDI_MAP)
                         Controls[CurrentID]->state.POWER    = TRUE
                         Controls[CurrentID]->state.DISABLED = TRUE
                    END IF
                    IS_VALID = TRUE
               END IF
          END SELECT
          
          'DEFAULT ACTION = TO DELETE SELECTED MODULE IF ANY
          IF IS_VALID = FALSE THEN 
               IF SOUNDRA.SELECTED_MODULE THEN
                    IF SOUNDRA.SELECTED_MODULE->PORT_TYPE = SOUNDRA.PORT_TYPES.DEFAULT THEN
                         ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
                         LI = SOUNDRA.SELECTED_MODULE->MOD_ID
                         IF (LI > -1) ANDALSO (LI < SOUNDRA.LI_MODULES.LENGTH) ANDALSO _
                            (SOUNDRA.MODULES[LI] > 0) THEN
                              DeleteModule(SOUNDRA.MODULES[LI])
                              Controls[ControlAccess[CS_MIDI_DEVS_LIST]+1]->UPDATE_GTEXT(TRUE)
                              '¤¤¤#223: "Module deleted."
                              TMPSTR = LNG_SET_EXT(223,"Module deleted.")
                              MessageBar(TMPSTR)
                              IS_VALID = TRUE
                         END IF
                    END IF
               END IF
          END IF
          IF IS_VALID = FALSE THEN 
               '¤¤¤#224: "Nothing to delete"
               TMPSTR = LNG_SET_EXT(224,"Nothing to delete")
               MessageBar(TMPSTR)
          END IF
     END IF
     
     RETURN 0
END FUNCTION
FUNCTION ctrl_li_save      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32     R, ID, LI=-1, DOT_POS, FILE_TYPE
          DIM AS IOS     SETTINGS_VISIBLE, SYNTH_LIST, IS_ANSWERED,ANSWER,IS_VALID
          DIM AS TXT     FILE_PATH, FILE_NAME, FILE_EXT, TMPSTR

          'CHECK DIALOG BOX ANSWER
          IS_ANSWERED = DB_DATA.IS_ANSWERED
          DB_DATA.is_answered  = FALSE
          IF IS_ANSWERED = TRUE THEN
               IF DB_DATA.answer = TRUE THEN
                    FILE_TYPE = DB_DATA.EXT_ID
                    FILE_NAME = Controls[TFAccess[TF_Dialog]]->TEXT.content
               END IF
               Controls[CurrentID]->state.POWER    = FALSE
               Controls[CurrentID]->state.DISABLED = FALSE
               ANSWER = TRUE
          ELSE
               SETTINGS_VISIBLE = Controls[Windows[W_TUNING]]->state.POWER
               ID = ControlAccess[CS_BROWSER_TAB0] + T_SESSIONS
               SYNTH_LIST       = Controls[ID]->state.POWER
               ID = FindActiveList()
               IF ID THEN LI = Controls[ID]->text.si
               'DETERMINE WHAT TO SAVE
               IF ID = ControlAccess[CS_FILE_LIST] THEN
                    IF LI > -1 THEN 'BY FILE EXTENSION
                         FILE_NAME = FOLDER0.GetName(LI)
                         DOT_POS   = INSTRREV(FILE_NAME,".")
                         IF DOT_POS THEN 
                              FILE_EXT = UCASE(MID(FILE_NAME,DOT_POS+1))
                              SELECT CASE FILE_EXT
                              CASE "SOUNDRA"
                                   FILE_TYPE = 1
                                   IS_VALID  = TRUE
                              CASE "TNG"
                                   FILE_TYPE = 2
                                   IS_VALID  = TRUE
                              END SELECT
                              IF IS_VALID = TRUE THEN
                                   '¤¤¤#225: "Confirm overwriting of"
                                   TMPSTR = LNG_SET_EXT(225,"Confirm overwriting of")
                                   TMPSTR &= ": " & FILE_NAME & " ?"
                                   DB_Ask(CurrentID,TMPSTR,FALSE,FILE_NAME,DB_CONFIRM, LI, FILE_TYPE)
                                   Controls[CurrentID]->state.POWER       = TRUE
                                   Controls[CurrentID]->state.DISABLED    = TRUE
                              END IF
                         END IF
                    ELSE'BY CONTEXT
                         IF     SYNTH_LIST THEN
                              FILE_TYPE = 4
                              ANSWER = TRUE
                         ELSEIF SETTINGS_VISIBLE THEN
                              FILE_TYPE = 5
                              ANSWER = TRUE
                         ELSE
                              FILE_TYPE = 1
                              FILE_NAME = SETTINGS.SESSION_FILE
                              ANSWER = TRUE
                         END IF                    
                    END IF
               ELSEIF ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY THEN 'MODULE FILE
                    FILE_TYPE = 0 '3
               END IF
               IF FILE_TYPE = 0 THEN 
                    FILE_TYPE = 4 'SAVE SESSION BY DEFAULT
                    ANSWER    = TRUE
               END IF
          END IF

          'SAVE ITEM
          FILE_PATH = CURDIR
          SELECT CASE FILE_TYPE
          CASE 1 'SYNTH FILE (SELECTED)
               IF ANSWER = TRUE THEN R = SaveSession(FILE_PATH, FILE_NAME)
          CASE 2 'TUNING FILE (SELECTED)
               IF ANSWER = TRUE THEN
                    TUNINGS(TUNING_CURRENT).FILEPATH = FILE_PATH
                    TUNINGS(TUNING_CURRENT).FILENAME = FILE_NAME
                    R = SaveTuning(@TUNINGS(TUNING_CURRENT))
               END IF
          CASE 3 'MODULE FILE (SELECTED)
          CASE 4 'SYNTH FILE (CURRENT SESSION)
               FILE_PATH = SETTINGS.SESSION_PATH
               FILE_NAME = SETTINGS.SESSION_FILE
               R = SaveSession(SETTINGS.SESSION_PATH, SETTINGS.SESSION_FILE)
          CASE 5 'TUNING FILE (CURRENT TUNING)
               FILE_PATH = TUNINGS(TUNING_CURRENT).FILEPATH
               FILE_NAME = TUNINGS(TUNING_CURRENT).FILENAME
               R = SaveTuning(@TUNINGS(TUNING_CURRENT))
          CASE ELSE
               '¤¤¤#226: "Nothing to save."
               TMPSTR = LNG_SET_EXT(226,"Nothing to save.")
               MessageBar(TMPSTR)
               RETURN 0
          END SELECT
          
          'CHECK ERROR
          IF R THEN 
               '¤¤¤#227: "Saving error: "
               TMPSTR = LNG_SET_EXT(227,"Saving error: ")
               TMPSTR &= ERR2TXT(R)
               TERMINAL(TMPSTR,MSG_ERROR)
               ShowMessages(MSG_SYSTEM)
          ELSEIF ANSWER = TRUE THEN
               IF FILEEXISTS(FILE_PATH & "\" & FILE_NAME) THEN
                    Browse(@FOLDER0,,,FALSE,FALSE)
                    SelectFile(FILE_NAME)
                    '¤¤¤#228: "has been saved."
                    TMPSTR = LNG_SET_EXT(228,"has been saved.")
                    TMPSTR  = FILE_NAME & " " & TMPSTR
                    MessageBar(TMPSTR)
               ELSE
                    '¤¤¤#229: "Unknown error while saving: "
                    TMPSTR = LNG_SET_EXT(229,"Unknown error while saving: ")
                    TMPSTR &= FILE_NAME
                    TERMINAL(TMPSTR,MSG_ERROR)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_li_load      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32     R,ID,LI
          DIM AS TXT     TMPSTR
          ID = FindActiveList()
          IF ID = 0  THEN 
               IF SOUNDRA.SELECTED_MODULE THEN
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
               ELSE
                    '¤¤¤#230: "No visible list from which to load."
                    TMPSTR = LNG_SET_EXT(230,"No visible list from which to load.")
                    MessageBar(TMPSTR)
                    RETURN 0
               END IF
          END IF
          
          LI = Controls[ID]->text.si
          IF LI > -1 THEN 
               IF ID = ControlAccess[CS_MODULE_LIST] + MOD_REFERENCE THEN LI = SOUNDRA.MM_MAP(LI)
          ELSEIF SOUNDRA.SELECTED_MODULE THEN
               IF SOUNDRA.SELECTED_MODULE->PORT_TYPE > SOUNDRA.PORT_TYPES.DEFAULT THEN
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_REFERENCE
                    LI = SOUNDRA.SELECTED_MODULE->FCT_ID
               ELSE
                    ID = ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
                    LI = SOUNDRA.SELECTED_MODULE->MOD_ID
               END IF
          END IF

          IF LI = -1 THEN 
               '¤¤¤#231: "No selected item to copy."
               TMPSTR = LNG_SET_EXT(231,"No selected item to copy.")
               MessageBar(TMPSTR)
               RETURN 0
          END IF
          
          SELECT CASE ID
          CASE ControlAccess[CS_MODULE_LIST] + MOD_REFERENCE
               DIM AS I32 X,Y, MOD_ID = SOUNDRA.FIND_TOP_MODULE()
               X = Controls[ControlAccess[CS_SYNTH_OUT]]->X
               Y = Controls[ControlAccess[CS_SYNTH_OUT]]->Y
               IF MOD_ID > -1 THEN IF SOUNDRA.MODULES[MOD_ID]->GUI_PTR THEN _
                  Y = SOUNDRA.MODULES[MOD_ID]->GUI_PTR->Y
               IF SOUNDRA.REFERENCE_MODULE(LI).GUI_PTR THEN _
                  Y -= SOUNDRA.REFERENCE_MODULE(LI).GUI_PTR->H + GRID_STEP * 2
               MOD_ID = CreateModule(LI,X,Y,TRUE)
          CASE ControlAccess[CS_MODULE_LIST] + MOD_LIBRARY
               IF LI < SOUNDRA.LI_MODULES.LENGTH THEN
                    DIM AS I32 X,Y, MOD_ID = SOUNDRA.FIND_TOP_MODULE()
                    X = Controls[ControlAccess[CS_SYNTH_OUT]]->X
                    Y = Controls[ControlAccess[CS_SYNTH_OUT]]->Y
                    IF MOD_ID > -1 THEN IF SOUNDRA.MODULES[MOD_ID]->GUI_PTR THEN _
                       Y = SOUNDRA.MODULES[MOD_ID]->GUI_PTR->Y
                    IF SOUNDRA.MODULES[LI]->GUI_PTR THEN Y -= SOUNDRA.MODULES[LI]->GUI_PTR->H + GRID_STEP * 2
                    MOD_ID = CreateModule(LI,X,Y,FALSE)
               END IF
          CASE ControlAccess[CS_FILE_LIST]
               DIM AS TXT               FILE_NAME, FILE_EXT
               DIM AS I32               DOT_POS
               FILE_NAME = FOLDER0.GetName(LI)
               IF NOT(FOLDER0.GetAttr(LI) AND fbDirectory) THEN
                    'RETRIEVE FILE EXTENSION
                    DOT_POS = INSTRREV(FILE_NAME,".")
                    IF DOT_POS THEN FILE_EXT = UCASE(MID(FILE_NAME,DOT_POS+1))
                    'ACTION
                    SELECT CASE FILE_EXT
                    CASE "WAV", "WAVE"
                         R = FIND_IN_WF_LIB(FOLDER0.GetPath(LI), FILE_NAME, TRUE)
                         IF R > -1 THEN 
                              '¤¤¤#232: "This file is already in the library."
                              TMPSTR = LNG_SET_EXT(232,"This file is already in the library.")
                              MessageBar(TMPSTR)
                         ELSE
                              R = LoadSample(FOLDER0.GetPath(LI),FILE_NAME)
                              IF R > -1 THEN
                                   '¤¤¤#233: "is now in the library."
                                   TMPSTR = LNG_SET_EXT(233,"is now in the library.")
                                   TMPSTR  = FILE_NAME & " " & TMPSTR
                                   MessageBar(TMPSTR)
                                   'SELECT NEXT ITEM IN LIST
                                   LI += IIF(Controls[ID]->text.reversed_index,-1,1)
                                   FLOOR(LI,0)
                                   CEIL(LI,FOLDER0.NbItems-1)
                                   Controls[ID]->text.si = LI
                                   Controls[ID]->text.update_vi(FOLDER0.NbItems)
                                   Controls[ID]->UPDATE_GTEXT()
                                   Controls[ControlAccess[CS_SAMPLE_LIST]]->UPDATE_GTEXT(TRUE)
                              END IF
                         END IF
                    CASE "TNG"
                         MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
                         R = LoadTuning(@Tunings(TUNING_COPY), FOLDER0.GetPath(LI), FILE_NAME)
                         MUTEXLOCK   MT_TUNINGS(TUNING_CURRENT)
                         IF R = 0 THEN
                              Tunings(TUNING_CURRENT) = Tunings(TUNING_COPY)
                              Controls[ControlAccess[CS_SP_LIST]]->UPDATE_GTEXT(TRUE)
                         ELSE
                              Tunings(TUNING_COPY)    = Tunings(TUNING_CURRENT)
                         END IF
                         MUTEXUNLOCK MT_TUNINGS(TUNING_CURRENT)
                         MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
                         UpdateTuningControls()
                         SOUNDRA.SETUP_MOD_TUNING()
                    CASE "SOUNDRA"
                         'SAVE CURRENT SYNTH
                         IF SETTINGS.AUTOSAVE = TRUE THEN
                              R = SaveSession(SETTINGS.SESSION_PATH, SETTINGS.SESSION_FILE)
                              IF R < 0 THEN TERMINAL("SaveSession(): " & ERR2TXT(R),MSG_ERROR)
                         END IF
                         R = LoadSession(FOLDER0.GetPath(LI),FILE_NAME)
                    CASE "MEL"
                         R = LoadMelody(FOLDER0.GetPath(LI),FILE_NAME)
                    CASE ELSE
                         '¤¤¤#234: "The selected file is not supported."
                         TMPSTR = LNG_SET_EXT(234,"The selected file is not supported.")
                         MessageBar(TMPSTR)
                    END SELECT
               END IF
          CASE ControlAccess[CS_CONSOLE_LIST] + MSG_ACTIONS
               'REVERT ACTIONS UP TO THE POINT OF THE SELECTED ITEM
               
          CASE ControlAccess[CS_SAMPLE_LIST]
               R = LoadSample(WF_Library[LI],Controls[ControlAccess[CS_SAMPLE_C]]->text.si)
               IF R > -1 THEN
                    '¤¤¤#235: "is now loaded in memory."
                    TMPSTR = LNG_SET_EXT(235,"is now loaded in memory.")
                    TMPSTR  = WF_Library[LI]->FILENAME & " " & TMPSTR
                    MessageBar(TMPSTR)
                    LI += IIF(Controls[ID]->text.reversed_index,-1,1)
                    FLOOR(LI,0)
                    CEIL(LI,LI_WF_Library.LENGTH-1)
                    Controls[ID]->text.si = LI
                    Controls[ID]->text.update_vi(LI_WF_Library.LENGTH)
                    Controls[ID]->UPDATE_GTEXT()
                    'SHOW SAMPLE LIST IF NOT VISIBLE
                    IF SOUNDRA.SELECTED_MODULE = 0 THEN
                         SelectModule(@SOUNDRA.REFERENCE_MODULE(SOUNDRA.MM_SAMPLE))
                    ELSEIF SOUNDRA.SELECTED_MODULE->FCT_ID <> SOUNDRA.MM_SAMPLE THEN
                         SelectModule(@SOUNDRA.REFERENCE_MODULE(SOUNDRA.MM_SAMPLE))
                    END IF
                    ID = ControlAccess[CS_PARAM_TAB0] + PARAM_SAMPLES
                    IF Controls[ID]->VISIBLE THEN 
                         IF Controls[ID]->state.POWER = FALSE THEN
                              ctrl_param_tabs(CA_MD,0,0,ID)
                              ctrl_update(ID)
                         END IF
                    END IF
               END IF
          END SELECT
          IF R < 0 THEN 
               '¤¤¤#236: "Loading error: "
               TMPSTR = LNG_SET_EXT(236,"Loading error: ")
               TMPSTR &= ERR2TXT(R)
               TERMINAL(TMPSTR,MSG_ERROR)
          END IF
     END IF

     RETURN 0
END FUNCTION

FUNCTION ctrl_new_synth    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 R
          DIM AS TXT FILE_PATH, FILE_NAME
          DIM AS THREAD_MESSAGE TMP_MSG
          
          'SAVE CURRENT SYNTH
          IF SETTINGS.AUTOSAVE = TRUE ANDALSO SETTINGS.SESSION_FILE <> "" THEN
               R = SaveSession(SETTINGS.SESSION_PATH, SETTINGS.SESSION_FILE)
               IF R < 0 THEN TERMINAL("SaveSession(): " & ERR2TXT(R),MSG_ERROR)
          END IF
          'MODULES
          SelectModule(0)
          Controls[ControlAccess[CS_SYNTH_OUT]]->state.POWER = FALSE
          Controls[ControlAccess[CS_SYNTH_OUT]]->UPDATE_GSTATE()
          SOUNDRA.DELETE_ALL_MODULES()
          'MIDI
          SOUNDRA.RESET_MIDI_MAPS()
          REFRESH_MIDI_LISTS(TRUE)
          SOUNDRA.RESET_BOARDS()
          
          SOUNDRA.RESET_MASTER_PRESETS()

          'PLANE
          TMP_MSG.CODE    = _REMOTE_CTRL
          TMP_MSG.DEST_ID = ControlAccess[CS_SYNTH_CENTER]
          TMP_MSG.TIME    = TIMER
          TMP_MSG.ACTION  = CA_CLICK
          SENDMTMSG(TMP_MSG)

          'NEW FILE
          FILE_PATH = HOMEPATH & "\" & "SYNTHS"   '!!! USE CURDIR?
          FILE_NAME = "NewSynth_" & TemporalStamp() & ".soundra"
          R = SaveSession(FILE_PATH, FILE_NAME)

          'UPDATE FILE LIST & TAB
          Browse(@FOLDER0,,,FALSE,FALSE)
          SelectFile(SETTINGS.SESSION_FILE)
          Controls[ControlAccess[CS_SESSION_TAB0]]->text.content = " " & SETTINGS.SESSION_NAME
          Controls[ControlAccess[CS_SESSION_TAB0]]->UPDATE_GTEXT()
          'MODULE LIST
          Controls[ControlAccess[CS_MODULE_LIST]+MOD_LIBRARY]->UPDATE_GTEXT(TRUE)

          'UPDATE KEYBOARDs
          Controls[ControlAccess[CS_BB_MODE]]->state.POWER = _
                     (SOUNDRA.BB[SOUNDRA.BB_NUM]->KEY_MODE = SOUNDRA.KEY_MODES.ISLAND)
          Controls[ControlAccess[CS_BB_MODE]]->text.si     = SOUNDRA.BB[SOUNDRA.BB_NUM]->KEY_MODE
          Controls[ControlAccess[CS_BB_MODE]]->UPDATE_GDATA(TRUE)
          
          Controls[ControlAccess[CS_BB_LOCK]]->state.POWER = SOUNDRA.BB[SOUNDRA.BB_NUM]->LOCK_MODE
          Controls[ControlAccess[CS_BB_LOCK]]->UPDATE_GSTATE()
          
          Controls[ControlAccess[CS_BB]]->UPDATE_GTEXT(TRUE)
          
          Controls[ControlAccess[CS_KB_POS]]->text.si      = SOUNDRA.KB[SOUNDRA.KB_NUM]->K_OFFSET.KEY-_
                                                           SOUNDRA.OCTAVE_MIN*TUNINGS(TUNING_CURRENT).NB_KEYS
          Controls[ControlAccess[CS_KB_POS]]->UPDATE_GTEXT(TRUE)
          
          Controls[ControlAccess[CS_KB_MODE]]->text.si     = SOUNDRA.KB[SOUNDRA.KB_NUM]->KEY_MODE
          Controls[ControlAccess[CS_KB_MODE]]->state.POWER = _
                     (SOUNDRA.KB[SOUNDRA.KB_NUM]->KEY_MODE = SOUNDRA.KEY_MODES.ISLAND)
          Controls[ControlAccess[CS_KB_MODE]]->UPDATE_GDATA(TRUE)
                    
          Controls[ControlAccess[CS_KB_LOCK]]->state.POWER = SOUNDRA.KB[SOUNDRA.KB_NUM]->LOCK_MODE
          Controls[ControlAccess[CS_KB_LOCK]]->UPDATE_GSTATE()
          
          Controls[ControlAccess[CS_KB]]->UPDATE_GTEXT(TRUE)

          FlushActionLog()
     END IF
     RETURN 0
END FUNCTION

'------------------------------------------------------------------------------------------------------------
' MESSAGE CONSOLE

FUNCTION ctrl_console_tabs (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_MD) THEN
          DIM AS I32 ID, INDEX
          ID = ControlAccess[CS_CONSOLE_TAB0]
          Controls[ID]->state.power   = FALSE
          Controls[ID]->UPDATE_GSTATE()
          Controls[ID+1]->state.power = FALSE
          Controls[ID+1]->UPDATE_GSTATE()
          Controls[ID+2]->state.power = FALSE
          Controls[ID+2]->UPDATE_GSTATE()
          'HIDE LISTS
          ID = ControlAccess[CS_CONSOLE_LIST]
          Controls[ID+MSG_SYSTEM]->VISIBLE  = FALSE
          Controls[ID+MSG_ACTIONS]->VISIBLE = FALSE
          'SET TAB & SHOW LIST
          INDEX = Controls[CurrentID]->INDEX
          Controls[CurrentID]->state.power = TRUE
          Controls[ID+INDEX]->VISIBLE      = TRUE
          Controls[ControlAccess[CS_LI_ORDER]]->text.si = Controls[ID+INDEX]->text.reversed_index + 1
          Controls[ControlAccess[CS_LI_ORDER]]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_system_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS I32 nb_msg
     Activation_BState(ACTION,Controls[CurrentID]->state)
     MUTEXLOCK   LI_ConsoleMSG.MUTEX
     nb_msg = LI_ConsoleMSG.LENGTH
     MUTEXUNLOCK LI_ConsoleMSG.MUTEX
     ListControl(Controls[CurrentID], nb_msg)
     RETURN 0
END FUNCTION
FUNCTION ctrl_action_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], LI_LOG_ACTIONS.LENGTH)
     RETURN 0
END FUNCTION

'------------------------------------------------------------------------------------------------------------
' FILE BROWSER
#MACRO RESET_LIST_SELECTION(RLS_CID)
     Controls[RLS_CID]->text.si       = -1
     Controls[RLS_CID]->text.hi       = -1
     Controls[RLS_CID]->text.vi       = 0
     Controls[RLS_CID]->UPDATE_GTEXT(TRUE)
#ENDMACRO
FUNCTION ctrl_file_list    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], FOLDER0.LI_ITEM.LENGTH)
     'OPEN FOLDER?
     IF (ACTION AND CA_CLICK) THEN
          I = Controls[CurrentID]->text.si
          IF I > -1 THEN
               IF FOLDER0.GetAttr(I) AND fbDirectory THEN
                    DIM AS TXT TMPSTR = FOLDER0.GetName(I)
                    CHDIR(TMPSTR)
                    RESET_LIST_SELECTION(ControlAccess[CS_FILE_LIST])
                    Browse(@FOLDER0,,,FALSE,FALSE)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_file_up      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          CHDIR("..")
          Browse(@FOLDER0,,,FALSE,FALSE)
          RESET_LIST_SELECTION(ControlAccess[CS_FILE_LIST])
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_file_home    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          CHDIR(HOMEPATH)
          Browse(@FOLDER0,,,FALSE,FALSE)
          Controls[ControlAccess[CS_DRIVES]]->TEXT.si = DRIVES.CurDriveIndex()
          Controls[ControlAccess[CS_DRIVES]]->UPDATE_GTEXT()
          RESET_LIST_SELECTION(ControlAccess[CS_FILE_LIST])
     END IF     
     RETURN 0
END FUNCTION
FUNCTION ctrl_file_ref     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          Browse(@FOLDER0,,,FALSE,FALSE)
          RESET_LIST_SELECTION(ControlAccess[CS_FILE_LIST])
          'Drives
          ScanDrives(@DRIVES)
          Controls[ControlAccess[CS_DRIVES]]->TEXT.si = DRIVES.CurDriveIndex()
          Controls[ControlAccess[CS_DRIVES]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_file_drives  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)  
     IF (ACTION AND CA_HOVER) OR (ACTION AND CA_MD) OR (ACTION AND CA_CLICK) THEN
          DIM AS I32     R, W,H
          DIM AS F32     W2
          DIM AS TXT     TMPSTR
          '¤¤¤#237: "Available drives. Click to open."
          TMPSTR = LNG_SET_EXT(237,"Available drives. Click to open.")
          'CALCULATIONS
          X  -= Controls[CurrentID]->X
          W   = Controls[CurrentID]->W
          W2  = (W - BORDER_SIZE*2) / DRIVES.NB_DRIVES
          'DRIVE
          IF (X > 0) AND (X < W - BORDER_SIZE*2) THEN
               DIM AS I32 I = INT(X / W2) + 1 'DRIVE NUMBER
               FLOOR(I,0)
               CEIL(I,DRIVES.NB_DRIVES)
               IF (ACTION AND CA_HOVER)      THEN 'HOVER
                    Controls[CurrentID]->text.hi = I
                    '¤¤¤#238: "Drive #{$}: "
                    TMPSTR = LNG_SET_EXT(238,"Drive #{$}: ")
                    TMPSTR  = TXTINSERT(TMPSTR,STR(I))
                    TMPSTR &= DRIVES.DriveLetters(I)
               ELSEIF (ACTION AND CA_CLICK)  THEN 'OPEN
                    Controls[CurrentID]->text.si = I
                    R = CHDIR(DRIVES.DriveLetters(I) & ":/")
                    IF R <> 0 THEN
                         ScanDrives(@DRIVES)
                         Controls[CurrentID]->text.si = DRIVES.CurDriveIndex()
                         Controls[CurrentID]->text.UPDATED = TRUE
                    END IF
                    Browse(@FOLDER0,,,FALSE,FALSE)
                    RESET_LIST_SELECTION(ControlAccess[CS_FILE_LIST])
               END IF
          ELSE
               Controls[CurrentID]->text.hi = -1
          END IF
          Controls[CurrentID]->CTRL_INFO = TMPSTR
     ELSE
          Controls[CurrentID]->text.hi = -1
     END IF
     RETURN 0
END FUNCTION

'------------------------------------------------------------------------------------------------------------
' LIBRARIES

FUNCTION ctrl_mod_tabs     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_MD) THEN
          DIM AS I32 ID, INDEX = Controls[CurrentID]->INDEX
          'TURN OFF TABS
          ID = ControlAccess[CS_MODULE_TAB0]
          Controls[ID]->state.power   = FALSE
          Controls[ID]->UPDATE_GSTATE()
          Controls[ID+1]->state.power = FALSE
          Controls[ID+1]->UPDATE_GSTATE()
          Controls[ID+2]->state.power = FALSE
          Controls[ID+2]->UPDATE_GSTATE()
          'HIDE LISTS
          ID = ControlAccess[CS_MODULE_LIST]
          Controls[ID+MOD_REFERENCE]->VISIBLE  = FALSE
          Controls[ID+MOD_LIBRARY]->VISIBLE    = FALSE
          'SET TAB & SHOW LIST
          Controls[CurrentID]->state.power = TRUE
          Controls[ID+INDEX]->VISIBLE      = TRUE
          Controls[ControlAccess[CS_LI_ORDER]]->text.si = Controls[ID+INDEX]->text.reversed_index + 1
          Controls[ControlAccess[CS_LI_ORDER]]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_reference_list(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], SOUNDRA.CATALOG_SIZE)
     IF (ACTION AND CA_MD) THEN
          DIM AS I32 HID = Controls[CurrentID]->TEXT.hi
          SOUNDRA.DRAG_MOD_ID = IIF(HID > -1,SOUNDRA.MM_MAP(HID),-1)
     ELSEIF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          IF SID > -1 THEN
               SelectModule(@SOUNDRA.REFERENCE_MODULE(SOUNDRA.MM_MAP(SID)))
          ELSE
               SelectModule(0)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_library_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     STATIC AS I32 LS
     MUTEXLOCK   SOUNDRA.LI_MODULES.MUTEX
     LS = SOUNDRA.LI_MODULES.LENGTH
     MUTEXUNLOCK SOUNDRA.LI_MODULES.MUTEX
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], LS)
     IF (ACTION AND CA_MD) THEN
          SOUNDRA.DRAG_MOD_ID = Controls[CurrentID]->TEXT.hi
     ELSEIF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          IF SID > -1 THEN
               MUTEXLOCK   SOUNDRA.LI_MODULES.MUTEX
               IF (SOUNDRA.MODULES[SID] > 0) ANDALSO (SID = SOUNDRA.MODULES[SID]->MOD_ID) THEN
                    SelectModule(SOUNDRA.MODULES[SID])
               ELSE
                    SelectModule(0)
               END IF
               MUTEXUNLOCK SOUNDRA.LI_MODULES.MUTEX
          ELSE
               SelectModule(0)
          END IF
     END IF
     RETURN 0
END FUNCTION

'SAMPLE LIBRARY
FUNCTION ctrl_sample_mode  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],3,X,Controls[CurrentID]->W,FALSE)
     'UPDATE TEXT
     IF UPDATED THEN
          DIM AS I32 I = Controls[CurrentID]->text.si
          DIM AS TXT TMPSTR
          IF     I = W_PREPEND     THEN
               '¤¤¤#239: "Prepend"
               TMPSTR = LNG_SET_EXT(239,"Prepend")
          ELSEIF I = W_OVERWRITE   THEN
               '¤¤¤#240: "Overwrite"
               TMPSTR = LNG_SET_EXT(240,"Overwrite")
          ELSEIF I = W_APPEND      THEN
               '¤¤¤#241: "Append"
               TMPSTR = LNG_SET_EXT(241,"Append")
          END IF
          Controls[CurrentID]->text.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_sample_chan  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],4,X,Controls[CurrentID]->W,FALSE)
     'UPDATE TEXT
     IF UPDATED THEN
          DIM AS I32 I = Controls[CurrentID]->text.si
          DIM AS TXT TMPSTR
          IF     I = MONO_IO  THEN
               TMPSTR = SCHAN_NAME(1)
          ELSEIF I = LEFT_IO  THEN
               TMPSTR = "L"
          ELSEIF I = RIGHT_IO THEN
               TMPSTR = "R"
          ELSEIF I = LEFT_IO + RIGHT_IO THEN
               TMPSTR = SCHAN_NAME(2)
          END IF
          Controls[CurrentID]->text.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_sample_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32 LS
     MUTEXLOCK   LI_WF_Library.MUTEX
     LS = LI_WF_Library.LENGTH 
     MUTEXUNLOCK LI_WF_Library.MUTEX
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], LS)
     IF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          IF SID > -1 THEN
               DIM AS I32 NBSC = 1
               MUTEXLOCK   LI_WF_Library.MUTEX
               IF WF_Library[SID]->STATUS > WFS_FREE THEN
                    SID  = -1
                    Controls[CurrentID]->TEXT.si = -1
               ELSE
                    NBSC = WF_Library[SID]->WaveFile.WaveFormat.nbChannels
               END IF
               MUTEXUNLOCK LI_WF_Library.MUTEX
               IF NBSC = 1 THEN
                    Controls[ControlAccess[CS_SAMPLE_C]]->text.si        = 0
                    Controls[ControlAccess[CS_SAMPLE_C]]->text.content   = SCHAN_NAME(1)
                    Controls[ControlAccess[CS_SAMPLE_C]]->state.DISABLED = TRUE      
                    Controls[ControlAccess[CS_SAMPLE_C]]->UPDATE_GDATA()
               ELSE
                    Controls[ControlAccess[CS_SAMPLE_C]]->text.si        = 3
                    Controls[ControlAccess[CS_SAMPLE_C]]->text.content   = SCHAN_NAME(2)
                    Controls[ControlAccess[CS_SAMPLE_C]]->state.DISABLED = FALSE
                    Controls[ControlAccess[CS_SAMPLE_C]]->UPDATE_GDATA()
               END IF
          ELSE
               Controls[ControlAccess[CS_SAMPLE_C]]->state.DISABLED = TRUE
               Controls[ControlAccess[CS_SAMPLE_C]]->UPDATE_GSTATE()
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_sample_list_m(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32 LS
     MUTEXLOCK   LI_WF_Sample.MUTEX
     LS = LI_WF_Sample.LENGTH 
     MUTEXUNLOCK LI_WF_Sample.MUTEX
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], LS)
     IF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          IF SID > -1 THEN
               MUTEXLOCK   LI_WF_Sample.MUTEX
               IF (WF_Sample[SID] > 0) ANDALSO (WF_Sample[SID]->STATUS = WFS_FREE) THEN 
                   Controls[CurrentID]->TEXT.si = SID
               ELSE
                   Controls[CurrentID]->TEXT.si = -1
               END IF
               MUTEXUNLOCK LI_WF_Sample.MUTEX
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_MIDICC_CHAN     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],NB_MIDI_CHANNELS_MAX,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED = TRUE THEN
          DIM AS I32 I
          DIM AS TXT TMPSTR
          I = Controls[CurrentID]->text.si
          Controls[CurrentID]->text.content = "Ch. " & STR(I)
          SOUNDRA.MIDICC_CHAN = I
          Controls[ControlAccess[CS_MIDI_DEVS_LIST]+1]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_MIDICT          (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],NB_MIDICT,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED = TRUE THEN
          DIM AS I32 NUM, CHAN = SOUNDRA.MIDICC_CHAN, I = Controls[CurrentID]->text.si
          DIM AS TXT TMPSTR
          FLOOR(I,0)
          Controls[CurrentID]->text.content = MIDICT_TEXT(I)
          NUM = Controls[ControlAccess[CS_MIDI_DEVS_LIST]+1]->text.si - 1
          MUTEXLOCK   SOUNDRA.MT_MIDICC
          IF SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID] > 0 THEN _
             SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID]->LINK(NUM,CHAN).MIDICT = I
          MUTEXUNLOCK SOUNDRA.MT_MIDICC
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_MIDI_DEVS_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID],NB_MIDI_DEVICES_MAX)
     IF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          IF SID > -1 THEN
               DIM AS IOS COND
               MUTEXLOCK   SOUNDRA.MT_MIDICC
               COND = (SOUNDRA.MIDI_MAP[SID] > 0) AND (TRIM(SOUNDRA.MIDI_MAP[SID]->DEVICE_NAME) <> "")
               IF COND THEN SOUNDRA.MIDICC_MAP_ID = SID
               MUTEXUNLOCK SOUNDRA.MT_MIDICC
               IF COND THEN 
                    'TOGGLE LIST VISIBILITY
                    Controls[CurrentID+1]->TEXT.si = -1
                    Controls[CurrentID+1]->UPDATE_GTEXT()
                    Controls[CurrentID+1]->VISIBLE = TRUE
                    Controls[CurrentID]->VISIBLE   = FALSE
                    Controls[ControlAccess[CS_MIDICC_CHAN]]->STATE.DISABLED = FALSE
                    Controls[ControlAccess[CS_MIDICC_CHAN]]->UPDATE_GSTATE()
                    Controls[ControlAccess[CS_MIDICT]]->STATE.DISABLED = TRUE
                    Controls[ControlAccess[CS_MIDICT]]->UPDATE_GSTATE()
               END IF
          END IF
          Controls[CurrentID]->TEXT.si = -1
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_MIDICC_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS IOS UPDATE_CT
     STATIC AS I32 LS = NB_MIDI_CONTROLS_MAX+1
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID],LS)
     DIM AS I32 SID
     IF (ACTION AND CA_CLICK) THEN
          SID = Controls[CurrentID]->TEXT.si
          Controls[ControlAccess[CS_MIDICT]]->state.DISABLED = TRUE
          Controls[ControlAccess[CS_MIDICT]]->UPDATE_GSTATE()
          IF SID > -1 THEN
               IF SID = 0 THEN 'GO BACK
                    Controls[ControlAccess[CS_MIDICC_CHAN]]->state.DISABLED = TRUE
                    Controls[ControlAccess[CS_MIDICC_CHAN]]->UPDATE_GSTATE()
                    Controls[CurrentID]->TEXT.si   = -1
                    Controls[CurrentID-1]->VISIBLE = TRUE
                    Controls[CurrentID]->VISIBLE   = FALSE
               ELSE
                    UPDATE_CT = TRUE
               END IF
          END IF
     ELSEIF OriginID = -1 AND Controls[ControlAccess[CS_MIDICT]]->state.DISABLED = FALSE THEN
          DIM AS I32 CHAN = SOUNDRA.MIDICC_CHAN
          SID = Controls[CurrentID]->TEXT.si
          IF SID > 0 THEN UPDATE_CT = TRUE
     END IF
     IF UPDATE_CT = TRUE THEN
          DIM AS I32 I,CHAN = SOUNDRA.MIDICC_CHAN
          MUTEXLOCK   SOUNDRA.MT_MIDICC
          IF SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID] > 0 THEN _
             I = SOUNDRA.MIDI_MAP[SOUNDRA.MIDICC_MAP_ID]->LINK(SID-1,CHAN).MIDICT
          MUTEXUNLOCK SOUNDRA.MT_MIDICC
          Controls[ControlAccess[CS_MIDICT]]->text.si        = I
          Controls[ControlAccess[CS_MIDICT]]->text.content   = MIDICT_TEXT(I)
          Controls[ControlAccess[CS_MIDICT]]->state.DISABLED = FALSE
          Controls[ControlAccess[CS_MIDICT]]->UPDATE_GDATA()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_sample_record(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               IF MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP) = FALSE THEN 
                    IF MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) = TRUE THEN
                         MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP) = TRUE
                    ELSE
                         DIM AS I32 R,ID
                         DIM AS TXT TMPSTR
                         DIM AS TXT FILENAME = "Rec" & TemporalStamp() & ".wav"
                         DIM AS TXT FILEPATH = HOMEPATH & "\AUDIO\RECORDED"
                         R = CreateBlankWF(FILEPATH, FILENAME, MOD_PTR->PARAM(6).PortValue, _
                                           MOD_PTR->PORT(5)->NBSC)
                         IF R THEN 
                              '¤¤¤#242: "Error creating new wave file: "
                              TMPSTR = LNG_SET_EXT(242,"Error creating new wave file: ")
                              TMPSTR &= ERR2TXT(R)
                              TERMINAL(TMPSTR,MSG_ERROR)
                         ELSE
                              ID = LI_WF_Sample.LENGTH-1
                              SET_MOD_SAMPLE(MOD_PTR, WF_Sample[ID], TRUE)
                         END IF
                    END IF
               END IF
               Controls[CurrentID]->state.POWER = MOD_PTR->FLAGS(SOUNDRA.MF_RECORD)
               Controls[CurrentID]->CLR = _
                  IIF(MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP)=TRUE,RGB(200,100,0),0)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_sample_set(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)     AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          DIM AS I32 LI = Controls[ControlAccess[CS_SAMPLE_LIST_M]]->text.si
          IF MOD_PTR > 0 THEN
               IF MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) = FALSE THEN
                    IF (LI = -1) THEN 'UNSELECT
                         SET_MOD_SAMPLE(MOD_PTR, 0)
                    ELSEIF (LI > -1) AND (LI < LI_WF_Sample.LENGTH) THEN
                         DIM AS F32 TEMPO
                         IF WF_Sample[LI]->STATUS = WFS_RESTRICTED  OR _
                            WF_Sample[LI]->WaveFile.Audiodata = 0   THEN RETURN 0
                         TEMPO = WF_Sample[LI]->F0(0) / SOUNDRA.F_MAX
                         IF WF_Sample[LI]->ResamplingFactor > 0  AND _
                            WF_Sample[LI]->ResamplingFactor <> 1 THEN _
                             TEMPO /= WF_Sample[LI]->ResamplingFactor
                         MOD_PTR->SET_VALUE(2,TEMPO)
                         SET_MOD_SAMPLE(MOD_PTR, WF_Sample[LI])
                    END IF
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_sample_save(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)    AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 LI = Controls[ControlAccess[CS_SAMPLE_LIST_M]]->text.si
          IF LI > -1 THEN
               DIM AS TXT TMPSTR
               'CHECK IF FILE IS RESTRICTED
               IF (WF_Sample[LI]->STATUS = WFS_RESTRICTED) OR (WF_Sample[LI]->WAVEFILE.AUDIODATA = 0) THEN _
                  RETURN 0
               'SAVE IF NOT ON DISK
               IF WF_Sample[LI]->ON_DISK = FALSE THEN
                    DIM AS I32 R
                    'SAVE TO DISK
                    IF SaveWF(WF_Sample[LI]) THEN 
                         '¤¤¤#243: "Error saving file: "
                         TMPSTR = LNG_SET_EXT(243,"Error saving file: ")
                         TMPSTR &= ERR2TXT(R)
                         TERMINAL(TMPSTR,MSG_ERROR)
                    ELSE
                         '¤¤¤#244: "has been saved."
                         TMPSTR = LNG_SET_EXT(244,"has been saved.")
                         TMPSTR  = WF_Sample[LI]->FILENAME & " " & TMPSTR
                         MessageBar(TMPSTR)
                         IF FindActiveList() = ControlAccess[CS_FILE_LIST] THEN
                              Browse(@FOLDER0,,,FALSE,FALSE)
                              Controls[ControlAccess[CS_FILE_LIST]]->UPDATE_GTEXT(TRUE)
                              SelectFile(WF_Sample[LI]->FILENAME)
                         END IF
                         'LOAD IN LIBRARY
                         R = FIND_IN_WF_LIB(WF_Sample[LI]->PATH, WF_Sample[LI]->FILENAME, FALSE)
                         IF R = -1 THEN
                              R = LoadSample(WF_Sample[LI]->PATH, WF_Sample[LI]->FILENAME)
                              IF R < 0 THEN 
                                   '¤¤¤#245: "Error loading to library: "
                                   TMPSTR = LNG_SET_EXT(245,"Error loading to library: ")
                                   TMPSTR &= ERR2TXT(R)
                                   TERMINAL(TMPSTR,MSG_ERROR)
                              END IF
                         END IF
                    END IF
               ELSE
                    '¤¤¤#246: "This file is already saved."
                    TMPSTR = LNG_SET_EXT(246,"This file is already saved.")
                    MessageBar(TMPSTR)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_sample_del(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)     AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 LI = Controls[ControlAccess[CS_SAMPLE_LIST_M]]->text.si
          CEIL(LI,LI_WF_Sample.LENGTH-1)
          IF LI > -1 THEN
               DIM AS I32 I,FCT_ID = SOUNDRA.MM_SAMPLE
               DIM AS TXT FULL_FILE_NAME
               'CHECK IF FILE IS RESTRICTED
               IF WF_Sample[LI]->STATUS = WFS_RESTRICTED THEN RETURN 0
               'UNLOAD FROM ALL SAMPLE MODULES THAT USE IT
               FOR I = 0 TO SOUNDRA.LI_MODULES.LENGTH-1
                    IF SOUNDRA.MODULES[I]->FCT_ID = FCT_ID            AND _
                       SOUNDRA.MODULES[I]->WF_PTR = WF_Sample[LI]     THEN
                         IF SOUNDRA.MODULE_STATUS(SOUNDRA.MODULES[I]) = SOUNDRA.SYNTH_STATES.LIVE THEN
                              'MSG TO SYNTH
                              SOUNDRA.WAIT4SYNTHUPDATE()
                              SOUNDRA.SU_DATA.MSG           = SOUNDRA.SYNTH_UPDATE_TYPES.SAMPLE
                              SOUNDRA.SU_DATA.MOD_PTR_IN    = SOUNDRA.MODULES[I]
                              SOUNDRA.SU_DATA.WF_PTR        = 0
                              SOUNDRA.SU_DATA.DONE          = FALSE
                         ELSE
                              'UNPLUG
                              SOUNDRA.MODULES[I]->WF_PTR    = 0
                              SOUNDRA.MODULES[I]->AudioData = 0
                         END IF
                    END IF
               NEXT I
               
               'UNLOAD FROM REFERENCE SAMPLE TOO
               IF SOUNDRA.REFERENCE_MODULE(FCT_ID).WF_PTR = WF_Sample[LI] THEN
                    SOUNDRA.REFERENCE_MODULE(FCT_ID).WF_PTR    = 0
                    SOUNDRA.REFERENCE_MODULE(FCT_ID).AudioData = 0
               END IF
               
               'DELETE FROM SAMPLE LIST
               MUTEXLOCK   LI_WF_Sample.MUTEX
               IF LI_WF_Sample.LENGTH > 1 THEN
                    FOR I = LI TO LI_WF_Sample.LENGTH - 2
                         SWAP WF_Sample[I],WF_Sample[I+1]
                         WF_Sample[I]->ID = I
                    NEXT I
               END IF
               LI_WF_Sample.LENGTH -= 1
               DELETE WF_Sample[LI_WF_Sample.LENGTH]
               MUTEXUNLOCK LI_WF_Sample.MUTEX
               CEIL(LI,LI_WF_Sample.LENGTH-1)
               Controls[ControlAccess[CS_SAMPLE_LIST_M]]->text.si = LI
               Controls[ControlAccess[CS_SAMPLE_LIST_M]]->UPDATE_GTEXT(TRUE)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_filter_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32 LS
     DIM AS IOS DISABLE_CONTROLS = FALSE
     MUTEXLOCK   LI_WF_Filter.MUTEX
     LS = LI_WF_Filter.LENGTH 
     MUTEXUNLOCK LI_WF_Filter.MUTEX
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], LS)
     IF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          SOUNDRA.FD.LI = -1
          IF SID > -1 THEN
               MUTEXLOCK   LI_WF_Filter.MUTEX
               IF     (WF_Filter[SID] = 0) THEN
                    SOUNDRA.FD.LI = -1
               ELSEIF (WF_Filter[SID]->STATUS > WFS_FREE) OR (WF_Filter[SID]->CONTENT < WFC_FILE) THEN
                    SOUNDRA.FD.LI = -1
               ELSE
                    SOUNDRA.FD.LI = SID
               END IF
               MUTEXUNLOCK LI_WF_Filter.MUTEX
          END IF
          'RESET PARAMS
          SOUNDRA.FD.FILE_OFFSET = 0
          SOUNDRA.FD.HI     = FALSE
          SOUNDRA.FD.WIN_ID = 1
          SOUNDRA.FD.K      = 8
          'LENGTH
          IF SOUNDRA.FD.LI > -1 THEN
               SOUNDRA.FD.N = WF_Filter[SOUNDRA.FD.LI]->WAVEFILE.FACT
               CEIL(SOUNDRA.FD.N,SOUNDRA.FILTER_WIN_SIZE_MAX)
          ELSE
               SOUNDRA.FD.N = 16
          END IF
          SOUNDRA.FIR_GENERATE(TRUE)
          'CONTROLS
          Controls[CurrentID]->TEXT.si = SID
          Controls[ControlAccess[CS_FILTER_WINDOW]+1]->state.POWER = FALSE
          Controls[ControlAccess[CS_FILTER_WINDOW]+1]->UPDATE_GSTATE()
          
          Controls[ControlAccess[CS_FILTER_WINDOW]]->text.si       = SOUNDRA.FD.WIN_ID
     Controls[ControlAccess[CS_FILTER_WINDOW]]->text.content  = SOUNDRA.FILTER_WINDOW_NAME(SOUNDRA.FD.WIN_ID)
     Controls[ControlAccess[CS_FILTER_WINDOW]]->UPDATE_GTEXT()
     
          Controls[ControlAccess[CS_FILTER_K]]->text.si            = SOUNDRA.FD.K
          Controls[ControlAccess[CS_FILTER_K]]->text.content       = "K = " & STR(SOUNDRA.FD.K)
          Controls[ControlAccess[CS_FILTER_K]]->UPDATE_GTEXT()
          
          Controls[ControlAccess[CS_FILTER_SIZE]]->text.si         = SOUNDRA.FD.N-SOUNDRA.FILTER_WIN_SIZE_MIN
          Controls[ControlAccess[CS_FILTER_SIZE]]->text.content    = "N = " & STR(SOUNDRA.FD.N)
          Controls[ControlAccess[CS_FILTER_SIZE]]->UPDATE_GTEXT()
          
          Controls[ControlAccess[CS_FILTER_GRAPH]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_graph (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS F32 dX_CUMUL
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'TO BE USED WITH WAVE FILES BIGGER THAN N MAX WHEN THEY ARE LOADED FROM LIBRARY
     IF (ACTION AND CA_SLIDE) THEN
          IF (SOUNDRA.FD.LI > -1) ANDALSO (WF_Filter[SOUNDRA.FD.LI]->CONTENT <> WFC_BLANK) THEN
               IF WF_Filter[SOUNDRA.FD.LI]->WAVEFILE.FACT > SOUNDRA.FD.N THEN
                    DIM AS F32 dX_RATE = SOUNDRA.FILTER_WIN_SIZE_MAX - SOUNDRA.FD.N + SLIDEL_RATE
                    dX_CUMUL += X/dX_RATE
                    SOUNDRA.FD.FILE_OFFSET -= INT(X)
                    dX_CUMUL -= INT(X)
                    FLOOR(SOUNDRA.FD.FILE_OFFSET,0)
                    CEIL(SOUNDRA.FD.FILE_OFFSET,WF_Filter[SOUNDRA.FD.LI]->WAVEFILE.FACT-SOUNDRA.FD.N)
                    SOUNDRA.FIR_GENERATE(TRUE)
                    Controls[ControlAccess[CS_FILTER_GRAPH]]->UPDATE_GTEXT(TRUE)
               END IF
          END IF 
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_win   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],SOUNDRA.NB_FILTER_WINDOWS,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 I
          DIM AS TXT TMPSTR
          I = Controls[CurrentID]->text.si
          TMPSTR = SOUNDRA.FILTER_WINDOW_NAME(I)
          Controls[CurrentID]->text.content = TMPSTR
          SOUNDRA.FD.WIN_ID = I
          SOUNDRA.FIR_GENERATE(FALSE)
          Controls[ControlAccess[CS_FILTER_GRAPH]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_size  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],SOUNDRA.FILTER_WIN_SIZE_MAX-SOUNDRA.FILTER_WIN_SIZE_MIN+1,X,_
                    Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 N,K
          DIM AS TXT TMPSTR
          N = Controls[CurrentID]->text.si + SOUNDRA.FILTER_WIN_SIZE_MIN
          TMPSTR = "N = " & STR(N)
          Controls[CurrentID]->text.content = TMPSTR
          SOUNDRA.FD.N = N
          'REFRESH K
          K = Controls[ControlAccess[CS_FILTER_K]]->text.si
          IF K > N THEN
               Controls[ControlAccess[CS_FILTER_K]]->text.si = N
               Controls[ControlAccess[CS_FILTER_K]]->text.content = "K = " & STR(N)
               Controls[ControlAccess[CS_FILTER_K]]->UPDATE_GTEXT()
               SOUNDRA.FD.K = N
          END IF
          SOUNDRA.FIR_GENERATE(TRUE)
          Controls[ControlAccess[CS_FILTER_GRAPH]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_slope (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS I32 K_MAX = Controls[ControlAccess[CS_FILTER_SIZE]]->text.si+SOUNDRA.FILTER_WIN_SIZE_MIN+1
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],K_MAX,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 K
          DIM AS TXT TMPSTR
          K = Controls[CurrentID]->text.si
          TMPSTR = "K = " & STR(K)
          Controls[CurrentID]->text.content = TMPSTR
          SOUNDRA.FD.K = K
          SOUNDRA.FIR_GENERATE(TRUE)
          Controls[ControlAccess[CS_FILTER_GRAPH]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_HI    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          NEGATE(SOUNDRA.FD.HI)
          Controls[CurrentID]->state.POWER = SOUNDRA.FD.HI
          Controls[CurrentID]->UPDATE_GSTATE()
          SOUNDRA.FIR_GENERATE(FALSE)
          Controls[ControlAccess[CS_FILTER_GRAPH]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_add   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 R,ID
          DIM AS TXT TMPSTR
          'CREATE A NEW WF ENTRY
          MT_LIST_DOUBLING(WF_Filter,LI_WF_Filter,_WF_CATALOG)
          IF R THEN RETURN R
          ID = LI_WF_Filter.LENGTH
          WF_Filter[ID]         = NEW _WF_CATALOG
          WF_Filter[ID]->STATUS = WFS_RESTRICTED
          'NAME
          IF SOUNDRA.FD.LI = -1 ORELSE WF_Filter[SOUNDRA.FD.LI]->CONTENT = WFC_BLANK THEN
               TMPSTR  = "FIR_" & STR(SOUNDRA.FD.K) & "-"
          ELSE
               TMPSTR  = WF_Filter[SOUNDRA.FD.LI]->FILENAME
               TMPSTR  = MID(TMPSTR,1,LEN(TMPSTR)-4)
               TMPSTR &= "_"
          END IF
          TMPSTR &= STR(SOUNDRA.FD.N)
          IF SOUNDRA.FD.HI         THEN TMPSTR &= "_HI"
          IF SOUNDRA.FD.WIN_ID > 0 THEN TMPSTR &= "_" & SOUNDRA.FILTER_WINDOW_NAME(SOUNDRA.FD.WIN_ID)
          TMPSTR &= ".wav"
          WF_Filter[ID]->FileName           = TMPSTR
          WF_Filter[ID]->Path               = HOMEPATH & "\FILTERS"
          WF_Filter[ID]->WAVEFILE.FACT      = SOUNDRA.FD.N
          WF_Filter[ID]->WAVEFILE.DATASIZE  = SOUNDRA.FD.N * 4
          WF_Filter[ID]->WAVEFILE.AUDIODATA = NEW F32[SOUNDRA.FD.N]
          FOR I AS I32 = 0 TO SOUNDRA.FD.N-1
               WF_Filter[ID]->WAVEFILE.AUDIODATA[I] = SOUNDRA.FD.COEFF[I]
          NEXT I
          WF_Filter[ID]->Duration = SOUNDRA.FD.N / WF_Filter[ID]->WaveFile.WaveFormat.SampleRate
          WF_Filter[ID]->CONTENT  = WFC_FILE
          'SAVE TO DISK
          SaveWF(WF_Filter[ID])
          'UPDATE LIST
          LI_WF_Filter.LENGTH    += 1
          WF_Filter[ID]->STATUS   = WFS_FREE
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_del   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 LI = Controls[ControlAccess[CS_FILTER_LIST]]->text.si
          CEIL(LI,LI_WF_Filter.LENGTH-1)
          IF LI > -1 THEN
               DIM AS I32 I,FCT_ID = SOUNDRA.MM_CONVOLUTION
               DIM AS TXT FULL_FILE_NAME
               'UNLOAD FROM ALL SAMPLE MODULES THAT USE IT
               FOR I = 0 TO SOUNDRA.LI_MODULES.LENGTH-1
                    IF SOUNDRA.MODULES[I]->FCT_ID = FCT_ID            AND _
                       SOUNDRA.MODULES[I]->WF_PTR = WF_Filter[LI]     THEN
                         IF SOUNDRA.MODULE_STATUS(SOUNDRA.MODULES[I]) = SOUNDRA.SYNTH_STATES.LIVE THEN
                              'MSG TO SYNTH
                              SOUNDRA.WAIT4SYNTHUPDATE()
                              SOUNDRA.SU_DATA.MSG           = SOUNDRA.SYNTH_UPDATE_TYPES.SAMPLE
                              SOUNDRA.SU_DATA.MOD_PTR_IN    = SOUNDRA.MODULES[I]
                              SOUNDRA.SU_DATA.WF_PTR        = 0
                              SOUNDRA.SU_DATA.DONE          = FALSE
                         ELSE
                              'UNPLUG
                              SOUNDRA.MODULES[I]->WF_PTR    = 0
                              SOUNDRA.MODULES[I]->AudioData = 0
                         END IF
                    END IF
               NEXT I
               
               'UNLOAD FROM REFERENCE SAMPLE TOO
               IF SOUNDRA.REFERENCE_MODULE(FCT_ID).WF_PTR = WF_Filter[LI] THEN
                    SOUNDRA.REFERENCE_MODULE(FCT_ID).WF_PTR    = 0
                    SOUNDRA.REFERENCE_MODULE(FCT_ID).AudioData = 0
               END IF
               
               'DELETE FROM SAMPLE LIST
               MUTEXLOCK   LI_WF_Filter.MUTEX
               IF LI_WF_Filter.LENGTH > 1 THEN
                    FOR I = LI TO LI_WF_Filter.LENGTH - 2
                         SWAP WF_Filter[I],WF_Filter[I+1]
                         WF_Filter[I]->ID = I
                    NEXT I
               END IF
               LI_WF_Filter.LENGTH -= 1
               DELETE WF_Filter[LI_WF_Filter.LENGTH]
               MUTEXUNLOCK LI_WF_Filter.MUTEX
               CEIL(LI,LI_WF_Filter.LENGTH-1)
               Controls[ControlAccess[CS_FILTER_LIST]]->text.si = LI
               Controls[ControlAccess[CS_FILTER_LIST]]->UPDATE_GTEXT(TRUE)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_filter_set   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          DIM AS I32 LI = Controls[ControlAccess[CS_FILTER_LIST]]->text.si
          IF (MOD_PTR > 0) THEN
               IF (LI = -1) THEN 'UNSELECT
                    SET_MOD_SAMPLE(MOD_PTR, 0)
               ELSEIF (LI > -1) AND (LI < LI_WF_Filter.LENGTH) THEN
                    IF WF_Filter[LI]->STATUS = WFS_RESTRICTED OR _
                       WF_Filter[LI]->WaveFile.Audiodata = 0 THEN RETURN 0
                    MOD_PTR->SET_VALUE(3,WF_Filter[LI]->F0(0)) 'ATTENUATION
                    SET_MOD_SAMPLE(MOD_PTR, WF_Filter[LI])
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_melody_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32 LS
     MUTEXLOCK   LI_WF_Melody.MUTEX
     LS = LI_WF_Melody.LENGTH 
     MUTEXUNLOCK LI_WF_Melody.MUTEX
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], LS)
     IF (ACTION AND CA_CLICK) THEN 
          DIM AS I32 SID = Controls[CurrentID]->TEXT.si
          IF SID > -1 THEN
               MUTEXLOCK   LI_WF_Melody.MUTEX
               IF (SID < LI_WF_Melody.LENGTH) ANDALSO (WF_Melody[SID] > 0) ANDALSO _
                  (WF_Melody[SID]->STATUS = WFS_FREE) THEN 
                   Controls[CurrentID]->TEXT.si = SID
               ELSE
                   Controls[CurrentID]->TEXT.si = -1
               END IF
               MUTEXUNLOCK LI_WF_Melody.MUTEX
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_melody_save  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 LI = Controls[ControlAccess[CS_MELODY_LIST]]->text.si
          DIM AS TXT TMPSTR
          IF LI > -1 THEN
               IF WF_MELODY[LI]->ON_DISK = FALSE THEN
                    DIM AS I32 R
                    'CHECK IF FILE IS RESTRICTED
                    IF WF_Melody[LI]->STATUS = WFS_RESTRICTED OR WF_Melody[LI]->WAVEFILE.AUDIODATA = 0 THEN _
                       RETURN 0
                    'SAVE TO DISK
                    R = SaveMelody(WF_MELODY[LI])
                    IF R < 0 THEN 
                         '¤¤¤#247: "Error saving file: "
                         TMPSTR = LNG_SET_EXT(247,"Error saving file: ")
                         TMPSTR &= ERR2TXT(R)
                         TERMINAL(TMPSTR,MSG_ERROR)
                    ELSE
                         '¤¤¤#248: "has been saved."
                         TMPSTR = LNG_SET_EXT(248,"has been saved.")
                         TMPSTR  = WF_MELODY[LI]->FILENAME & " " & TMPSTR
                         MessageBar(TMPSTR)
                         IF FindActiveList() = ControlAccess[CS_FILE_LIST] THEN
                              Browse(@FOLDER0,,,FALSE,FALSE)
                              SelectFile(WF_MELODY[LI]->FILENAME)
                         END IF
                    END IF
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_melody_del   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 LI = Controls[ControlAccess[CS_MELODY_LIST]]->text.si
          CEIL(LI,LI_WF_Melody.LENGTH-1)
          IF LI > -1 THEN
               DIM AS I32 FCT_ID = SOUNDRA.MM_SEQUENCER
               DIM AS TXT FULL_FILE_NAME
               'CHECK IF FILE IS RESTRICTED
               IF WF_Melody[LI]->STATUS = WFS_RESTRICTED THEN RETURN 0
               'UNLOAD FROM ALL MELODY MODULES THAT USE IT
               FOR I AS I32 = 0 TO SOUNDRA.LI_MODULES.LENGTH-1
                    IF SOUNDRA.MODULES[I]->FCT_ID = FCT_ID        AND _
                       SOUNDRA.MODULES[I]->WF_PTR = WF_Melody[LI] THEN
                         IF SOUNDRA.MODULE_STATUS(SOUNDRA.MODULES[I]) = SOUNDRA.SYNTH_STATES.LIVE THEN
                              IF SOUNDRA.MODULES[I]->FLAGS(SOUNDRA.MF_PLAY_POWER) THEN 
                                   SOUNDRA.MODULES[I]->FLAGS(SOUNDRA.MF_PLAY_POWER) = FALSE
                                   SOUNDRA.WAIT4SEQRESET(SOUNDRA.MODULES[I])
                              END IF
                              'MSG TO SYNTH
                              SOUNDRA.WAIT4SYNTHUPDATE()
                              SOUNDRA.SU_DATA.MSG           = SOUNDRA.SYNTH_UPDATE_TYPES.SAMPLE
                              SOUNDRA.SU_DATA.MOD_PTR_IN    = SOUNDRA.MODULES[I]
                              SOUNDRA.SU_DATA.WF_PTR        = 0
                              SOUNDRA.SU_DATA.DONE          = FALSE
                         ELSE
                              'UNPLUG
                              SOUNDRA.MODULES[I]->WF_PTR    = 0
                              SOUNDRA.MODULES[I]->AudioData = 0
                         END IF
                    END IF
               NEXT I
               
               'UNLOAD FROM REFERENCE MELODY TOO
               IF SOUNDRA.REFERENCE_MODULE(FCT_ID).WF_PTR = WF_Melody[LI] THEN
                    SOUNDRA.REFERENCE_MODULE(FCT_ID).WF_PTR     = 0
                    SOUNDRA.REFERENCE_MODULE(FCT_ID).AudioData  = 0
               END IF
               Controls[ControlAccess[CS_MELODY_LIST]]->text.si = -1
               Controls[ControlAccess[CS_MELODY_LIST]]->UPDATE_GTEXT()
               SOUNDRA.DELETE_MELODY(LI)
               CEIL(LI,LI_WF_Melody.LENGTH-1)
               Controls[ControlAccess[CS_MELODY_LIST]]->text.si = LI
               Controls[ControlAccess[CS_MELODY_LIST]]->UPDATE_GTEXT(TRUE)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_melody_set   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          DIM AS I32 LI = Controls[ControlAccess[CS_MELODY_LIST]]->text.si
          IF (MOD_PTR > 0)THEN
               IF MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) = FALSE THEN
                    DIM AS IOS WAS_PLAYING = MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER)
                    DIM AS IOS WAS_AUTORW  = MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND)
                    DIM AS F32 TEMPO,OFFSET,DURATION,FREQ
                    DIM AS _WF_CATALOG PTR WF_PTR
                    'TURN OFF PLAYBACK
                    IF WAS_PLAYING THEN
                         MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND) = FALSE
                         MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER) = FALSE
                         IF SOUNDRA.MODULE_STATUS(MOD_PTR) = SOUNDRA.SYNTH_STATES.LIVE THEN _
                            SOUNDRA.WAIT4SEQRESET(MOD_PTR)
                    END IF
                    'PROCESS SELECTION, IF ANY
                    IF (LI > -1) AND (LI < LI_WF_Melody.LENGTH) THEN
                         IF WF_Melody[LI]->STATUS = WFS_RESTRICTED OR _
                            WF_Melody[LI]->WaveFile.Audiodata = 0  THEN RETURN 0
                         WF_PTR   = WF_Melody[LI]
                         TEMPO    = WF_PTR->F0(0)
                         OFFSET   = WF_PTR->WaveFile.Audiodata[SOUNDRA.SEQ.OFFSET]
                         DURATION = WF_PTR->Duration
                         FREQ     = WF_PTR->WaveFile.Audiodata[SOUNDRA.SEQ.META_LENGTH+SOUNDRA.SEQ.FREQ]
                    END IF
                    'SETUP MODULE
                    MOD_PTR->SET_VALUE(0,FREQ)
                    MOD_PTR->SET_VALUE(2,TEMPO)
                    MOD_PTR->SET_VALUE(3,OFFSET)
                    MOD_PTR->SET_VALUE(4,DURATION)
                    SET_MOD_SAMPLE(MOD_PTR, WF_PTR)
                    MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND) = WAS_AUTORW
                    MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER) = WAS_PLAYING
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_melody_record(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               IF MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP) = FALSE THEN 
                    IF MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) = TRUE THEN
                         MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP) = TRUE
                    ELSE
                         DIM AS TXT TMPSTR
                         DIM AS I32 R,ID,STATUS    = SOUNDRA.MODULE_STATUS(MOD_PTR)
                         DIM AS IOS WAS_PLAYING    = MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER)
                         DIM AS IOS WAS_AUTOREWIND = MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND)
                         'TURN OFF PLAYBACK
                         IF WAS_PLAYING = TRUE THEN
                              MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND) = FALSE
                              MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER) = FALSE
                              IF STATUS = SOUNDRA.SYNTH_STATES.LIVE THEN 
                                   '¤¤¤#249: "Wait until the record button turns on."
                                   TMPSTR = LNG_SET_EXT(249,"Wait until the record button turns on.")
                                   MessageBar(TMPSTR)
                                   SOUNDRA.WAIT4SEQRESET(MOD_PTR)
                              END IF
                         END IF
                         MOD_PTR->NBVC = 0
                         MOD_PTR->SAVED_NBVC = 0
                         'CREATE AND CONFIGURE CONTAINER (WAVE FILE CATALOG ENTRY)
                         MT_LIST_DOUBLING(WF_Melody,LI_WF_Melody,_WF_CATALOG)
                         IF R THEN RETURN R
                         ID = LI_WF_Melody.LENGTH
                         WF_Melody[ID]           = NEW _WF_CATALOG
                         WF_Melody[ID]->ID       = ID
                         WF_Melody[ID]->CONTENT  = WFC_HEADER
                         WF_Melody[ID]->STATUS   = WFS_RESTRICTED
                         WF_Melody[ID]->PATH     = HOMEPATH & "\MELODIES"
                         WF_Melody[ID]->FILENAME = TemporalStamp() & ".mel"
                         WF_Melody[ID]->ON_DISK  = FALSE
                         'CREATE MELODY BUFFER
                         WF_Melody[ID]->WAVEFILE.AUDIODATA = NEW F32[SOUNDRA.SEQ.MAX_SIZE]
                         IF WF_Melody[ID]->WAVEFILE.AUDIODATA = 0 THEN
                              '¤¤¤#250: "Unable to create new melody file."
                              TMPSTR = LNG_SET_EXT(250,"Unable to create new melody file.")
                              TERMINAL(TMPSTR,MSG_ERROR)
                              DELETE WF_Melody[ID]
                              RETURN -_MAF
                         END IF
                         WF_Melody[ID]->WaveFile.DataSize = 4*SOUNDRA.SEQ.MAX_SIZE
                         WF_Melody[ID]->CONTENT  = WFC_FILE
                         LI_WF_Melody.LENGTH    += 1
                         SET_MOD_SAMPLE(MOD_PTR, WF_Melody[ID], TRUE)
                         MOD_PTR->FLAGS(SOUNDRA.MF_AUTOREWIND) = WAS_AUTOREWIND
                         MOD_PTR->FLAGS(SOUNDRA.MF_PLAY_POWER) = WAS_PLAYING
                    END IF
               END IF
               Controls[CurrentID]->state.POWER = MOD_PTR->FLAGS(SOUNDRA.MF_RECORD)
               Controls[CurrentID]->CLR = _
                  IIF(MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP)=TRUE,RGB(200,100,0),0)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_melgen_record(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS SOUNDRA.MODULE PTR MOD_PTR = SOUNDRA.SELECTED_MODULE
          IF MOD_PTR > 0 THEN
               IF MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP) = FALSE THEN 
                    IF MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) = TRUE THEN
                         MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP) = TRUE
                    ELSE
                         DIM AS I32 R,ID
                         DIM AS TXT TMPSTR
                         'CREATE AND CONFIGURE CONTAINER (WAVE FILE CATALOG ENTRY)
                         MT_LIST_DOUBLING(WF_Melody,LI_WF_Melody,_WF_CATALOG)
                         IF R THEN RETURN R
                         ID = LI_WF_Melody.LENGTH
                         WF_Melody[ID]           = NEW _WF_CATALOG
                         WF_Melody[ID]->ID       = ID
                         WF_Melody[ID]->CONTENT  = WFC_HEADER
                         WF_Melody[ID]->STATUS   = WFS_RESTRICTED
                         WF_Melody[ID]->PATH     = HOMEPATH & "\MELODIES"
                         WF_Melody[ID]->FILENAME = TemporalStamp() & ".mel"
                         WF_Melody[ID]->ON_DISK  = FALSE
                         'CREATE MELODY BUFFER
                         WF_Melody[ID]->WAVEFILE.AUDIODATA = NEW F32[SOUNDRA.SEQ.MAX_SIZE]
                         IF WF_Melody[ID]->WAVEFILE.AUDIODATA = 0 THEN
                              '¤¤¤#251: "Unable to create new melody file."
                              TMPSTR = LNG_SET_EXT(251,"Unable to create new melody file.")
                              TERMINAL(TMPSTR,MSG_ERROR)
                              DELETE WF_Melody[ID]
                              RETURN -_MAF
                         END IF
                         WF_Melody[ID]->WaveFile.DataSize = 4*SOUNDRA.SEQ.MAX_SIZE
                         WF_Melody[ID]->CONTENT  = WFC_FILE
                         LI_WF_Melody.LENGTH    += 1
                         MOD_PTR->WF_PTR         = WF_Melody[ID]
                         MOD_PTR->Audiodata      = WF_Melody[ID]->WAVEFILE.AUDIODATA
                         MOD_PTR->REC_POS        = 0
                         MOD_PTR->WB_POS(0,3)    = 0
                         MOD_PTR->FLAGS(SOUNDRA.MF_RECORD) = TRUE
                         Controls[CurrentID]->CLR          = SWATCH0.BUTTON_BG_ON
                    END IF
                    Controls[CurrentID]->state.POWER = MOD_PTR->FLAGS(SOUNDRA.MF_RECORD)
                    Controls[CurrentID]->CLR = _
                       IIF(MOD_PTR->SYS_FLAGS(SOUNDRA.MSF_QREC_STOP)=TRUE,RGB(200,100,0),0)
               END IF
          END IF
     END IF
END FUNCTION
'------------------------------------------------------------------------------------------------------------
' BOARDS

FUNCTION ctrl_bb           (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SOUNDRA.VALUE_INPUT = (Controls[TFAccess[TF_Dialog]]->VISIBLE = TRUE) AND _
                           (Controls[ControlAccess[CS_PARAM_FA]+FAC_EDIT]->STATE.POWER = TRUE)
     IF (ACTION = CA_RESET) THEN 
          Controls[CurrentID]->text.hi = -1
     ELSE 'CALCULATE KEY POSITION
          DIM AS I32     I,K
          DIM AS TXT     TMPSTR
          I = INT(SOUNDRA.NB_KEYS_BB * ((MI.Y - Controls[CurrentID]->Y) / Controls[CurrentID]->H))
          FLOOR(I,-1)
          IF I >= SOUNDRA.NB_KEYS_BB THEN I = -1
          Controls[CurrentID]->text.hi = I
          IF (PLAYING = FALSE) AND (SOUNDRA.VALUE_INPUT = FALSE) THEN
               IF (ACTION = CA_MD) THEN MessageBar(KB_MSG)
          ELSEIF I > -1 THEN
               IF SOUNDRA.VALUE_INPUT THEN
                    IF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
                         K = I + SOUNDRA.BB[SOUNDRA.BB_NUM]->K_OFFSET.OCTAVE
                         SET_PARAM_FROM_KEY(K,TRUE)
                    END IF
               ELSE
                    'IF SOUNDRA.BB[SOUNDRA.BB_NUM]->KEY_MODE = SOUNDRA.K_CONTINUOUS THEN
                         'BB_CONTINUOUS(ACTION, Controls[CurrentID], I)
                    'ELSE
                         BB_KEYS(ACTION, Controls[CurrentID], I)
                    'END IF
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
SUB BB_KEYS(ACTION AS I32, CTRL AS _CTRL PTR, I AS I32)
     STATIC AS I32            LAST_K = -1,CURRENT_INDEX=-1
     DIM AS I32               K,K0,Q
     DIM AS SOUNDRA.BOARD     PTR BB = SOUNDRA.BB[SOUNDRA.BB_NUM]
     CTRL->text.si = -1
     IF (ACTION AND CA_MD) THEN
          K             = I + BB->K_OFFSET.OCTAVE
          LAST_K        = K
          CURRENT_INDEX = -1
          IF SOUNDRA.BB_RELEASE(-1,K) THEN
               IF BB->LOCK_MODE = FALSE THEN CURRENT_INDEX = SOUNDRA.BB_ADD(K)
          ELSE
               CURRENT_INDEX = SOUNDRA.BB_ADD(K)
          END IF
          IF CURRENT_INDEX > -1 THEN CTRL->text.si = I
     ELSEIF (ACTION AND CA_SLIDE) THEN
          K = I + BB->K_OFFSET.OCTAVE
          IF (LAST_K <> K) THEN
               IF BB->KEY_MODE = SOUNDRA.KEY_MODES.ISLAND THEN
                    IF LAST_K > -1 THEN SOUNDRA.BB_RELEASE(CURRENT_INDEX,LAST_K)
                    CURRENT_INDEX = SOUNDRA.BB_ADD(K)
               ELSE
                    SOUNDRA.BB_MODIFY(CURRENT_INDEX,K)
               END IF
               LAST_K = K
               CTRL->text.si = I
          END IF
     ELSEIF (ACTION AND CA_CLICK) THEN
          IF (BB->LOCK_MODE = FALSE) THEN
               K = I + BB->K_OFFSET.OCTAVE
               SOUNDRA.BB_RELEASE(CURRENT_INDEX,K)
          END IF
     ELSEIF (ACTION AND CA_MU) THEN
          K = I + BB->K_OFFSET.OCTAVE
          IF     BB->LOCK_MODE = FALSE THEN
               SOUNDRA.BB_RELEASE(CURRENT_INDEX,K)
               IF (K <> LAST_K) ANDALSO (LAST_K > -1) THEN SOUNDRA.BB_RELEASE(CURRENT_INDEX,LAST_K)
          ELSEIF BB->FIND_GCHANNEL(K,CURRENT_INDEX) > -1 THEN  'RELEASE KEY IF IT WAS ALREADY ACTIVE
               SOUNDRA.BB_RELEASE(CURRENT_INDEX,K)
          END IF
     END IF
END SUB

FUNCTION ctrl_kb_num       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],SOUNDRA.NB_KEYBOARDS_MAX,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED = TRUE THEN 
          IF SOUNDRA.KB_NUM <> Controls[CurrentID]->text.si THEN
               DIM AS I32 I,NEW_NUM = Controls[CurrentID]->text.si
               'FIND CORRESPONDING MODULE PTR
               SOUNDRA.KB_MOD_PTR = 0
               MUTEXLOCK   SOUNDRA.LI_MODULES.MUTEX
               FOR I = 0 TO SOUNDRA.LI_MODULES.LENGTH-1
                    IF (SOUNDRA.MODULES[I] > 0) ANDALSO _
                       (SOUNDRA.MODULES[I]->FCT_ID  = SOUNDRA.MM_KEYBOARD AND _
                        SOUNDRA.MODULES[I]->TYPE_ID = NEW_NUM+1) THEN
                         SOUNDRA.KB_MOD_PTR = SOUNDRA.MODULES[I]
                         EXIT FOR
                    END IF
               NEXT I
               MUTEXUNLOCK SOUNDRA.LI_MODULES.MUTEX
               SOUNDRA.KB_NUM = NEW_NUM
               'UPDATE KB Controls
               Controls[CurrentID]->text.content = "#" & STR(NEW_NUM+1)
               'MUTEXLOCK   SOUNDRA.MT_KB(NEW_NUM) '!!! PROBABLY USELESS HERE
               Controls[ControlAccess[CS_KB_LOCK]]->state.POWER = SOUNDRA.KB[NEW_NUM]->LOCK_MODE
               I = SOUNDRA.KB[NEW_NUM]->K_OFFSET.KEY-SOUNDRA.OCTAVE_MIN * TUNINGS(TUNING_CURRENT).NB_KEYS
               'MUTEXUNLOCK SOUNDRA.MT_KB(NEW_NUM)
               Controls[ControlAccess[CS_KB_LOCK]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_KB_POS]]->text.si = I
               Controls[ControlAccess[CS_KB_POS]]->UPDATE_GTEXT(TRUE)
               Controls[ControlAccess[CS_KB]]->UPDATE_GTEXT(TRUE)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_bb_num       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],SOUNDRA.NB_KEYBOARDS_MAX,Y,Controls[CurrentID]->H,TRUE)
     IF UPDATED THEN 
          IF SOUNDRA.BB_NUM <> Controls[CurrentID]->text.si THEN
               DIM AS I32 NEW_NUM = Controls[CurrentID]->text.si
               'FIND CORRESPONDING MODULE PTR
               SOUNDRA.BB_MOD_PTR = 0
               MUTEXLOCK   SOUNDRA.LI_MODULES.MUTEX
               FOR I AS I32 = 0 TO SOUNDRA.LI_MODULES.LENGTH-1
                    IF (SOUNDRA.MODULES[I] > 0) ANDALSO _
                       (SOUNDRA.MODULES[I]->FCT_ID  = SOUNDRA.MM_BEATBOARD AND _
                        SOUNDRA.MODULES[I]->TYPE_ID = NEW_NUM+1) THEN
                         SOUNDRA.BB_MOD_PTR = SOUNDRA.MODULES[I]
                         EXIT FOR
                    END IF
               NEXT I
               MUTEXUNLOCK SOUNDRA.LI_MODULES.MUTEX
               SOUNDRA.BB_NUM = NEW_NUM
               'UPDATE BB Controls
               Controls[ControlAccess[CS_BB_LOCK]]->state.POWER = SOUNDRA.BB[NEW_NUM]->LOCK_MODE
               Controls[ControlAccess[CS_BB_LOCK]]->UPDATE_GSTATE()
               Controls[CurrentID]->text.content = "#" & STR(NEW_NUM+1)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_kb_lock      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     FullPower_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
          DIM AS I32               I = SOUNDRA.KB_NUM
          DIM AS SOUNDRA.BOARD     PTR REF_BOARD
          REF_BOARD = SOUNDRA.KB[I]
          IF REF_BOARD > 0 THEN
               MUTEXLOCK   SOUNDRA.MT_KB(I)
               NEGATE(REF_BOARD->LOCK_MODE)
               Controls[CurrentID]->state.POWER = REF_BOARD->LOCK_MODE
               IF REF_BOARD->LOCK_MODE = FALSE THEN REF_BOARD->RELEASE_GCHANNELS(SOUNDRA.MT_KB_G(I))
               MUTEXUNLOCK SOUNDRA.MT_KB(I)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_bb_lock      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     FullPower_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
          DIM AS I32               I = SOUNDRA.BB_NUM
          DIM AS SOUNDRA.BOARD     PTR REF_BOARD
          REF_BOARD = SOUNDRA.BB[I]
          IF REF_BOARD > 0 THEN
               MUTEXLOCK   SOUNDRA.MT_BB(I)
               NEGATE(REF_BOARD->LOCK_MODE)
               Controls[CurrentID]->state.POWER = REF_BOARD->LOCK_MODE
               IF REF_BOARD->LOCK_MODE = FALSE THEN REF_BOARD->RELEASE_GCHANNELS(SOUNDRA.MT_BB_G(I))
               MUTEXUNLOCK SOUNDRA.MT_BB(I)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_kb_mode      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
          DIM AS I32 N = SOUNDRA.KB_NUM
          Controls[CurrentID]->text.si += 1
          IF Controls[CurrentID]->text.si > 1 THEN Controls[CurrentID]->text.si = 0
          SOUNDRA.KB[N]->KEY_MODE = Controls[CurrentID]->text.si
          Controls[CurrentID]->state.POWER  = (SOUNDRA.KB[N]->KEY_MODE=SOUNDRA.KEY_MODES.ISLAND)
          Controls[CurrentID]->text.UPDATED = TRUE
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_bb_mode      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
          DIM AS I32 N = SOUNDRA.BB_NUM
          Controls[CurrentID]->text.si += 1
          IF Controls[CurrentID]->text.si > 1 THEN Controls[CurrentID]->text.si = 0
          SOUNDRA.BB[N]->KEY_MODE = Controls[CurrentID]->text.si
          Controls[CurrentID]->state.POWER = (SOUNDRA.BB[N]->KEY_MODE=SOUNDRA.KEY_MODES.ISLAND)
          Controls[CurrentID]->text.UPDATED = TRUE
     END IF
     RETURN 0
END FUNCTION

SUB KB_CR_MANAGER()
     DIM AS I32 I = CR.SC - 2
     IF I > -1 AND I < 13 THEN
          IF (PLAYING = FALSE) AND (SOUNDRA.VALUE_INPUT = FALSE) THEN
               MessageBar(KB_MSG)
          ELSE
               DIM AS IOS               RELEASE,SHIFTUP
               DIM AS I32               K,NBK
               DIM AS SOUNDRA.BOARD     PTR KB_PTR
               RELEASE = CR.RELEASE
               SHIFTUP = CR.LSHIFT_ENABLED OR CR.RSHIFT_ENABLED
               KB_PTR  = SOUNDRA.KB[SOUNDRA.KB_NUM]
               NBK     = TUNINGS(TUNING_CURRENT).NB_KEYS
               IF SHIFTUP = TRUE THEN I += NBK
               
               K = SOUNDRA.KB_GET_K(I)
               IF RELEASE = TRUE THEN
                    IF KB_PTR->LOCK_MODE = FALSE THEN
                         IF SOUNDRA.KB_RELEASE(-1,K) = FALSE THEN
                              I += IIF(SHIFTUP,-NBK,NBK)
                              IF I > -1 THEN
                                   K = SOUNDRA.KB_GET_K(I)
                                   SOUNDRA.KB_RELEASE(-1,K)
                              END IF
                         END IF
                    END IF
               ELSEIF SOUNDRA.KB_RELEASE(-1,K) = TRUE THEN
                    IF KB_PTR->LOCK_MODE = FALSE THEN SOUNDRA.KB_ADD(K)
               ELSE
                    SOUNDRA.KB_ADD(K)
               END IF
          END IF
     END IF
END SUB

FUNCTION ctrl_kb           (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SOUNDRA.VALUE_INPUT = (Controls[TFAccess[TF_Dialog]]->VISIBLE = TRUE) AND _
                           (Controls[ControlAccess[CS_PARAM_FA]+FAC_EDIT]->STATE.POWER = TRUE)
     IF (ACTION AND CA_KB) THEN
          IF OriginID = 0 THEN KB_CR_MANAGER()
     ELSEIF (ACTION = CA_RESET) THEN 
          Controls[CurrentID]->text.hi = -1
     ELSE 'CALCULATE KEY POSITION
          DIM AS I32     I,K, MIDI_INDEX
          DIM AS TXT     TMPSTR
          I = INT(SOUNDRA.NB_KEYS_KB * ((MI.X - Controls[CurrentID]->X) / Controls[CurrentID]->W))
          FLOOR(I,-1)
          IF I >= SOUNDRA.NB_KEYS_KB THEN I = -1
          Controls[CurrentID]->text.hi = I
          IF (PLAYING = FALSE) AND (SOUNDRA.VALUE_INPUT = FALSE)   THEN
               IF (ACTION = CA_MD) THEN MessageBar(KB_MSG)
          ELSEIF I > -1 THEN
               IF SOUNDRA.VALUE_INPUT THEN
                    IF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
                         DIM AS I32 K
                         K = SOUNDRA.KB_GET_K(I) 'MT
                         SET_PARAM_FROM_KEY(K) 'MT
                    END IF
               ELSE
                    KB_KEYS(ACTION, Controls[CurrentID], I) 'MT
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

SUB KB_KEYS(ACTION AS I32, CTRL AS _CTRL PTR, I AS I32)
     STATIC AS I32            LAST_K = -1,CURRENT_INDEX=-1
     DIM AS I32               K,K0
     DIM AS SOUNDRA.BOARD     PTR KB = SOUNDRA.KB[SOUNDRA.KB_NUM]
     CTRL->text.si = -1
     IF (ACTION AND CA_MD) THEN
          K = SOUNDRA.KB_GET_K(I)
          LAST_K        = K
          CURRENT_INDEX = -1
          IF SOUNDRA.KB_RELEASE(-1,K) THEN
               IF KB->LOCK_MODE = FALSE THEN CURRENT_INDEX = SOUNDRA.KB_ADD(K)
          ELSE
               CURRENT_INDEX = SOUNDRA.KB_ADD(K)
          END IF
          IF CURRENT_INDEX > -1 THEN CTRL->text.si = I
     ELSEIF (ACTION AND CA_SLIDE) THEN
          K0 = LAST_K
          K  = SOUNDRA.KB_GET_K(I)
          IF (K0 <> K) THEN
               IF KB->KEY_MODE = SOUNDRA.KEY_MODES.ISLAND THEN
                    IF LAST_K > -1 THEN SOUNDRA.KB_RELEASE(CURRENT_INDEX,LAST_K)
                    CURRENT_INDEX = SOUNDRA.KB_ADD(K)
               ELSE
                    SOUNDRA.KB_MODIFY(CURRENT_INDEX,K)
               END IF
               LAST_K = K
               CTRL->text.si = I
          END IF
     ELSEIF (ACTION AND CA_CLICK) THEN
          IF (KB->LOCK_MODE = FALSE) THEN 
               K = SOUNDRA.KB_GET_K(I)
               SOUNDRA.KB_RELEASE(CURRENT_INDEX,K)
          END IF
     ELSEIF (ACTION AND CA_MU) THEN
          K = SOUNDRA.KB_GET_K(I)
          IF   KB->LOCK_MODE = FALSE THEN
               SOUNDRA.KB_RELEASE(CURRENT_INDEX,K)
               IF (K <> LAST_K) ANDALSO (LAST_K > -1) THEN SOUNDRA.KB_RELEASE(CURRENT_INDEX,LAST_K)
          ELSEIF KB->FIND_GCHANNEL(K,CURRENT_INDEX) > -1 THEN 
               SOUNDRA.KB_RELEASE(CURRENT_INDEX,K)
          END IF
     END IF
END SUB

FUNCTION ctrl_kb_slider    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS I32 NB_KEYS = TUNINGS(TUNING_CURRENT).NB_KEYS
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32 list_size = (SOUNDRA.OCTAVE_MAX-SOUNDRA.OCTAVE_MIN+1)*NB_KEYS
     SLIDEL_CONTROL(Controls[CurrentID],list_size,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 I = Controls[CurrentID]->text.si
          DIM AS SOUNDRA.BOARD PTR KB_PTR = SOUNDRA.KB[SOUNDRA.KB_NUM]
          'MT
          KB_PTR->K_OFFSET.KEY    = I + SOUNDRA.OCTAVE_MIN*NB_KEYS
          KB_PTR->K_OFFSET.OCTAVE = KB_PTR->K_OFFSET.KEY \ NB_KEYS
          Controls[ControlAccess[CS_KB]]->UPDATE_GTEXT(TRUE)
          Controls[CurrentID]->text.UPDATED = TRUE
     END IF
     RETURN 0
END FUNCTION

'------------------------------------------------------------------------------------------------------------
' SETTINGS :: A/V

FUNCTION ctrl_display_driver(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32 list_size = UBOUND(DISPLAY_DRIVER_NAME)
     SLIDEL_CONTROL(Controls[CurrentID],list_size,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 I = Controls[CurrentID]->text.si
          DIM AS TXT TMPSTR
          '¤¤¤#252: "Driver"
          TMPSTR = LNG_SET_EXT(252,"Driver")
          TMPSTR &= ": " & STR(I+1) & ") " & DISPLAY_DRIVER_NAME(I)
          Controls[CurrentID]->text.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_display_set   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I = Controls[CurrentID-1]->text.si
          IF NOT(I = DisplayDriverNum) THEN
               DIM AS TXT               TMPSTR
               DIM AS THREAD_MESSAGE    TMP_MSG
               DIM AS I32               ID = ControlAccess[CS_VINFOS]
               'SETTING
               TMPSTR = DISPLAY_DRIVER_NAME(I) & ", " & SCR_W & " x " & SCR_H & " @ 32-bit" 
               Controls[ID]->text.content = TMPSTR
               Controls[ID]->UPDATE_GTEXT()
               'MESSAGE
               TMP_MSG.CODE = _DRIVER_CHANGE
               TMP_MSG.TIME = TIMER
               TMP_MSG.X    = I
               SENDMTMSG(TMP_MSG)
          END IF
     END IF     
     RETURN 0
END FUNCTION
FUNCTION ctrl_display_cancel(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'RESET SELECTION TO CURRENT INDEX
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT TMPSTR
          Controls[CurrentID-2]->text.si = DisplayDriverNum
          '¤¤¤#253: "Driver"
          TMPSTR = LNG_SET_EXT(253,"Driver")
          TMPSTR &= ": " & STR(DisplayDriverNum+1) & ") " & DISPLAY_DRIVER_NAME(DisplayDriverNum)
          Controls[CurrentID-2]->text.content = TMPSTR
          Controls[CurrentID-2]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_resolution   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],NB_SCREENMODES,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32     I = Controls[CurrentID]->text.si
          DIM AS IOS     W,H
          DIM AS TXT     TMPSTR
          ScreenControl FB.GET_DESKTOP_SIZE,W,H
          'SKIP SIZES BIGGER THAN DESKTOP
          IF INCREMENT = 0 THEN INCREMENT = -1
          DO WHILE ((SCREENMODES(I).X > W) OR (SCREENMODES(I).Y > H))
               I += INCREMENT
               IF I >= NB_SCREENMODES THEN I = 0
               IF I < 0 THEN I = NB_SCREENMODES - 1
          LOOP
          'SET
          W = SCREENMODES(I).X
          H = SCREENMODES(I).Y
          '¤¤¤#254: "Resolution"
          TMPSTR = LNG_SET_EXT(254,"Resolution")
          TMPSTR &= ": " & STR(W) & " x " & STR(H)
          Controls[CurrentID]->text.si      = I
          Controls[CurrentID]->text.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_monitors   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)    AS I32
     DIM AS I32 N = NB_MONITORS
     CEIL(N,3)
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],N,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 I = Controls[CurrentID]->text.si
          NB_MONITORS = GetSystemMetrics(80)
          FLOOR(I,0)
          CEIL(I,NB_MONITORS-1)
          CEIL(I,2)
          Controls[CurrentID]->text.si      = I
          Controls[CurrentID]->text.content = "x" & STR(I+1)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_resolution_set(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I,N,ID = ControlAccess[CS_DISPLAY_RES]
          I = Controls[ID]->text.si
          N = Controls[ControlAccess[CS_NB_MONITORS]]->text.si
          'IF CHANGED
          IF (N <> SETTINGS.MONITORS-1) OR _
             (SETTINGS.SCREEN_WIDTH  <> SCREENMODES(I).X OR SETTINGS.SCREEN_HEIGHT <> SCREENMODES(I).Y) THEN
               'IF VALID
               ScreenControl FB.GET_DESKTOP_SIZE,DESK_W,DESK_H
               IF ((SCREENMODES(I).X > DESK_W) OR (SCREENMODES(I).Y > DESK_H)) THEN
                    DIM AS TXT TMPSTR
                    '¤¤¤#255: "Invalid window size."
                    TMPSTR = LNG_SET_EXT(255,"Invalid window size.")
                    TERMINAL(TMPSTR,MSG_WARNING)
               ELSE
                    DIM AS THREAD_MESSAGE TMP_MSG
                    DIM AS F32 M
                    'MESSAGE
                    TMP_MSG.CODE = _RES_CHANGE
                    TMP_MSG.TIME = TIMER
                    TMP_MSG.X    = I
                    TMP_MSG.Z    = N
                    SENDMTMSG(TMP_MSG)
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_resolution_cancel(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'RESET SELECTION TO CURRENT INDEX
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 W,H,ID
          DIM AS TXT TMPSTR
          W  = SCREENMODES(SCREENMODE_ID).X
          H  = SCREENMODES(SCREENMODE_ID).Y
          ID = ControlAccess[CS_NB_MONITORS]
          Controls[ID]->text.si      = SETTINGS.MONITORS-1
          Controls[ID]->text.content = "x" & STR(SETTINGS.MONITORS)
          Controls[ID]->UPDATE_GTEXT()
          ID = ControlAccess[CS_DISPLAY_RES]
          Controls[ID]->text.si      = SCREENMODE_ID
          '¤¤¤#256: "Resolution"
          TMPSTR = LNG_SET_EXT(256,"Resolution")
          TMPSTR &= ": " & STR(W) & " x " & STR(H)
          Controls[ID]->text.content = TMPSTR
          Controls[ID]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_GUI_zoom     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS F32 M = 5*(SETTINGS.ZMAX - SETTINGS.ZMIN) + 1
     SLIDEL_CONTROL(Controls[CurrentID],M,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS F32 Z = (Controls[CurrentID]->text.si / 5) + SETTINGS.ZMIN
          DIM AS TXT TMPSTR
          '¤¤¤#257: "Zoom"
          TMPSTR = LNG_SET_EXT(257,"Zoom")
          TMPSTR &= ": " & STR(Z) & "x"
          Controls[CurrentID]->text.content = TMPSTR
     END IF        
     RETURN 0
END FUNCTION
FUNCTION ctrl_GUI_zoom_set (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS F32 Z = (Controls[CurrentID-1]->text.si / 5) + SETTINGS.ZMIN
          Z = INT(100*Z) / 100
          IF SETTINGS.ZOOM_FACTOR <> Z THEN
               DIM AS THREAD_MESSAGE    TMP_MSG
               TMP_MSG.CODE = _ZOOM_CHANGE
               TMP_MSG.TIME = TIMER
               TMP_MSG.X    = Z
               SENDMTMSG(TMP_MSG)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_GUI_zoom_cancel(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'RESET SELECTION TO CURRENT ZOOM INDEX
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT TMPSTR
          Controls[CurrentID-2]->text.si = 5*(SETTINGS.ZOOM_FACTOR - SETTINGS.ZMIN)
          '¤¤¤#258: "Zoom"
          TMPSTR = LNG_SET_EXT(258,"Zoom")
          TMPSTR &= ": " & STR(SETTINGS.ZOOM_FACTOR) & "x"
          Controls[CurrentID-2]->text.content = TMPSTR
          Controls[CurrentID-2]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_output_driver(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32 list_size
     MUTEXLOCK   MT_Audio
     list_size = NB_OD
     MUTEXUNLOCK MT_Audio
     SLIDEL_CONTROL(Controls[CurrentID],list_size,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32  I = Controls[CurrentID]->text.si
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_Audio
          TMPSTR = OUTPUT_DEVICES(I)
          MUTEXUNLOCK MT_Audio
          Controls[CurrentID]->text.content = STR(I+1) & ") " & TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_output_set   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I = Controls[CurrentID-1]->text.si
          DIM AS IOS  ISNEW, REFRESH
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_Audio
          IF I < NB_OD THEN 
               ISNEW  = NOT(OUTPUT_DEVICES(I) = CAST(TXT,OUTPUT_DEVICE_NAME))
               TMPSTR = OUTPUT_DEVICES(I)
          ELSE
               REFRESH = TRUE
          END IF
          MUTEXUNLOCK MT_Audio
          IF ISNEW = TRUE THEN
               DIM AS THREAD_MESSAGE TMP_MSG
               SETTINGS.OUTPUT_DEVICE_NAME = TMPSTR
               SaveSettings()
               TMP_MSG.TIME = TIMER
               TMP_MSG.code = _PB_DEVICE
               TMP_MSG.x    = I
               SENDMTMSG(TMP_MSG, JIGSAW_AUDIO.THREAD_ID)
          ELSEIF REFRESH = TRUE THEN
               MUTEXLOCK   MT_Audio
               I = OUTPUT_DEVICE_INDEX
               TMPSTR = OUTPUT_DEVICES(I)
               MUTEXUNLOCK MT_Audio
               Controls[CurrentID-1]->text.si      = I
               Controls[CurrentID-1]->text.content = TMPSTR
               Controls[CurrentID-1]->UPDATE_GTEXT()
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_output_cancel (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'RESET SELECTION OT CURRENT DEVICE INDEX
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I,I0
          DIM AS TXT TMPSTR
          MUTEXLOCK   MT_Audio
          I = OUTPUT_DEVICE_INDEX
          TMPSTR = OUTPUT_DEVICES(OUTPUT_DEVICE_INDEX)
          MUTEXUNLOCK MT_Audio
          Controls[CurrentID-2]->text.si      = I
          Controls[CurrentID-2]->text.content = STR(I+1) & ") " & TMPSTR
          Controls[CurrentID-2]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_outputbs     (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],32,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32 I = Controls[CurrentID]->text.si+1
          DIM AS TXT TMPSTR
          '¤¤¤#259: "Playback buffer: {$}ms"
          TMPSTR = LNG_SET_EXT(259,"Playback buffer: {$}ms")
          TMPSTR = TXTINSERT(TMPSTR,STR(I))
          Controls[CurrentID]->text.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_outputbs_set (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS THREAD_MESSAGE TMP_MSG
          DIM AS I32 ID = ControlAccess[CS_OUTBS]
          IF SETTINGS.OUTPUT_BUFFER_LENGTH <> Controls[ID]->text.si + 1 THEN
               SETTINGS.OUTPUT_BUFFER_LENGTH = Controls[ID]->text.si + 1
               SaveSettings()
               TMP_MSG.TIME = TIMER
               TMP_MSG.code = _BUFFER_RESIZE
               TMP_MSG.x    = SETTINGS.OUTPUT_BUFFER_LENGTH
               SENDMTMSG(TMP_MSG, JIGSAW_AUDIO.THREAD_ID)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_outputbs_cancel(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN 'RESET SELECTION TO CURRENT LENGTH
          DIM AS TXT TMPSTR
          Controls[CurrentID-2]->text.si = SETTINGS.OUTPUT_BUFFER_LENGTH-1
          '¤¤¤#260: "Playback buffer: {$}ms"
          TMPSTR = LNG_SET_EXT(260,"Playback buffer: {$}ms")
          TMPSTR = TXTINSERT(TMPSTR,STR(SETTINGS.OUTPUT_BUFFER_LENGTH))
          Controls[CurrentID-2]->text.content = TMPSTR
          Controls[CurrentID-2]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_input_driver (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32  list_size
     MUTEXLOCK   MT_Audio
     list_size = NB_ID
     MUTEXUNLOCK MT_Audio
     SLIDEL_CONTROL(Controls[CurrentID],list_size,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED THEN
          DIM AS I32  I = Controls[CurrentID]->text.si
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_Audio
          TMPSTR = INPUT_DEVICES(I)
          MUTEXUNLOCK MT_Audio
          Controls[CurrentID]->text.content = STR(I+1) & ") " & TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_input_set    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I = Controls[CurrentID-1]->text.si
          DIM AS IOS  ISNEW, REFRESH
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_Audio
          IF I < NB_ID THEN 
               ISNEW   = NOT(INPUT_DEVICES(I) = CAST(TXT,INPUT_DEVICE_NAME))
               TMPSTR  = INPUT_DEVICES(I)
          ELSE
               REFRESH = TRUE
          END IF
          MUTEXUNLOCK MT_Audio
          IF ISNEW = TRUE THEN
               DIM AS THREAD_MESSAGE TMP_MSG
               SETTINGS.INPUT_DEVICE_NAME = TMPSTR
               SaveSettings()
               TMP_MSG.TIME = TIMER
               TMP_MSG.code = _CP_DEVICE
               TMP_MSG.x    = I
               SENDMTMSG(TMP_MSG, JIGSAW_AUDIO.THREAD_ID)
          ELSEIF REFRESH = TRUE THEN
               MUTEXLOCK   MT_Audio
               I = INPUT_DEVICE_INDEX
               TMPSTR = INPUT_DEVICE_NAME
               MUTEXUNLOCK MT_Audio
               Controls[CurrentID-1]->text.si      = I
               Controls[CurrentID-1]->text.content = TMPSTR
               Controls[CurrentID-1]->UPDATE_GTEXT()
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_input_cancel  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'RESET SELECTION OT CURRENT DEVICE INDEX
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_Audio
          I = INPUT_DEVICE_INDEX
          TMPSTR = INPUT_DEVICES(INPUT_DEVICE_INDEX)
          MUTEXUNLOCK MT_Audio
          Controls[CurrentID-2]->text.si      = I
          Controls[CurrentID-2]->text.content = STR(I+1) & ") " & TMPSTR
          Controls[CurrentID-2]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION
'------------------------------------------------------------------------------------------------------------
' SETTINGS :: SYSTEM
FUNCTION ctrl_setting_toggle  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS IOS I = NOT(Controls[CurrentID]->text.si - 1)
          NEGATE(I)
          Controls[CurrentID]->text.si = ABS(I)
          SELECT CASE Controls[CurrentID]->INDEX
          CASE ST_FORCE_CTHROTTLE
               SETTINGS.FORCE_CTHROTTLE = I
          CASE ST_AUTO_CONNECT
               SETTINGS.AUTO_CONNECT    = I
               Controls[ControlAccess[CS_USE_DEFAULT]]->state.DISABLED   = NOT(I)
               Controls[ControlAccess[CS_USE_DEFAULT]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_USE_DEFAULT]+1]->state.DISABLED = NOT(I)
               Controls[ControlAccess[CS_USE_DEFAULT]+1]->UPDATE_GSTATE()
               AUTO_CONNECT             = I
          CASE ST_USE_DEFAULT
               SETTINGS.USE_DEFAULT     = I
               USE_DEFAULT_DEVICE       = I
          CASE ST_AUTO_SAVE
               SETTINGS.AUTOSAVE        = I
          CASE ST_DISPLAY_MIDIIN
               SETTINGS.DISPLAY_MIDIIN  = I
          CASE ST_SHADER
               SETTINGS.USE_SHADER      = I
          END SELECT
          SaveSettings()
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_language    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32     list_size = NB_LANGUAGES
     DIM AS TXT     TMPSTR
     SLIDEL_CONTROL(Controls[CurrentID],list_size,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED = TRUE THEN
          DIM AS I32 I  = Controls[CurrentID]->text.si
          DIM AS IOS LE = FALSE
          DIM AS TXT FILENAME
          'CHECK IF LANGUAGE IS AVAILABLE
          IF INCREMENT = 0 THEN INCREMENT = 1
          DO WHILE (LE = FALSE) AND (I <> 0)
               FILENAME = EXEPATH & "\" & LANG_ABRV(I) & "_LANGPACK.TXT"
               LE = FILEEXISTS(FILENAME)
               FILENAME = EXEPATH & "\" & LANG_ABRV(I) & "_LANGPACK_EXT.TXT"
               LE AND= FILEEXISTS(FILENAME)
               IF LE = FALSE THEN
                    I += INCREMENT
                    IF I = list_size THEN I = 0
                    IF I < 0 THEN I = list_size-1
               END IF
               Controls[CurrentID]->text.si      = I
          LOOP
          Controls[CurrentID]->text.content = LANG_NAME(I)
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_language_set(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I,R
          I = Controls[CurrentID-1]->text.si
          IF I <> SETTINGS.LANGUAGE THEN
               DIM AS THREAD_MESSAGE TMP_MSG
               SETTINGS.LANGUAGE = I
               'LANGUAGE          = I
               TMP_MSG.TIME      = TIMER
               TMP_MSG.code      = _LANGUAGE
               SENDMTMSG(TMP_MSG)
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_language_cancel(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          Controls[CurrentID-2]->text.si      = SETTINGS.LANGUAGE
          Controls[CurrentID-2]->text.content = LANG_NAME(SETTINGS.LANGUAGE)
          Controls[CurrentID-2]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION
'------------------------------------------------------------------------------------------------------------
' SETTINGS :: TUNING

FUNCTION ctrl_tuning_keys  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS I32          I,W
     DIM AS F32          W2
     DIM AS _TextLine    NEW_CTRL_INFO
     DIM AS TXT          TMPSTR
     Activation_BState(ACTION,Controls[CurrentID]->state)
     
     'Measurements
     X -= Controls[CurrentID]->X
     W  = Controls[CurrentID]->W
     W2 = (W - BORDER_SIZE*2) / TUNINGS(TUNING_COPY).NB_KEYS
     
     'Button Index
     IF ((ACTION AND CA_HOVER) OR (ACTION AND CA_MD)) AND (X > BORDER_SIZE AND X < W-1-BORDER_SIZE) THEN
          X -= BORDER_SIZE
          I  = int(X / W2)
          Controls[CurrentID]->text.hi = I
          '¤¤¤#261: "Key #{$}: {$} = {$}; Click to edit its value or set a reference key."
          TMPSTR = LNG_SET_EXT(261,"Key #{$}: {$} = {$}; Click to edit its value or set a reference key.")
          TMPSTR = TXTINSERT(TMPSTR,STR(I+1))
          TMPSTR = TXTINSERT(TMPSTR,TUNINGS(TUNING_COPY).KEY_NAME(I))
          TMPSTR = TXTINSERT(TMPSTR,STR(TUNINGS(TUNING_COPY).KEY_ABS(I)))
          NEW_CTRL_INFO  = TMPSTR
     ELSE
          Controls[CurrentID]->text.hi = -1
          '¤¤¤#262: "Keys ; Click to edit their values or set a reference key."
          TMPSTR = LNG_SET_EXT(262,"Keys ; Click to edit their values or set a reference key.")
          NEW_CTRL_INFO = TMPSTR
     END IF
     Controls[CurrentID]->CTRL_INFO = NEW_CTRL_INFO
     
     IF (ACTION AND CA_CLICK) AND (X > BORDER_SIZE AND X < W-1-BORDER_SIZE) THEN
          IF DB_DATA.CALLER_ID = ControlAccess[CS_TNG_ABS]-1 OR _
             DB_DATA.CALLER_ID = ControlAccess[CS_TNG_REL]-1 THEN
               Controls[DB_DATA.CALLER_ID]->state.POWER = FALSE
               Controls[DB_DATA.CALLER_ID]->UPDATE_GSTATE()
               DB_cancel()
          END IF
          I = int((X - BORDER_SIZE) / W2)
          FLOOR(I,0)
          IF I = Controls[CurrentID]->text.si THEN 
               Controls[CurrentID]->text.si = -1
          ELSE
               Controls[CurrentID]->text.si = I
          END IF
          IF Controls[CurrentID]->text.si = -1 THEN
               Controls[ControlAccess[CS_TNG_REL]]->STATE.DISABLED   = TRUE
               Controls[ControlAccess[CS_TNG_REL]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_REL]-1]->STATE.DISABLED = TRUE
               Controls[ControlAccess[CS_TNG_REL]-1]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_REL]-2]->STATE.DISABLED = TRUE
               Controls[ControlAccess[CS_TNG_REL]-2]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_ABS]]->STATE.DISABLED   = TRUE
               Controls[ControlAccess[CS_TNG_ABS]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_ABS]-1]->STATE.DISABLED = TRUE
               Controls[ControlAccess[CS_TNG_ABS]-1]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_ABS]-2]->STATE.DISABLED = TRUE
               Controls[ControlAccess[CS_TNG_ABS]-2]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_RFK]]->STATE.DISABLED   = TRUE
               Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_KR]]->STATE.DISABLED    = TRUE
               Controls[ControlAccess[CS_TNG_KR]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_KR]]->UPDATE_GSTATE()
          ELSE
               DIM AS IOS DIS = (Controls[CurrentID]->text.si = 0)
               Controls[ControlAccess[CS_TNG_REL]]->STATE.DISABLED   = DIS
               Controls[ControlAccess[CS_TNG_REL]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_REL]-1]->STATE.DISABLED = DIS
               Controls[ControlAccess[CS_TNG_REL]-1]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_REL]-2]->STATE.DISABLED = DIS
               Controls[ControlAccess[CS_TNG_REL]-2]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_ABS]]->STATE.DISABLED   = DIS
               Controls[ControlAccess[CS_TNG_ABS]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_ABS]-1]->STATE.DISABLED = DIS
               Controls[ControlAccess[CS_TNG_ABS]-1]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_ABS]-2]->STATE.DISABLED = DIS
               Controls[ControlAccess[CS_TNG_ABS]-2]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_RFK]]->STATE.DISABLED   = FALSE
               Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_KR]]->STATE.DISABLED    = FALSE
               Controls[ControlAccess[CS_TNG_KR]]->UPDATE_GSTATE()
               Controls[ControlAccess[CS_TNG_KR]]->UPDATE_GSTATE()
               'SET VALUES
               MUTEXLOCK   SOUNDRA.FA_REL_K.MUTEX
               SOUNDRA.FA_REL_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_REL(I))
               MUTEXUNLOCK SOUNDRA.FA_REL_K.MUTEX
               MUTEXLOCK   SOUNDRA.FA_ABS_K.MUTEX
               SOUNDRA.FA_ABS_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_ABS(I))
               MUTEXUNLOCK SOUNDRA.FA_ABS_K.MUTEX
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_sp  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS I32 I,W
     DIM AS F32 W2
     Activation_BState(ACTION,Controls[CurrentID]->state)
     'DIMENSIONS
     X -= Controls[CurrentID]->X
     W  = Controls[CurrentID]->W
     W2 = (W - BORDER_SIZE*2) / TUNINGS(TUNING_COPY).NB_KEYS
     'ZONE HIGHLIGHT
     IF (ACTION AND CA_HOVER) AND (X > BORDER_SIZE AND X < W-1-BORDER_SIZE) THEN
          X -= BORDER_SIZE
          I  = int(X / W2)
          Controls[CurrentID]->text.hi = I
     ELSE
          Controls[CurrentID]->text.hi = -1
     END IF
     'TOGGLE KEY STATE
     IF (ACTION AND CA_MD) AND (X > BORDER_SIZE AND X < W-1-BORDER_SIZE) THEN
          DIM AS I32 J = Controls[ControlAccess[CS_TNG_SP_LIST]]->text.si
          IF J > -1 THEN
               DIM AS UI64 P
               I = int((X - BORDER_SIZE) / W2)
               FLOOR(I,0)
               MUTEXLOCK MT_TUNINGS(TUNING_COPY)
               P = TUNINGS(TUNING_COPY).MODE(J)
               TUNINGS(TUNING_COPY).MODE(J)  = P XOR (1 SHL I)
               TUNINGS(TUNING_COPY).MODIFIED = TRUE
               MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
               Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_tuning_equal (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I = Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          TUNINGS(TUNING_COPY).SET_EQUAL()
          IF I > -1 THEN
               SOUNDRA.FA_ABS_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_ABS(I))
               SOUNDRA.FA_REL_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_REL(I))
          END IF
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_geo (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I = Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          TUNINGS(TUNING_COPY).SET_GEO()
          IF I > -1 THEN
               SOUNDRA.FA_ABS_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_ABS(I))
               SOUNDRA.FA_REL_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_REL(I))
          END IF
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_tuning_keym  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I = Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          TUNINGS(TUNING_COPY).NB_KEYS = TUNINGS(TUNING_COPY).NB_KEYS - 1
          '¤¤¤#263: "keys"
          TMPSTR = LNG_SET_EXT(263,"keys")
          TMPSTR = TUNINGS(TUNING_COPY).NB_KEYS & " " & TMPSTR
          Controls[ControlAccess[CS_TNG_RFK]]->TEXT.content     = TUNINGS(TUNING_COPY).KEY_NAME(0)
          IF I > -1 THEN
               SOUNDRA.FA_ABS_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_ABS(I))
               SOUNDRA.FA_REL_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_REL(I))
          END IF
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          Controls[ControlAccess[CS_TNG_NBK_LBL]]->TEXT.content = TMPSTR
          Controls[ControlAccess[CS_TNG_NBK_LBL]]->UPDATE_GTEXT()
          Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.FONT_SIZE  = DEF_FONT_SIZE + 3
          Controls[ControlAccess[CS_TNG_KEYS]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_keyp  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  I = Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          TUNINGS(TUNING_COPY).NB_KEYS = TUNINGS(TUNING_COPY).NB_KEYS + 1
          '¤¤¤#264: "keys"
          TMPSTR = LNG_SET_EXT(264,"keys")
          TMPSTR = TUNINGS(TUNING_COPY).NB_KEYS & " " & TMPSTR
          Controls[ControlAccess[CS_TNG_RFK]]->TEXT.content     = TUNINGS(TUNING_COPY).KEY_NAME(0)
          IF I > -1 THEN
               SOUNDRA.FA_ABS_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_ABS(I))
               SOUNDRA.FA_REL_K.IMPORT_VALUE(TUNINGS(TUNING_COPY).KEY_REL(I))
          END IF
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          Controls[ControlAccess[CS_TNG_NBK_LBL]]->TEXT.content = TMPSTR
          Controls[ControlAccess[CS_TNG_NBK_LBL]]->UPDATE_GTEXT()
          Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.FONT_SIZE  = DEF_FONT_SIZE + 3
          Controls[ControlAccess[CS_TNG_KEYS]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_RK    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  KEY_NUM = Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si
          FLOOR(KEY_NUM,0)
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)     
          Controls[CurrentID]->TEXT.content = TUNINGS(TUNING_COPY).KEY_NAME(KEY_NUM)
          TUNINGS(TUNING_COPY).RF_KEY = KEY_NUM
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si         = -1
          Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.FONT_SIZE  = DEF_FONT_SIZE + 3
          Controls[ControlAccess[CS_TNG_KEYS]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_REL]]->STATE.DISABLED   = TRUE
          Controls[ControlAccess[CS_TNG_REL]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_REL]-1]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_REL]-1]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_REL]-2]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_REL]-2]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_ABS]]->STATE.DISABLED   = TRUE
          Controls[ControlAccess[CS_TNG_ABS]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_ABS]-1]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_ABS]-1]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_ABS]-2]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_ABS]-2]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_RFK]]->STATE.DISABLED   = TRUE
          Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_KR]]->STATE.DISABLED    = TRUE
          Controls[ControlAccess[CS_TNG_KR]]->UPDATE_GSTATE()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_tuning_kren  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS IOS IS_ANSWERED = DB_DATA.IS_ANSWERED
          DIM AS TXT NEW_NAME
          DIM AS I32 KNUM = Controls[ControlAccess[CS_TNG_KEYS]]->text.si
          DB_DATA.is_answered = FALSE
          IF IS_ANSWERED = TRUE THEN
               IF DB_DATA.answer = TRUE THEN
                    NEW_NAME = TRIM(Controls[TFAccess[TF_Dialog]]->TEXT.content)
                    IF LEN(NEW_NAME) > 3 THEN NEW_NAME = MID(NEW_NAME,1,3)
                    IF KNUM > -1 THEN
                         MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
                         TUNINGS(TUNING_COPY).KEY_NAME(KNUM) = NEW_NAME
                         MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
                         Controls[ControlAccess[CS_TNG_KEYS]]->UPDATE_GTEXT(TRUE)
                         Controls[ControlAccess[CS_TNG_RFK]]->TEXT.content  =TUNINGS(TUNING_COPY).KEY_NAME(0)
                         Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GTEXT()
                    END IF
               END IF
               Controls[CurrentID]->state.POWER = FALSE
          ELSE
               IF DB_DATA.CALLER_ID = CurrentID THEN
                    DB_cancel()
                    Controls[CurrentID]->state.POWER = FALSE
               ELSE 'ASK NEW NAME
                    IF KNUM > -1 THEN
                         DIM AS TXT  TMPSTR
                         MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
                         NEW_NAME = TUNINGS(TUNING_COPY).KEY_NAME(KNUM)
                         MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
                         '¤¤¤#265: "Rename key #{$} (3 characters max.):"
                         TMPSTR = LNG_SET_EXT(265,"Rename key #{$} (3 characters max.):")
                         TMPSTR = TXTINSERT(TMPSTR,STR(KNUM))
                         DB_Ask(CurrentID,TMPSTR, TRUE,NEW_NAME,DB_INPUT,KNUM,-1)
                         Controls[CurrentID]->state.POWER = TRUE
                    END IF
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_tuning_sp_list  (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)   AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     DIM AS I32  list_size
     MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
     list_size = TUNINGS(TUNING_COPY).vNBSP
     MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
     ListControl(Controls[CurrentID], list_size)
     IF (ACTION AND CA_CLICK) THEN
          IF Controls[CurrentID]->TEXT.si = -1 THEN Controls[CurrentID]->TEXT.si = 0
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_sp_list          (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     ListControl(Controls[CurrentID], TUNINGS(TUNING_CURRENT).vNBSP)
     IF (ACTION AND CA_CLICK) THEN
          IF Controls[CurrentID]->TEXT.si = -1 THEN Controls[CurrentID]->TEXT.si = 0
          IF SOUNDRA.SELECTED_MODULE > 0 THEN
               IF SOUNDRA.SELECTED_MODULE->FCT_ID = SOUNDRA.MM_MELGEN THEN
                  SOUNDRA.SELECTED_MODULE->GEN_ID = Controls[CurrentID]->TEXT.si
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_sp_del    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0) AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 N,I = Controls[ControlAccess[CS_TNG_SP_LIST]]->text.si
          DIM AS TXT TMPSTR
          '¤¤¤#266: "[New scale pattern]"
          TMPSTR = LNG_SET_EXT(266,"[New scale pattern]")
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          'MOVE ITEMS
          IF I = 0 THEN
               TUNINGS(TUNING_COPY).MODE_INIT(I)
          ELSE
               FOR J AS I32 = I+1 TO TUNINGS(TUNING_COPY).vNBSP-1
                    TUNINGS(TUNING_COPY).MODE(I)      = TUNINGS(TUNING_COPY).MODE(J)
                    TUNINGS(TUNING_COPY).MODE_NAME(I) = TUNINGS(TUNING_COPY).MODE_NAME(J)
               NEXT J
               TUNINGS(TUNING_COPY).vNBSP -= 1
          END IF
          N = TUNINGS(TUNING_COPY).vNBSP
          TUNINGS(TUNING_COPY).MODIFIED = TRUE
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          CEIL(I,N-1)
          Controls[ControlAccess[CS_TNG_SP_LIST]]->text.si = I
          CEIL(I,N-Controls[ControlAccess[CS_TNG_SP_LIST]]->text.nb_lines)
          Controls[ControlAccess[CS_TNG_SP_LIST]]->text.vi = I
          Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_sp_add    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32  N,I
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          I = TUNINGS(TUNING_COPY).vNBSP
          IF I < TNG_MAXSP THEN
               TUNINGS(TUNING_COPY).MODE_INIT(I)
               TUNINGS(TUNING_COPY).vNBSP   += 1
               TUNINGS(TUNING_COPY).MODIFIED = TRUE
          END IF
          N = TUNINGS(TUNING_COPY).vNBSP
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          Controls[ControlAccess[CS_TNG_SP_LIST]]->text.si = I
          CEIL(I,N-Controls[ControlAccess[CS_TNG_SP_LIST]]->text.nb_lines)
          Controls[ControlAccess[CS_TNG_SP_LIST]]->text.vi = I
          Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_sp_ren (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS IOS IS_ANSWERED = DB_DATA.IS_ANSWERED
          DIM AS TXT NEW_NAME
          DIM AS I32 KNUM = Controls[ControlAccess[CS_TNG_SP_LIST]]->text.si
          DB_DATA.is_answered = FALSE
          IF IS_ANSWERED = TRUE THEN
               IF DB_DATA.answer = TRUE THEN
                    NEW_NAME = TRIM(Controls[TFAccess[TF_Dialog]]->TEXT.content)
                    IF KNUM > -1 THEN
                         MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
                         TUNINGS(TUNING_COPY).MODE_NAME(KNUM) = NEW_NAME
                         TUNINGS(TUNING_COPY).MODIFIED        = TRUE
                         MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
                         Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
                    END IF
               END IF
               Controls[CurrentID]->state.POWER = FALSE
          ELSE
               IF DB_DATA.CALLER_ID = CurrentID THEN
                    DB_cancel()
                    Controls[CurrentID]->state.POWER = FALSE
               ELSE 'ASK NEW NAME
                    IF KNUM > -1 THEN
                         DIM AS TXT  TMPSTR
                         MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
                         NEW_NAME = TUNINGS(TUNING_COPY).MODE_NAME(KNUM)
                         MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
                         '¤¤¤#267: "Rename pattern (32 characters max.) :"
                         TMPSTR = LNG_SET_EXT(267,"Rename pattern (32 characters max.) :")
                         DB_Ask(CurrentID,TMPSTR, TRUE,NEW_NAME,DB_INPUT,KNUM,-1)
                         Controls[CurrentID]->state.POWER = TRUE
                    END IF
               END IF
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_tuning_new   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS _Tuning NEW_TNG
          'SET & SAVE
          NEW_TNG.FILENAME = "Tuning " & TemporalStamp() & ".tng"
          NEW_TNG.FILEPATH = HOMEPATH & "\" & "TUNINGS"
          SaveTuning(@NEW_TNG)
          'SELECT IN BROWSER
          OpenBrowser(HOMEPATH & "\TUNINGS",NEW_TNG.FILENAME)
          SelectFile(NEW_TNG.FILENAME)
          'COPY
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          TUNINGS(TUNING_COPY)    = NEW_TNG
          TUNINGS(TUNING_COPY).MODIFIED = FALSE
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          MUTEXLOCK   MT_TUNINGS(TUNING_CURRENT)
          TUNINGS(TUNING_CURRENT) = NEW_TNG
          MUTEXUNLOCK MT_TUNINGS(TUNING_CURRENT)
          UpdateTuningControls()
          SOUNDRA.SETUP_MOD_TUNING()
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_tuning_set   (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 R,I,N,NB_KEYS
          DIM AS TXT FILENAME, TMPSTR
          MUTEXLOCK   MT_TUNINGS(TUNING_CURRENT)
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          'COPY VALUES & REFRESH
          TUNINGS(TUNING_CURRENT) = TUNINGS(TUNING_COPY)
          Controls[ControlAccess[CS_TNG_NAME]]->TEXT.content = TUNINGS(TUNING_COPY).FILENAME
          NB_KEYS  = TUNINGS(TUNING_CURRENT).NB_KEYS
          FILENAME = TUNINGS(TUNING_CURRENT).FILENAME
          TUNINGS(TUNING_COPY).MODIFIED = FALSE
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          MUTEXUNLOCK MT_TUNINGS(TUNING_CURRENT)
          Controls[ControlAccess[CS_TNG_KEYS]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_NAME]]->UPDATE_GTEXT()
          'UPDATE SLIDER & KB
          FOR N = 0 TO SOUNDRA.NB_KEYBOARDS_MAX-1
               SOUNDRA.KB[N]->K_OFFSET.KEY = SOUNDRA.KB[N]->K_OFFSET.OCTAVE * NB_KEYS
          NEXT N
          I = SOUNDRA.KB[SOUNDRA.KB_NUM]->K_OFFSET.KEY - SOUNDRA.OCTAVE_MIN*NB_KEYS
          Controls[ControlAccess[CS_KB_POS]]->text.si = I
          Controls[ControlAccess[CS_KB_POS]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_KB]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_SP_LIST]]->UPDATE_GTEXT(TRUE)
          R = SaveTuning(@TUNINGS(TUNING_CURRENT))
          IF R < 0 THEN 
               '¤¤¤#268: "Error saving {$}: "
               TMPSTR = LNG_SET_EXT(268,"Error saving {$}: ")
               TMPSTR  = TXTINSERT(TMPSTR,FILENAME)
               TMPSTR &= ERR2TXT(R)
               TERMINAL(TMPSTR,MSG_ERROR)
          ELSE
               Browse(@FOLDER0,,,FALSE,FALSE)
               Controls[ControlAccess[CS_FILE_LIST]]->UPDATE_GTEXT(TRUE)
               '¤¤¤#269: "has been saved."
               TMPSTR = LNG_SET_EXT(269,"has been saved.")
               TMPSTR  = FILENAME & " " & TMPSTR
               MessageBar(TMPSTR)
          END IF
          SOUNDRA.SETUP_MOD_TUNING()
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_tuning_cancel(ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS TXT  TMPSTR
          MUTEXLOCK   MT_TUNINGS(TUNING_COPY)
          MUTEXLOCK   MT_TUNINGS(TUNING_CURRENT)
          TUNINGS(TUNING_COPY) = TUNINGS(TUNING_CURRENT)
          MUTEXUNLOCK MT_TUNINGS(TUNING_CURRENT)
          Controls[ControlAccess[CS_TNG_NAME]]->TEXT.content = TUNINGS(TUNING_COPY).FILENAME
          '¤¤¤#270: "keys"
          TMPSTR = LNG_SET_EXT(270,"keys")
          TMPSTR = TUNINGS(TUNING_COPY).NB_KEYS & " " & TMPSTR
          Controls[ControlAccess[CS_TNG_RFK]]->TEXT.content     = TUNINGS(TUNING_COPY).KEY_NAME(0)
          TUNINGS(TUNING_COPY).MODIFIED = FALSE
          MUTEXUNLOCK MT_TUNINGS(TUNING_COPY)
          Controls[ControlAccess[CS_TNG_NAME]]->UPDATE_GTEXT()
          Controls[ControlAccess[CS_TNG_NBK_LBL]]->TEXT.content = TMPSTR
          Controls[ControlAccess[CS_TNG_NBK_LBL]]->UPDATE_GTEXT()
          Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GTEXT()
          Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.si         = -1
          Controls[ControlAccess[CS_TNG_KEYS]]->TEXT.FONT_SIZE  = DEF_FONT_SIZE + 3
          Controls[ControlAccess[CS_TNG_KEYS]]->UPDATE_GTEXT(TRUE)
          Controls[ControlAccess[CS_TNG_REL]]->STATE.DISABLED   = TRUE
          Controls[ControlAccess[CS_TNG_REL]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_REL]-1]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_REL]-1]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_REL]-2]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_REL]-2]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_ABS]]->STATE.DISABLED   = TRUE
          Controls[ControlAccess[CS_TNG_ABS]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_ABS]-1]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_ABS]-1]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_ABS]-2]->STATE.DISABLED = TRUE
          Controls[ControlAccess[CS_TNG_ABS]-2]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_RFK]]->STATE.DISABLED   = TRUE
          Controls[ControlAccess[CS_TNG_RFK]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_KR]]->STATE.DISABLED    = TRUE
          Controls[ControlAccess[CS_TNG_KR]]->UPDATE_GSTATE()
          Controls[ControlAccess[CS_TNG_SP_LIST]]->text.si      = -1
          Controls[ControlAccess[CS_TNG_SP_LIST]]->text.vi      = 0
          Controls[ControlAccess[CS_TNG_SP_LIST]]->UPDATE_GTEXT(TRUE)
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_measure      (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],32,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED = TRUE THEN
          DIM AS TXT TMPSTR
          '¤¤¤#271: "Measure length"
          TMPSTR = LNG_SET_EXT(271,"Measure length")
          TMPSTR &= ": " & STR(Controls[CurrentID]->text.si+1)
          Controls[CurrentID]->TEXT.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_time_Base    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     SLIDEL_CONTROL(Controls[CurrentID],3,X,Controls[CurrentID]->W,FALSE)
     IF UPDATED = TRUE THEN
          DIM AS TXT TMPSTR
          '¤¤¤#272: "Base"
          TMPSTR = LNG_SET_EXT(272,"Base")
          TMPSTR &= ": " & STR(PRIME(Controls[CurrentID]->text.si))
          Controls[CurrentID]->TEXT.content = TMPSTR
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_ts_set       (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS IOS UPDATE_GRIDS
          'MEASURE LENGTH
          'IF SOUNDRA.MEASURE_LENGTH <> Controls[ControlAccess[CS_TS_LENGTH]]->TEXT.si THEN
          '     SOUNDRA.MEASURE_LENGTH = Controls[ControlAccess[CS_TS_LENGTH]]->TEXT.si
          '     UPDATE_GRIDS = TRUE
          'END IF
          'TIME BASE
          IF SOUNDRA.TIME_BASE <> Controls[ControlAccess[CS_TS_BASE]]->TEXT.si THEN
               DIM AS I32 I
               SOUNDRA.TIME_BASE = Controls[ControlAccess[CS_TS_BASE]]->TEXT.si
               Controls[ControlAccess[CS_BB]]->UPDATE_GTEXT(TRUE)
               UPDATE_GRIDS = TRUE
               'RELEASE ALL BB KEYS
               FOR I = 0 TO SOUNDRA.NB_KEYBOARDS_MAX-1
                    SOUNDRA.BB[I]->RELEASE_GCHANNELS(SOUNDRA.MT_BB_G(I))
               NEXT I
               SaveSettings()
          END IF
     END IF
     RETURN 0
END FUNCTION
FUNCTION ctrl_ts_cancel    (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I
          DIM AS TXT TMPSTR
          'RESTORE VALUES
          'I = SOUNDRA.MEASURE_LENGTH
          'Controls[ControlAccess[CS_TS_LENGTH]]->TEXT.si      = I
          'Controls[ControlAccess[CS_TS_LENGTH]]->TEXT.content = "Measure length: " & STR(I)
          'Controls[ControlAccess[CS_TS_LENGTH]]->UPDATE_GTEXT()
          I = SOUNDRA.TIME_BASE
          Controls[ControlAccess[CS_TS_BASE]]->TEXT.si        = I
          '¤¤¤#273: "Base"
          TMPSTR = LNG_SET_EXT(273,"Base")
          TMPSTR &= ": " & STR(PRIME(I))
          Controls[ControlAccess[CS_TS_BASE]]->TEXT.content   = TMPSTR
          Controls[ControlAccess[CS_TS_BASE]]->UPDATE_GTEXT()
     END IF
     RETURN 0
END FUNCTION

'SYNTH

FUNCTION ctrl_module_group (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     STATIC AS I32 DX,DY
     Activation_BState(ACTION,Controls[CurrentID]->state)
     IF (ACTION AND CA_MD) THEN
          DIM AS SOUNDRA.MODULE    PTR MOD_PTR
          DIM AS I32               I,NBO
          DX = 0
          DY = 0
          BringFront(CurrentID)
          IF Controls[CurrentID]->DATA > 0 THEN
               MOD_PTR = CAST(SOUNDRA.MODULE PTR,Controls[CurrentID]->DATA)
               IF MOD_PTR > 0 THEN
                    NBO = MOD_PTR->NB_OUTPUTS
                    FOR I = 0 TO MOD_PTR->NB_PORTS-1
                         IF     MOD_PTR->PARAM(I).INPUT_TYPE = SOUNDRA.INPUT_TYPES.EXTERNAL THEN
                              Controls[CurrentID]->CUSTOM_FLAG = TRUE
                         ELSEIF MOD_PTR->PARAM(I).INPUT_TYPE = SOUNDRA.INPUT_TYPES.FEEDBACK THEN
                              NBO -= 1
                         END IF
                    NEXT I
                    Controls[CurrentID]->CUSTOM_FLAG OR= (NBO > 0) OR (MOD_PTR = SOUNDRA.ROOT_MODULE)
                    'DESELECT PORT
                    IF (SOUNDRA.LINK_IN_CID > 0) THEN
                         Controls[SOUNDRA.LINK_IN_CID]->state.POWER = FALSE
                         Controls[SOUNDRA.LINK_IN_CID]->UPDATE_GSTATE()
                         SOUNDRA.LINK_IN      = 0
                         SOUNDRA.LINK_IN_PID  = -1
                         SOUNDRA.LINK_IN_CID  = 0
                    END IF
                    'DESELECT OUTPUT
                    IF (SOUNDRA.LINK_OUT_CID > 0) THEN
                         Controls[SOUNDRA.LINK_OUT_CID]->state.POWER = FALSE
                         Controls[SOUNDRA.LINK_OUT_CID]->UPDATE_GSTATE()
                         SOUNDRA.LINK_OUT     = 0
                         SOUNDRA.LINK_OUT_CID = 0
                    END IF
               END IF
          END IF
     ELSEIF    (ACTION AND CA_SLIDE) THEN
          DX += X
          DY += Y
          IF (ABS(DX) > 5) OR (ABS(DY) > 5) THEN
               Controls[CurrentID]->X = Controls[CurrentID]->X + X
               Controls[CurrentID]->Y = Controls[CurrentID]->Y + Y
               SOUNDRA.MOVING_CONNECTORS = Controls[CurrentID]->CUSTOM_FLAG
          END IF
     ELSEIF (ACTION AND CA_CLICK) OR (ACTION AND CA_MU) THEN
          DIM AS SOUNDRA.MODULE   PTR MOD_PTR
          IF (ABS(DX) < 6) AND (ABS(DY) < 6) THEN
               IF Controls[CurrentID]->DATA > 0 THEN
                    MOD_PTR = CAST(SOUNDRA.MODULE PTR,Controls[CurrentID]->DATA)
                    IF SOUNDRA.SELECTED_MODULE = MOD_PTR THEN
                         SelectModule(0)
                    ELSE
                         SelectModule(MOD_PTR)
                    END IF
               END IF
          END IF
          IF SOUNDRA.MOVING_CONNECTORS = TRUE THEN
               SOUNDRA.REDRAW_CONNECTORS = TRUE
               SOUNDRA.MOVING_CONNECTORS = FALSE
          END IF
          Controls[CurrentID]->CUSTOM_FLAG = FALSE
          DX = 0
          DY = 0
     END IF
     RETURN 0
END FUNCTION

FUNCTION SYNTH_OUT() AS I32
     DIM AS IOS DO_RESET, DISCONNECT
     IF SOUNDRA.LINK_OUT THEN
          'CONNECT?
          IF SOUNDRA.ROOT_MODULE <> SOUNDRA.LINK_OUT THEN
               SOUNDRA.DO_PRECOMPILE(FALSE,FALSE,SOUNDRA.LINK_OUT)
               LogAction(ACTION_SYNTH_CONNECT,SOUNDRA.ROOT_MODULE)
          END IF
     ELSE
          'DISCONNECT?
          IF SOUNDRA.ROOT_MODULE > 0 THEN
               SOUNDRA.DO_PRECOMPILE(TRUE,FALSE)
               LogAction(ACTION_SYNTH_DISCONNECT,SOUNDRA.ROOT_MODULE)
          END IF
     END IF
     SOUNDRA.REDRAW_CONNECTORS = TRUE
     RETURN (SOUNDRA.ROOT_MODULE > 0)
END FUNCTION

FUNCTION ctrl_module_patch (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS SOUNDRA.MODULE PTR MOD_PTR
     DIM AS I32            INDEX = -1
     Activation_BState(ACTION,Controls[CurrentID]->state)

     IF (ACTION > CA_HOVER) THEN
          IF Controls[CurrentID]->DATA > 0 THEN
               MOD_PTR = CAST(SOUNDRA.MODULE PTR,Controls[CurrentID]->DATA)
               INDEX   = Controls[CurrentID]->INDEX
          END IF
     END IF
     
     IF (ACTION AND CA_MD) THEN
          IF INDEX > -1 THEN
               IF SOUNDRA.SELECTED_MODULE <> MOD_PTR ORELSE INDEX <> MOD_PTR->SELECTED_PID THEN
                    IF SOUNDRA.LINK_IN_CID > 0 ANDALSO Controls[SOUNDRA.LINK_IN_CID] > 0 THEN
                         Controls[SOUNDRA.LINK_IN_CID]->state.POWER = FALSE
                         Controls[SOUNDRA.LINK_IN_CID]->UPDATE_GSTATE()
                    END IF
                    SelectModule(MOD_PTR,INDEX)
               END IF
          END IF
     ELSEIF (ACTION AND CA_SLIDE) THEN
          IF INDEX > -1 THEN SOUNDRA.PARAM_CONTROL(MOD_PTR, INDEX, -Y, TRUE) 
     ELSEIF (ACTION AND CA_CLICK) THEN
          DIM AS I32 I,J
          DIM AS TXT TMPSTR
          DIM AS IOS DO_RESET
          IF CurrentID = ControlAccess[CS_SYNTH_OUT] THEN
               Controls[CurrentID]->state.POWER = SYNTH_OUT()
               DO_RESET = TRUE
          ELSEIF MOD_PTR > 0 THEN
               IF INDEX > -1 THEN   'INPUT BUTTON
                    IF (SOUNDRA.LINK_IN = MOD_PTR) AND (SOUNDRA.LINK_IN_PID = INDEX) THEN
                         'DESELECT
                         SOUNDRA.LINK_IN     = 0
                         SOUNDRA.LINK_IN_PID = -1
                         SOUNDRA.LINK_IN_CID = 0
                         Controls[CurrentID]->state.POWER = FALSE
                    ELSE 'SELECT
                         SOUNDRA.LINK_IN     = MOD_PTR
                         SOUNDRA.LINK_IN_PID = INDEX
                         IF SOUNDRA.LINK_IN_CID > 0 ANDALSO Controls[SOUNDRA.LINK_IN_CID] > 0 THEN
                              Controls[SOUNDRA.LINK_IN_CID]->state.POWER = FALSE
                              Controls[SOUNDRA.LINK_IN_CID]->UPDATE_GSTATE()
                         END IF
                         SOUNDRA.LINK_IN_CID = CurrentID
                         Controls[CurrentID]->state.POWER = TRUE
                         SelectModule(MOD_PTR,INDEX)
                    END IF
               ELSE 'OUTPUT BUTTON
                    IF SOUNDRA.LINK_OUT = MOD_PTR THEN
                         'DESELECT
                         SOUNDRA.LINK_OUT     = 0
                         SOUNDRA.LINK_OUT_CID = 0
                         Controls[CurrentID]->state.POWER = FALSE
                    ELSE 'SELECT
                         SOUNDRA.LINK_OUT = MOD_PTR
                         IF SOUNDRA.LINK_OUT_CID > 0 ANDALSO Controls[SOUNDRA.LINK_OUT_CID] > 0 THEN
                              Controls[SOUNDRA.LINK_OUT_CID]->state.POWER = FALSE
                              Controls[SOUNDRA.LINK_OUT_CID]->UPDATE_GSTATE()
                         END IF
                         SOUNDRA.LINK_OUT_CID = CurrentID
                         Controls[CurrentID]->state.POWER = TRUE
                    END IF
               END IF

               'UN/LINK
               IF (SOUNDRA.LINK_IN > 0) AND (SOUNDRA.LINK_OUT > 0) AND (SOUNDRA.LINK_IN_PID > -1) THEN
                    'SET LINK
                    DIM AS I32 FID = SOUNDRA.LINK_IN->FCT_ID
                    IF FID > -1 AND FID < SOUNDRA.CATALOG_SIZE THEN
                         IF SOUNDRA.WAIT4SYNTHUPDATE() = SOUNDRA.SYNTH_STATES.LIVE THEN
                              SOUNDRA.SU_DATA.MSG         = SOUNDRA.SYNTH_UPDATE_TYPES.PLUG
                              SOUNDRA.SU_DATA.MOD_PTR_IN  = SOUNDRA.LINK_IN
                              SOUNDRA.SU_DATA.MOD_PTR_OUT = SOUNDRA.LINK_OUT
                              SOUNDRA.SU_DATA.PID         = SOUNDRA.LINK_IN_PID
                              SOUNDRA.SU_DATA.FID         = FID
                              SOUNDRA.SU_DATA.DONE        = FALSE
                              J = SOUNDRA.GET_PLUG_TYPE(SOUNDRA.LINK_IN,SOUNDRA.LINK_IN_PID, _
                                  SOUNDRA.LINK_OUT, @SOUNDRA.REFERENCE_MODULE(FID))
                         ELSE
                              J = SOUNDRA.LINK_IN->PLUG(SOUNDRA.LINK_IN_PID, SOUNDRA.LINK_OUT, _
                                 @SOUNDRA.REFERENCE_MODULE(FID))
                              SOUNDRA.PRECOMPILE(FALSE)
                         END IF
                         IF J = ACTION_MOD_DISCONNECT THEN
                              IF SOUNDRA.LINK_IN > 0 ANDALSO _
                                 SOUNDRA.LINK_IN_PID = SOUNDRA.LINK_IN->VHOST_PID THEN _
                                 SOUNDRA.LINK_IN->SET_VHOST_PID()
                         END IF
                         LogAction(J,SOUNDRA.LINK_IN,SOUNDRA.LINK_IN_PID,SOUNDRA.LINK_OUT)
                         PARAM_LIST_UPDATE(-1)
                    END IF
                    DO_RESET = TRUE
               END IF
          END IF

          IF DO_RESET THEN
               'LINK SELECTION
               IF SOUNDRA.LINK_IN_CID  > 0 ANDALSO Controls[SOUNDRA.LINK_IN_CID]  > 0 THEN
                    Controls[SOUNDRA.LINK_IN_CID]->state.POWER  = FALSE
                    Controls[SOUNDRA.LINK_IN_CID]->UPDATE_GSTATE()
               END IF
               IF SOUNDRA.LINK_OUT_CID > 0 ANDALSO Controls[SOUNDRA.LINK_OUT_CID] > 0 THEN
                    Controls[SOUNDRA.LINK_OUT_CID]->state.POWER = FALSE
                    Controls[SOUNDRA.LINK_OUT_CID]->UPDATE_GSTATE()
               END IF
               SOUNDRA.LINK_IN_CID   = 0
               SOUNDRA.LINK_OUT_CID  = 0
               SOUNDRA.LINK_OUT      = 0
               SOUNDRA.LINK_IN       = 0
               SOUNDRA.LINK_IN_PID   = -1
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_synth_plane (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     DIM AS TXT TMPSTR
     IF     (ACTION AND CA_HOVER) THEN
          X = X - SOUNDRA.PLANE_X
          Y = Y - SOUNDRA.PLANE_Y
          TMPSTR = "(" & STR(X) & "," & STR(Y) & ")"
          Controls[ControlAccess[CS_XYPOS]]->text.content = TMPSTR
          Controls[ControlAccess[CS_XYPOS]]->UPDATE_GTEXT()
     ELSEIF (ACTION AND CA_MU)    THEN
          SOUNDRA.REDRAW_CONNECTORS = TRUE
          SOUNDRA.MOVING_CONNECTORS = FALSE
     ELSEIF (ACTION AND CA_MD) THEN
          'DESELECT PORT
          IF (SOUNDRA.LINK_IN_CID > 0) ANDALSO (Controls[SOUNDRA.LINK_IN_CID] > 0) THEN
               Controls[SOUNDRA.LINK_IN_CID]->state.POWER = FALSE
               Controls[SOUNDRA.LINK_IN_CID]->UPDATE_GSTATE()
               SOUNDRA.LINK_IN      = 0
               SOUNDRA.LINK_IN_PID  = -1
               SOUNDRA.LINK_IN_CID  = 0
          END IF
          'DESELECT OUTPUT
          IF (SOUNDRA.LINK_OUT_CID > 0) ANDALSO (Controls[SOUNDRA.LINK_OUT_CID] > 0) THEN
               Controls[SOUNDRA.LINK_OUT_CID]->state.POWER = FALSE
               Controls[SOUNDRA.LINK_OUT_CID]->UPDATE_GSTATE()
               SOUNDRA.LINK_OUT     = 0
               SOUNDRA.LINK_OUT_CID = 0
          END IF
     ELSEIF (ACTION AND CA_CLICK) THEN
          SelectModule(0)
     ELSEIF (ACTION AND CA_SLIDE) THEN
          DIM AS I32 ID,N = Controls[CurrentID]->family.n
          'MOVE ALL MODULES
          SOUNDRA.PLANE_X += X
          SOUNDRA.PLANE_Y += Y
          FOR I AS I32 = 0 TO N - 1
               ID = Controls[CurrentID]->family.children[I]
               IF ID > 0 THEN
                    Controls[ID]->X = Controls[ID]->X + X
                    Controls[ID]->Y = Controls[ID]->Y + Y
                    ctrl_update(ID)
               END IF
          NEXT I
          SOUNDRA.MOVING_CONNECTORS = TRUE
     ELSEIF (ACTION AND CA_DRAG) THEN
          DIM AS I32 ID = -1
          DIM AS SOUNDRA.MODULE PTR NEW_DATA_PTR
          IF OriginID = ControlAccess[CS_MODULE_LIST]+MOD_REFERENCE OR _
             OriginID = ControlAccess[CS_MODULE_LIST]+MOD_LIBRARY   THEN
               ID = SOUNDRA.DRAG_MOD_ID
               IF ID > -1 THEN
                    IF     (OriginID = ControlAccess[CS_MODULE_LIST]+MOD_REFERENCE) THEN
                         IF (ID < SOUNDRA.CATALOG_SIZE) THEN NEW_DATA_PTR = @SOUNDRA.REFERENCE_MODULE(ID)
                    ELSEIF (ID < SOUNDRA.LI_MODULES.LENGTH) THEN
                         NEW_DATA_PTR = SOUNDRA.MODULES[ID]
                    END IF
               END IF
          END IF
          IF Controls[CurrentID]->DATA <> NEW_DATA_PTR THEN Controls[CurrentID]->DATA = NEW_DATA_PTR
          Controls[CurrentID]->CX = MI.X
          Controls[CurrentID]->CY = MI.Y
          X = MI.X - SOUNDRA.PLANE_X
          Y = MI.Y - SOUNDRA.PLANE_Y
          TMPSTR = STR(X) & "," & STR(Y)
          Controls[ControlAccess[CS_XYPOS]]->text.content = TMPSTR
          Controls[ControlAccess[CS_XYPOS]]->UPDATE_GTEXT()
     ELSEIF (ACTION AND CA_DROP) THEN 'CREATE A MODULE AT CURRENT POSITION
          IF (Controls[CurrentID]->DATA > 0) THEN
               DIM AS SOUNDRA.MODULE PTR MOD_PTR = CAST(SOUNDRA.MODULE PTR,Controls[CurrentID]->DATA)          
               Controls[CurrentID]->DATA = 0
               IF MOD_PTR > 0 THEN
                    IF MOD_PTR->PORT_TYPE > SOUNDRA.PORT_TYPES.DEFAULT THEN
                         CreateModule(MOD_PTR->FCT_ID,X,Y,TRUE)
                    ELSE
                         CreateModule(MOD_PTR->MOD_ID,X,Y,FALSE)
                    END IF
               END IF
               SOUNDRA.DRAG_MOD_ID = -1
          END IF
     END IF
     RETURN 0
END FUNCTION

FUNCTION ctrl_pp_display (ACTION AS I32,X AS I32,Y AS I32,CurrentID AS I32,OriginID AS I32=0)  AS I32
     RETURN 0
END FUNCTION
'------------------------------------------------------------------------------------------------------------