desc:Load/Playback MIDI (Alpha_03)

   
/* ========================================================
Release Notes:
  -- Alpha 01 - initial release
  -- Alpha 02 - loop recording and playback
  -- Alpha 03 - erroneous : at end of block section

===========================================================
*/

// =========================================================

slider1:1<0,6,1{Off,Record,Play back,Full - Stopped,-,-,Clear all}>-Mode
slider2:0<0,16,1{None,Session 01,Session 02,Session 03,Session 04,Session 05,Session 06,Session 07,Session 08,Session 09,Session 10,Session 11,Session 12,Session 13,Session 14,Session 15,Session 16,Session 17,Session 18,Session 19,Session 20}>-Recording
slider3:0<0,16,1{None,Session 01,Session 02,Session 03,Session 04,Session 05,Session 06,Session 07,Session 08,Session 09,Session 10,Session 11,Session 12,Session 13,Session 14,Session 15,Session 166,Session 17,Session 18,Session 19,Session 20}>-Playback
slider5:0<0,16384,1>-MIDI messages in session
slider6:0<0,16384,1>-MIDI messages in total 
slider7:0<0,256,1>-Display Width (beats)

// -------------------------------------------
in_pin:none
out_pin:none

filename:0,Big_ASCII_characters_03.png

import DS_library.jsfx-inc

// =========================================================
@serialize
    save_start = start_msg_buffer;
    save_len = (num_messages * num_msg_items) + (num_sessions * num_sess_items);
    stored00 = file_mem(0, save_start, save_len);
    stored01 = file_var(0, session_msgs); 
    stored02 = file_var(0, total_msgs); 
    stored03 = file_var(0, rec_session_idx);
    stored04 = file_var(0, play_session_idx);

// =========================================================
@init
    ext_noinit = 1;
    ext_midi_bus = 1;
    NOTE_ON = 9;
    NOTE_OFF = 8;
    CC_MSG = 11;
    PC_MSG = 12;
    CH_PRESS = 13;
    PITCH_WHEEL = 14;
    SYSEX = 15;

    NO = 0;
    YES = 1;
    EMPTY = -1;

    auto_mode = NO;
    auto_clear = NO;
    display_bars = 16;
    
// for the Mode slider
    OFF = 0;
    RECORD = 1;         
    PLAY = 2;
    FULL = 3;
    PLAYOVER = 4;

// other states
    CLEAR = 6;
    AUTOCLEAR = 7;

    DRUM = 20;
    
// transport state
    STOPPED = 0;
    PLAYING = 1;
    RECORDING = 5;

// trigger keys for sessions
    FIRST_TRIGGER =  36; // C1

// ------------------------------------------------------------------------
// message storage array
    num_messages = 16384; //8192 or 256 or 16384 
    num_msg_items = 5;
    start_msg_buffer = 1024;
        msg_type = start_msg_buffer;
        msg_chan = msg_type + num_messages;
        msg_num = msg_chan + num_messages;
        msg_val = msg_num + num_messages;
        msg_precise_beats = msg_val + num_messages;
    end_msg_buffer = start_msg_buffer + num_messages * num_msg_items;
        memset(start_msg_buffer, EMPTY, num_messages * num_msg_items); 

// pointers into message storage array
    num_sessions = 64; //16;
    num_sess_items = 4;
    start_sessions = end_msg_buffer;
        session_start = start_sessions;
        session_end = session_start + num_sessions;
        min_pitch = session_end + num_sessions;
        max_pitch = min_pitch + num_sessions;
    end_sessions = session_start + num_sessions * num_sess_items;  
        memset(start_sessions, EMPTY, num_sessions * num_sess_items); 
        memset(min_pitch,128,num_sessions); 
        
    widx = 0;
    ridx = 0;
    msg_count = 0;

// ------------------------------------------------------------------------
// storage for the currently active notes
    note_active = end_sessions;
    note_start = note_active;
    note_vel = note_start +128; 
    note_end = note_vel +128; 
    end_note_actives = note_end + 128 * 2;


// ------------------------------------------------------------------------
// .... the Mode states:
//      OFF = 0;    RECORD = 1;     PLAY = 2;    FULL = 3; PLAYOVER = 4;   CLEAR = 6;

    modename_len = 16;
    modenames = end_note_actives;
    end_modenames = modenames + 7 * modename_len;

        modenames[0]=$'O';  modenames[1]=$'f';  modenames[2]=$'f';  modenames[3]=$' ';
        modenames[4]=$' ';  modenames[5]=$' ';  modenames[6]=$' ';  modenames[7]=$' ';
        modenames[8]=$' ';  modenames[9]=$' ';  modenames[10]=$' ';  modenames[11]=$' ';
        modenames[12]=$' ';  modenames[13]=$' ';  modenames[14]=$' ';  modenames[15]=$' ';

        modenames[16]=$'R';  modenames[17]=$'e';  modenames[18]=$'c';  modenames[19]=$'o';
        modenames[20]=$'r';  modenames[21]=$'d';  modenames[22]=$'i';  modenames[23]=$'n';
        modenames[24]=$'g';  modenames[25]=$' ';  modenames[26]=$' ';  modenames[27]=$' ';
        modenames[28]=$' ';  modenames[29]=$' ';  modenames[30]=$' ';  modenames[31]=$' ';

        modenames[32]=$'P';  modenames[33]=$'l';  modenames[34]=$'a';  modenames[35]=$'y';
        modenames[36]=$'i';  modenames[37]=$'n';  modenames[38]=$'g';  modenames[39]=$' ';
        modenames[40]=$'B';  modenames[41]=$'a';  modenames[42]=$'c';  modenames[43]=$'k';
        modenames[44]=$' ';  modenames[45]=$' ';  modenames[46]=$' ';  modenames[47]=$' ';

        modenames[48]=$'F';  modenames[49]=$'u';  modenames[50]=$'l';  modenames[51]=$'l';
        modenames[52]=$' ';  modenames[53]=$'-';  modenames[54]=$' ';  modenames[55]=$'S';
        modenames[56]=$'t';  modenames[57]=$'o';  modenames[58]=$'p';  modenames[59]=$'p';
        modenames[60]=$'e';  modenames[61]=$'d';  modenames[62]=$' ';  modenames[63]=$' ';

        modenames[64]=$'P';  modenames[65]=$'l';  modenames[66]=$'a';  modenames[67]=$'y';
        modenames[68]=$'b';  modenames[69]=$'a';  modenames[70]=$'c';  modenames[71]=$'k';
        modenames[72]=$' ';  modenames[73]=$'E';  modenames[74]=$'n';  modenames[75]=$'d';
        modenames[76]=$'e';  modenames[77]=$'d';  modenames[78]=$' ';  modenames[79]=$' ';

        modenames[80]=$' ';  modenames[81]=$' ';  modenames[82]=$' ';  modenames[83]=$' ';
        modenames[84]=$' ';  modenames[85]=$' ';  modenames[86]=$' ';  modenames[87]=$' ';
        modenames[88]=$' ';  modenames[89]=$' ';  modenames[90]=$' ';  modenames[91]=$' ';
        modenames[92]=$' ';  modenames[93]=$' ';  modenames[94]=$' ';  modenames[95]=$' ';

        modenames[96]=$'C';  modenames[97]=$'l';  modenames[98]=$'e';  modenames[99]=$'a';
        modenames[100]=$'r';  modenames[101]=$'e';  modenames[102]=$'d';  modenames[103]=$' ';
        modenames[104]=$'A';  modenames[105]=$'l';  modenames[106]=$'l';  modenames[107]=$' ';
        modenames[108]=$' ';  modenames[109]=$' ';  modenames[110]=$' ';  modenames[111]=$' ';

// ------------------------------------------------------------------------
//-    coordinatelist = end_modenames;
//-    end_coords = coordinatelist + 10;


    end_main_storage = end_modenames;
    end_storage_1 = FUNC.add_lib_store(end_main_storage);
    

    first_rec = YES;
    first_play = NO;
    rec_session_idx = -1; // updated at start of each recording
    play_session_idx = -1;
    one_character = $' ';

// =========================================================
@slider
    mode = slider1;
    mode == RECORD ? first_rec = YES;   
    mode == PLAY   ? first_play = YES;
    mode == CLEAR ? (
        memset(start_msg_buffer, EMPTY, num_messages * num_msg_items); 
        memset(start_sessions, EMPTY, num_sessions * num_sess_items); 
        memset(min_pitch, 128, num_sessions); 
        widx = 0;
        ridx = 0;
        msg_count = 0;
        mode = CLEAR;    
        first_rec = YES;
        first_play = NO;
        slider2 = 0;
        slider3 = 0;
        slider5 = 0; session_msgs = 0;
        slider6 = 0; total_msgs = 0;
    );

    slider3 >= slider2 ? slider3 = slider2;
    slider3 != old_s3 ? (
        old_s3 = slider3;
        slider5 = 0; session_msgs = 0;
    );

    rec_session_idx = slider2 -1;
    play_session_idx = slider3 -1;

// =========================================================
@block

    transport_state = play_state;
    play_pos = play_position;                                   // as at the start of the block, in seconds
    beat_pos = beat_position;                                   // as at the start of the block, in beats
    samples_per_block = samplesblock;                           // e.g. 441 for 10ms buffer
    beats_per_block = (tempo / 60) * samplesblock / srate;      //e.g. 0.02 for 120 bpm
    block_no +=1;
    mpos =0;
    beats_per_bar = ts_num;

    transport_state == STOPPED ? ( first_rec = YES; first_play = YES; );

// =========================================================
@sample

// ... switches Mode automatically

    auto_mode == YES ? (
        transport_state == RECORDING && mode != OFF ? (
            mode = PLAY; slider1 = PLAY;
        );
        transport_state == PLAYING && mode != OFF && total_msgs < num_messages ? (
             mode = RECORD; slider1 = RECORD;
        );
    );


// -------------------------------------------
// RECORDING Mode
// -------------------------------------------

    mode == RECORD && transport_state == PLAYING && widx < num_messages ? (

// .... detect loop back to start, start a new recording
        play_pos < last_play_pos ? (
            last_play_pos = EMPTY;
            first_rec = YES; 
        );

        first_rec == YES ? ( 
            first_rec = NO;
            session_msgs = 0;
            rec_session_idx +=1; 
            rec_session_idx >= num_sessions ? (
                rec_session_idx -=1; 
                widx = 0;
                mode = FULL;
                slider1 = mode; 
                sliderchange(-1);  
            ):(  
                session_idx = rec_session_idx;
                play_session_idx = rec_session_idx;
                slider2 = rec_session_idx +1;
                slider3 = rec_session_idx +1;
                slider5 = session_msgs;
                sliderchange(-1);
                session_start[rec_session_idx] = widx;
            );
        );

        mode == RECORD && midirecv(mpos, msg1, msg23) ? (
            session_msgs +=1;
            total_msgs +=1;
            slider5 = session_msgs; 
            slider6 = total_msgs; 
            sliderchange(-1);
            msg = (msg1 / 16) | 0;
            midisend(mpos, msg1, msg23);
            record_event = (msg >= NOTE_OFF && msg < SYSEX); 
            record_event ? (
                channel = 1 + msg1 - (msg * 16);     
                msg_number = msg23 & 127;
                msg_value = (msg23 / 256) | 0;

                play_pos = play_position + ((mpos +1) / srate); // add on the offset (in seconds)
                precise_beats = beat_position + (mpos * (tempo / 60) / srate);

                last_play_pos = play_pos;
                msg_type[widx] = msg;
                msg_chan[widx] = channel;
                msg_num[widx] = msg_number;
                msg_val[widx] = msg_value;
                msg_precise_beats[widx] = precise_beats;

                msg == NOTE_ON && msg_number > max_pitch[rec_session_idx] ? 
                    max_pitch[rec_session_idx] = msg_number;
                msg == NOTE_ON && msg_number < min_pitch[rec_session_idx] ? 
                    min_pitch[rec_session_idx] = msg_number;

                session_end[rec_session_idx] = widx;
                widx +=1;
                widx >= num_messages ? (
                    widx = 0;
                    mode = FULL;
                    slider1 = mode; 
                    sliderchange(-1);  
// Autoclear
                    auto_clear == YES ? (
                        mode = CLEAR;    
                        memset(start_msg_buffer, EMPTY, num_messages * num_msg_items); 
                        memset(start_sessions, EMPTY, num_sessions * num_sess_items); 
                        memset(min_pitch, 128, num_sessions); 
                        widx = 0;
                        ridx = 0;
                        msg_count = 0;
                        first_rec = YES;
                        first_play = NO;
                        slider1 = CLEAR;
                        slider2 = 0;
                        slider3 = 0;
                        slider5 = 0; session_msgs = 0;
                        slider6 = 0; total_msgs = 0;
                        rec_session_idx = slider2 -1;
                        play_session_idx = slider3 -1;
                        display_start_beats = 0;
                        mode = RECORD;
                    );
// end of Autoclear

                );  
                msg_count +=1;
            );
        );
    );  // end of Recording mode


// -------------------------------------------
// PLAYBACK Mode
// -------------------------------------------

//    mode == PLAY && transport_state == RECORDING ? (
    mode == PLAY && (transport_state == RECORDING || transport_state == PLAYING) ? (

// .... detect loop back to start
        play_pos < last_play_pos ? (
            last_play_pos = EMPTY;
            first_play = YES; 
        );

        first_play == YES ? (
            first_play = NO;
            session_idx = play_session_idx;
            ridx = session_start[play_session_idx];
            disp_idx = session_start[play_session_idx];
            session_msgs = 0;
            slider5 = session_msgs;
            sliderchange(-1);
        );

        last_play_pos = play_pos;
        precise_beats = beat_position + (mpos * (tempo / 60) / srate);

        msg_precise_beats[ridx] > EMPTY && msg_precise_beats[ridx] <= precise_beats 
        && ridx <= session_end[play_session_idx] ? (
            msg1 = msg_type[ridx] * 16 + msg_chan[ridx] -1;
            msg23 = msg_val[ridx] * 256 + msg_num[ridx];
            midisend(mpos, msg1, msg23);
            session_msgs +=1;
            slider5 = session_msgs;
            sliderchange(-1);
            ridx +=1;
        );
        mpos +=1;
    );  // end of playback mode

// ==================================================================================
//           D R A W   T H E    G R A P H I C S   E L E M E N T S
// ==================================================================================

@gfx 720 410

    MIN_WIDTH = 600; // minimum GUI width (to dipslay all the buttons)

// --------------------------------------------------------
// FUNCTION print the input CC ; output channel / CC mapping for a control
// --------------------------------------------------------

function print_note_details (Apitch, Bval)
local(save_X1 save_Y1 disp_W note_ptr)
(
    save_X1 = gfx_x; save_Y1 = gfx_y;
    gfx_a = 0.75; 
    disp_W = 40;
    note_ptr = octave + Apitch * 4;    
    set_colour(MATT_RED);
    gfx_x -=4; gfx_y -=2;

    notes[note_ptr +2] != $' ' ? disp_W +=8;
    notes[note_ptr +3] != $' ' ? disp_W +=8;
    Bval > 10 ? disp_W +=8;
    Bval > 100 ? disp_W +=8;    
    gfx_rectto(gfx_x + disp_W, gfx_y + 12);
    gfx_x = save_X1; gfx_y = save_Y1;
    
    set_colour(HIGHLIGHTED);
    gfx_drawchar(notes[note_ptr +0]);
    gfx_drawchar(notes[note_ptr +1]);
    notes[note_ptr +2] != $' ' ? gfx_drawchar(notes[note_ptr +2]);
    notes[note_ptr +3] != $' ' ? gfx_drawchar(notes[note_ptr +3]);
    gfx_drawchar($' ' ); 
    gfx_drawnumber(Bval,0);
    gfx_a = 1.00;
YES;
); 



// --------------------------------------------------------
// display the current Mode in BIG letters
// --------------------------------------------------------

    std_colour = 5;
    mode_colour = 7;
    BIG_X = 20; BIG_Y = 20; BIG_H  = 40; BIG_W = 400; 
 
 
    mode == PLAY && session_msgs > 0 
                 && (transport_state == PLAYING || transport_state == RECORDING)
                 && session_msgs == (session_end[session_idx] - session_start[session_idx] +1) ? (
        mode = PLAYOVER;
    );
 
    gfx_x = BIG_X; gfx_y = BIG_Y;
    text_ptr = modenames + mode * modename_len;
    print_text_big (text_ptr, modename_len, mode_colour, 0, 1); // (text_pointer, text_length, colour, Bfont, zoom)
         
    mode == PLAYOVER ? mode = PLAY:

// --------------------------------------------------------
// display the Playback / Recording session ids in BIG characters
// --------------------------------------------------------

    counts_colour = 5;
    PBID_X = 340;
    gfx_x = PBID_X;  
 
    mode == PLAY ? (
        gfx_x = print_number_big (play_session_idx +1, 0, NO, counts_colour, 0); // (Bvalue, decimals, signed, colour, Bfont)
        one_character[0] = $'/';
        gfx_x = print_text_big (one_character, 1, counts_colour, 0, 1); // (text_pointer, text_length, colour, Bfont, zoom)
    );

    (mode == RECORD || mode == PLAY) && rec_session_idx > EMPTY ? (
        print_number_big (rec_session_idx +1, 0, NO, counts_colour, 0); // (Bvalue, decimals, signed, colour, Bfont)
    );

// ----------------------
// and the timeline position

    gfx_x = 520;
    precise_beats = beat_pos;
    PLAYPOS.print_timepos_big (beat_pos, std_colour, 0); // (beat_pos, colour, Bfont)

// --------------------------------------------------------
// display the message counts in small characters
// --------------------------------------------------------

    gfx_x = 20; gfx_y = 70;
    set_colour(PALE_GREY);
    gfx_drawchar($'S'); gfx_drawchar($'e'); gfx_drawchar($'s');
    gfx_drawchar($'s'); gfx_drawchar($'i'); gfx_drawchar($'o');
    gfx_drawchar($'n'); gfx_drawchar($' '); gfx_drawchar($'m');
    gfx_drawchar($'e'); gfx_drawchar($'s'); gfx_drawchar($'s');
    gfx_drawchar($'a'); gfx_drawchar($'g'); gfx_drawchar($'e');
    gfx_drawchar($'s'); gfx_drawchar($':'); gfx_drawchar($' ');

    session_msgs > 0 ? (
        gfx_drawnumber(session_msgs,0);
        gfx_drawchar($' '); gfx_drawchar($'/'); gfx_drawchar($' ');
        gfx_drawnumber(session_end[session_idx] - session_start[session_idx] +1,0);
    );

    gfx_x = 20; gfx_y = 82;
    set_colour(PALE_GREY);
    gfx_drawchar($'T'); gfx_drawchar($'o'); gfx_drawchar($'t');
    gfx_drawchar($'a'); gfx_drawchar($'l'); gfx_drawchar($' ');
    gfx_drawchar($' '); gfx_drawchar($' '); gfx_drawchar($'m');
    gfx_drawchar($'e'); gfx_drawchar($'s'); gfx_drawchar($'s');
    gfx_drawchar($'a'); gfx_drawchar($'g'); gfx_drawchar($'e');
    gfx_drawchar($'s'); gfx_drawchar($':'); gfx_drawchar($' ');

    total_msgs > 0 ? ( 
       gfx_drawnumber(total_msgs,0);
        gfx_drawchar($' '); gfx_drawchar($'/'); gfx_drawchar($' ');
        gfx_drawnumber(num_messages,0);
    );

// --------------------------------------------------------
// display the storage progress bar 
// flash it if it is getting close to Full
// --------------------------------------------------------// 

    PROGRESS_X = 320 -40; PROGRESS_Y = 70; PROGRESS_H  = 20; PROGRESS_W = 150 + 40; 

    set_colour(MID_GREY);
    gfx_x = PROGRESS_X; gfx_y = PROGRESS_Y;    
    gfx_rectto(gfx_x + PROGRESS_W,gfx_y + PROGRESS_H);
    fullness = total_msgs / num_messages;
    fullness_W = fullness * PROGRESS_W; 

    set_colour(GREEN);
    fullness >= 0.75 ? (gfx_r=0.80; gfx_g=0.50; gfx_b=0.20; );
    fullness >= 0.90 ? (gfx_r=0.80; gfx_g=0.20; gfx_b=0.20; );

    flash_int = 35; // flash interval, adjust for flash rate
    flash_count +=1;
    mode == RECORD && fullness >= 0.90 && flash_count > flash_int / 2? (
        flash = YES; 
        flash_count > flash_int ? flash_count = 0;
    ): flash = NO;

    gfx_x = PROGRESS_X; gfx_y = PROGRESS_Y;    
    flash == NO ? gfx_rectto(gfx_x + fullness_W,gfx_y + PROGRESS_H);

// .... display the % value

    fullness < 1 ? (
        gfx_x = PROGRESS_X + PROGRESS_W - 30; gfx_y = PROGRESS_Y + 6;    
        fullness < 0.90 ? set_colour(LIGHT_GREY) : set_colour(HIGHLIGHTED);
        fullness < 0.1 ? gfx_x +=8;
        gfx_drawnumber(fullness * 100,0); gfx_x +=4; gfx_drawchar($'%');
    );


// --------------------------------------------------------
// display the session length COMMENTED OUT
// --------------------------------------------------------
/*
    session_msgs > 0 ? (
        gfx_x = 518; gfx_y = 70;
        set_colour(PALE_GREY);
        gfx_drawchar($'S'); gfx_drawchar($'e'); gfx_drawchar($'s');
        gfx_drawchar($'s'); gfx_drawchar($'i'); gfx_drawchar($'o');
        gfx_drawchar($'n'); gfx_drawchar($' '); gfx_drawchar($'l');
        gfx_drawchar($'e'); gfx_drawchar($'n'); gfx_drawchar($'g');
        gfx_drawchar($'t'); gfx_drawchar($'h'); gfx_drawchar($':');
        gfx_drawchar($' ');


        start_idx = session_start[session_idx];
        end_idx = session_end[session_idx];
        precise_beats = msg_precise_beats[end_idx] - msg_precise_beats[start_idx] - beats_per_bar -1;
        LEN.print_timepos_small (precise_beats,0); // (beat_pos, align)
        
    );
*/

// --------------------------------------------------------
// display the session start and stop positions
// --------------------------------------------------------

    session_msgs > 0 || mode == PLAY ? (
        gfx_x = 500; gfx_y = 70;
        set_colour(PALE_GREY);
        gfx_drawchar($'S'); gfx_drawchar($'e'); gfx_drawchar($'s');
        gfx_drawchar($'s'); gfx_drawchar($'i'); gfx_drawchar($'o');
        gfx_drawchar($'n'); gfx_drawchar($' '); gfx_drawchar($'s');
        gfx_drawchar($'t'); gfx_drawchar($'a'); gfx_drawchar($'r');
        gfx_drawchar($'t'); gfx_drawchar($':'); gfx_drawchar($' ');
        gfx_drawchar($' ');

        start_idx = session_start[session_idx];
        precise_beats = msg_precise_beats[start_idx]; 
        START.print_timepos_small (precise_beats,0); // (beat_pos, align)
    );

    session_msgs > 0 || mode == PLAY ? (
        gfx_x = 500; gfx_y = 82;
        set_colour(PALE_GREY);
        gfx_drawchar($'S'); gfx_drawchar($'e'); gfx_drawchar($'s');
        gfx_drawchar($'s'); gfx_drawchar($'i'); gfx_drawchar($'o');
        gfx_drawchar($'n'); gfx_drawchar($' '); gfx_drawchar($'e');
        gfx_drawchar($'n'); gfx_drawchar($'d'); gfx_drawchar($':');
        gfx_drawchar($' '); gfx_drawchar($' '); gfx_drawchar($' ');
        gfx_drawchar($' ');

        end_idx = session_end[session_idx];
        precise_beats = msg_precise_beats[end_idx]; 
        END.print_timepos_small (precise_beats,0); // (beat_pos, align)
    );



// --------------------------------------------------------
// display the 4 segment display positions
// --------------------------------------------------------
    session_msgs > 0 ? (
        gfx_x = PR_X + 4; gfx_y = PR_Y -14;
        set_colour(ORANGE);
        
// autoscroll display during recording or playback 
        transport_state == PLAYING || transport_state == RECORDING? (
            display_start_beats = (beat_pos / (display_bars * beats_per_bar) |0 ) * display_bars * beats_per_bar;

//-4    msg_idx = session_start[session_idx];
//-4    session_start_pb = msg_precise_beats[msg_idx];
//-4    display_start_beats = ((session_start_pb +1) / (display_bars * beats_per_bar) |0) * display_bars * beats_per_bar;
        );
            display_end_beats =  display_start_beats + display_bars * beats_per_bar;
            precise_beats = display_start_beats;

        start_idx = session_start[session_idx];
        idx = 0;
        loop(4,
            gfx_x = PR_X +4 + (PR_W /4 * idx);
            SS_OK = DS.print_timepos_small(precise_beats, LEFT);
            precise_beats += display_bars;
            idx +=1;
        );
        gfx_x = PR_X + PR_W -64;  
    );


// --------------------------------------------------------
// display the grid lines and positions
// --------------------------------------------------------

    PR_X = 20; PR_Y = 110;  
    PR_H = max(200, gfx_h) - 160;
    PR_W = max(gfx_w, MIN_WIDTH) - PR_X - 10 -10;
    gfx_r = 0.60; gfx_g = 0.60; gfx_b = 0.80;
    set_colour(LIGHT_GREY);

    gfx_x = PR_X;
    gfx_y = PR_Y;
    gfx_lineto(gfx_x + PR_W, gfx_y,1);
    gfx_x = PR_X;
    gfx_y = PR_Y + PR_H + 8; // to allow for the  maximum note height
    gfx_lineto(gfx_x + PR_W, gfx_y,1);
    
    gfx_x = PR_X;
    gfx_y = PR_Y;
    idx =0;
    loop(17,
        gfx_y = PR_Y;
        (idx % 4) == 0 ? gfx_a = 1.00 : gfx_a = 0.60;
        gfx_lineto(gfx_x,PR_Y + PR_H + 8,1); 
        gfx_x += (PR_W/16);
        idx +=1;
    );
    gfx_a = 1.00;


// --------------------------------------------------------
// display the notes of the current session in the current display segment
// --------------------------------------------------------

    msg_idx = session_start[play_session_idx];
    session_start_pb = (msg_precise_beats[msg_idx] / (display_bars * beats_per_bar) |0 ) * display_bars * beats_per_bar;
    msg_idx2 = session_end[play_session_idx];
    session_end_pb = ((msg_precise_beats[msg_idx2] / (display_bars * beats_per_bar) |0) +1) * display_bars * beats_per_bar;
    mpx = max_pitch[play_session_idx];
    
    session_length = session_end_pb - session_start_pb;
    range = max_pitch[play_session_idx] - min_pitch[play_session_idx];
    note_height = (max(2,min(16, (PR_H  / range))) | 0) -1; 
    pitch_height = (PR_H - note_height) / range;  
    scaler = 16 / display_bars;

    set_colour(HIGHLIGHTED);
    DIAMOND_X = 3050; DIAMOND_Y = 70; DIAMOND_H = 10; DIAMOND_W = 10;
    PR_Y +=4;
    
    loop(session_msgs,
        (msg_type[msg_idx] == NOTE_ON && msg_val[msg_idx] > 0) ? (
            pitch = msg_num[msg_idx];
            note_start[pitch] = msg_precise_beats[msg_idx] - display_start_beats;
            note_vel[pitch] = msg_val[msg_idx];
        );

        msg_type[msg_idx] == NOTE_OFF 
        || (msg_type[msg_idx] == NOTE_ON && msg_val[msg_idx] == 0)? (
            pitch = msg_num[msg_idx];
            note_end[pitch] = msg_precise_beats[msg_idx] - display_start_beats;
            note_start_disp = PR_X + (note_start[pitch] / 64 * PR_W * scaler) +1;
            note_end_disp   = PR_X + (note_end[pitch] / 64 * PR_W * scaler) -1;
            
            gfx_x = note_start_disp;
            gfx_y = PR_Y + (mpx - pitch) * pitch_height;

            note_start_disp >= PR_X && note_end_disp <= PR_X + PR_W ? (
                vel_shade == YES ? gfx_a = (note_vel[pitch] +64) /192; // range 0.33 to 1.00
                drum_mode == NO ? (
                    hover_x = gfx_x; hover_w = max(2,note_end_disp - gfx_x);
                    hover_y = gfx_y; hover_h = note_height;
                    gfx_rectto(max(note_end_disp, gfx_x+3), gfx_y + note_height);  // minimum width of 3 pixels 
                ):(
                    hover_x = gfx_x - DIAMOND_W /2; hover_w = DIAMOND_W /2;
                    hover_y = gfx_y; hover_h = DIAMOND_H;  
SHAPE = 0; // set to 0 for diamonds, 5 for triangles
                    coordinatelist[0] = DIAMOND_X + SHAPE;
                    coordinatelist[1] = DIAMOND_Y;
                    coordinatelist[2] = DIAMOND_W - SHAPE; 
                    coordinatelist[3] = DIAMOND_H; // source x y w h 

                    coordinatelist[4] = gfx_x - (5 - SHAPE); 
                    coordinatelist[5] = gfx_y -2; 
                    coordinatelist[6] = DIAMOND_W - SHAPE;
                    coordinatelist[7] = DIAMOND_H;  // dest    x y w h

                    coordinatelist[8] = 0;
                    coordinatelist[9] = 0;
                    gfx_blitext(BIG_ASCII_,coordinatelist,0);
                );
                gfx_a = 1.00;
// -----------------------------------------------
// display the note details, on hovering
// .... overwritten by subsequent notes

                mouse_x > hover_x && mouse_x < (hover_x + hover_w)  
                && mouse_y > hover_y && mouse_y < (hover_y + hover_h) ? (
                    gfx_x = max(PR_X,min(PR_X + PR_W -32,hover_x));

                    mouse_y < PR_Y  + 2 + note_height ? gfx_y = PR_Y  + 4 + note_height 
                                                      : gfx_y = hover_y -12;
                    KNB.print_note_details (pitch,note_vel[pitch]);
                );

// -----------------------------            
            
            );
            note_start[pitch] = NO;
        );

        msg_idx +=1;
    );


// --------------------------------------------------------
// display the function buttons
// --------------------------------------------------------

//  Button co-ordinates
    BTN_OFFSET = PR_Y + PR_H + 16; 
    
    OFF_X = 20; OFF_Y = BTN_OFFSET; OFF_H  = 20; OFF_W = 70; 
    RECORD_X = 100; RECORD_Y = BTN_OFFSET; RECORD_H  = 20; RECORD_W = 70; 
    PLAYBACK_X = 180; PLAYBACK_Y = BTN_OFFSET; PLAYBACK_H  = 20; PLAYBACK_W = 70; 
    AUTOSW_X = 260; AUTOSW_Y = BTN_OFFSET; AUTOSW_H  = 20; AUTOSW_W = 70 -50; 
    CLEAR_X = 360 -40; CLEAR_Y = BTN_OFFSET; CLEAR_H  = 20; CLEAR_W = 50; 
    AUTOCLEAR_X = 420 -40; AUTOCLEAR_Y = BTN_OFFSET; AUTOCLEAR_H  = 20; AUTOCLEAR_W = 20; 
    DRUM_X = 420; DRUM_Y = BTN_OFFSET; DRUM_H  = 20; DRUM_W = 70;     
    VEL_X = 500; VEL_Y = BTN_OFFSET; VEL_H  = 20; VEL_W = 20; 

    NEXT_X = max(gfx_w,MIN_WIDTH) - 40; NEXT_Y = BTN_OFFSET; NEXT_H  = 20; NEXT_W = 20; 
    WIDTH_X = max(gfx_w,MIN_WIDTH) - 120; WIDTH_Y = BTN_OFFSET; WIDTH_H  = 20; WIDTH_W = 70; 
    PREV_X = max(gfx_w,MIN_WIDTH) - 150; PREV_Y = BTN_OFFSET; PREV_H  = 20; PREV_W = 20; 

// ------------------------------------------------
// display the Off button: label, button background and state

    mode == OFF ? (
        set_colour(ORANGE);
    ):(
        set_colour(BLUE_GREY);
    );
    gfx_x = OFF_X; gfx_y = OFF_Y;    
    gfx_rectto(gfx_x + OFF_W,gfx_y + OFF_H);
    gfx_x = OFF_X + 16; gfx_y = OFF_Y +6;    
    set_colour(DARK_GREY);
    gfx_x = OFF_X + 20; gfx_y = OFF_Y + 6; 
    gfx_drawchar($'O'); gfx_drawchar($'f'); gfx_drawchar($'f'); 

// ------------------------------------------------
// display the Record button: label, button background and state

    mode == RECORD ? (
        set_colour(ORANGE);
    ):(
        set_colour(BLUE_GREY);
    );
    gfx_x = RECORD_X; gfx_y = RECORD_Y;    
    gfx_rectto(gfx_x + RECORD_W,gfx_y + RECORD_H);
    gfx_x = RECORD_X + 16; gfx_y = RECORD_Y +6;    
    set_colour(DARK_GREY);
    gfx_x = RECORD_X+11; gfx_y = RECORD_Y + 6; 
    gfx_drawchar($'R'); gfx_drawchar($'e'); gfx_drawchar($'c'); 
    gfx_drawchar($'o'); gfx_drawchar($'r'); gfx_drawchar($'d');

// ------------------------------------------------
// display the Playback button: label, button background and state
    mode == PLAY ? (
        set_colour(ORANGE);
    ):(
        set_colour(BLUE_GREY);
    );
    gfx_x = PLAYBACK_X; gfx_y = PLAYBACK_Y;    
    gfx_rectto(gfx_x + PLAYBACK_W,gfx_y + PLAYBACK_H);
    gfx_x = PLAYBACK_X + 3; gfx_y = PLAYBACK_Y +6; 
    set_colour(DARK_GREY);
    gfx_drawchar($'P'); gfx_drawchar($'l'); gfx_drawchar($'a');
    gfx_drawchar($'y'); gfx_drawchar($'b'); gfx_drawchar($'a');
    gfx_drawchar($'c'); gfx_drawchar($'k');


// ------------------------------------------------
// display the Auto Switch button: label, button background and state
// click Play in the transport and the Recorder switches to recording
// click Record in the transport and the Recorder switches to playing back 

    auto_mode == YES ? (
        set_colour(ORANGE);
    ):(
        set_colour(BLUE_GREY);
    );
    gfx_x = AUTOSW_X; gfx_y = AUTOSW_Y;    
    gfx_rectto(gfx_x + AUTOSW_W,gfx_y + AUTOSW_H);

    set_colour(DARK_GREY);
    gfx_x = AUTOSW_X+6; gfx_y = AUTOSW_Y + 6;
    gfx_drawchar($'A');

// ------------------------------------------------
// display the Clear button: label, button background and state
    mode == CLEAR ? (
        set_colour(ORANGE);
    ):(
        set_colour(MATT_RED);
    );
    gfx_x = CLEAR_X; gfx_y = CLEAR_Y;    
    gfx_rectto(gfx_x + CLEAR_W,gfx_y + CLEAR_H);

    gfx_x = CLEAR_X+5; gfx_y = CLEAR_Y +2; 
    set_colour(DARK_GREY);
    gfx_drawchar($'C');  gfx_drawchar($'l'); gfx_drawchar($'e');
    gfx_drawchar($'a'); gfx_drawchar($'r');
    gfx_x = CLEAR_X+13; gfx_y = CLEAR_Y +12; 
    gfx_drawchar($'A');  gfx_drawchar($'l'); gfx_drawchar($'l');
    gfx_x = CLEAR_X + 16; gfx_y = CLEAR_Y +6;    

// ------------------------------------------------
// display the Auto Clear button: label, button background and state

    auto_clear == NO ? (
        set_colour(BLUE_GREY);
    ):(
        set_colour(MATT_RED);
    );
    gfx_x = AUTOCLEAR_X; gfx_y = AUTOCLEAR_Y;    
    gfx_rectto(gfx_x + AUTOCLEAR_W,gfx_y + AUTOCLEAR_H);
    set_colour(DARK_GREY);
    gfx_x = AUTOCLEAR_X+6; gfx_y = AUTOCLEAR_Y + 6;
    gfx_drawchar($'A');

// ------------------------------------------------
// display the Drum button: label, button background and state
   drum_mode == YES ? (
        set_colour(ORANGE);
    ):(
        set_colour(BLUE_GREY);
    );
    gfx_x = DRUM_X; gfx_y = DRUM_Y;    
    gfx_rectto(gfx_x + DRUM_W,gfx_y + DRUM_H);
    gfx_x = DRUM_X + 16; gfx_y = DRUM_Y +6;    
    set_colour(DARK_GREY);
    gfx_x = DRUM_X + 20; gfx_y = DRUM_Y + 6; 
    gfx_drawchar($'D'); gfx_drawchar($'r'); gfx_drawchar($'u'); gfx_drawchar($'m'); 


// ------------------------------------------------
// display the Velocity button: label, button background and state
    vel_shade == NO ? (
        set_colour(BLUE_GREY);
    ):(
        set_colour(ORANGE);
    );
    gfx_x = VEL_X; gfx_y = VEL_Y;    
    gfx_rectto(gfx_x + VEL_W,gfx_y + VEL_H);
    set_colour(DARK_GREY);
    gfx_x = VEL_X+6; gfx_y = VEL_Y + 6;
    gfx_drawchar($'V');  
  
// ------------------------------------------------
// display the Display Width button:  
//     label, button background and value

    set_colour(ORANGE);
    gfx_x = WIDTH_X; gfx_y = WIDTH_Y;    
    gfx_rectto(gfx_x + WIDTH_W,gfx_y + WIDTH_H);
    gfx_x = WIDTH_X + 16; gfx_y = WIDTH_Y +6;    
    set_colour(DARK_GREY);
    display_bars < 10 ? gfx_x = WIDTH_X+11 : gfx_x = WIDTH_X+7;
    gfx_y = WIDTH_Y + 6; 
    gfx_Drawnumber(display_bars,0);
    gfx_drawchar($' '); gfx_drawchar($'B'); gfx_drawchar($'a'); gfx_drawchar($'r'); 
    display_bars > 1 ? gfx_drawchar($'s');
    gfx_drawchar($' '); 

// ------------------------------------------------
// display the Next and Previous buttons: 
//     label, button background and value
    
//-4    (session_length > display_bars * beats_per_bar) && display_start_beats > 0 ? (

    session_start_pb < display_start_beats ? (
        set_colour(ORANGE);
        gfx_x = PREV_X; gfx_y = PREV_Y;    
        gfx_rectto(gfx_x + PREV_W,gfx_y + PREV_H);

        set_colour(DARK_GREY);
        gfx_x = PREV_X + 1;
        gfx_y = PREV_Y + 6; 
        gfx_drawchar($'<'); gfx_drawchar($'<');
    ):(
        set_colour(BLUE_GREY);
        gfx_x = PREV_X; gfx_y = PREV_Y;    
        gfx_rectto(gfx_x + PREV_W,gfx_y + PREV_H);
    );


//-4     last_segment = (session_length / (display_bars * beats_per_bar) | 0) * beats_per_bar * display_bars;
//-4    (session_length > display_bars * beats_per_bar) && last_segment > display_start_beats ? (
    session_end_pb > display_end_beats ? (
        set_colour(ORANGE);
        gfx_x = NEXT_X; gfx_y = NEXT_Y;    
        gfx_rectto(gfx_x + NEXT_W,gfx_y + NEXT_H);
    
        set_colour(DARK_GREY);
        gfx_x = NEXT_X + 3;
        gfx_y = NEXT_Y + 6; 
        gfx_drawchar($'>'); gfx_drawchar($'>'); 
    ):(
        gfx_r=0.40; gfx_g=0.40; gfx_b=0.70;
        set_colour(BLUE_GREY);
        gfx_x = NEXT_X; gfx_y = NEXT_Y;    
        gfx_rectto(gfx_x + NEXT_W,gfx_y + NEXT_H);
    );

// ==================================================================================
// handle the mouse navigation 
// ==================================================================================

    NO_CLICK = 0;
    CLICK = 1;
    RIGHT_CLICK = 2;
    SHIFT_RIGHT_CLICK = 10;

    DRAG_PBID = 21;
    DRAG_DW = 22; 

    mouse_cap != NO_CLICK ? (

// --------------------------------------------------------
// .... Clicks
// --------------------------------------------------------
        mouse_cap == CLICK && first_time ? (
            first_time = 0;

// --------------------------------------------------------
// .... click on the OFF button

            (mouse_x >= OFF_X && mouse_x <= (OFF_X + OFF_W) 
            && mouse_y>= OFF_Y && mouse_y<= (OFF_Y + OFF_H)) ? (
                mode = OFF;    
                slider1 = OFF;
                session_msgs = 0;
            );

// --------------------------------------------------------
// .... click on the RECORD button

            (mouse_x >= RECORD_X && mouse_x <= (RECORD_X + RECORD_W) 
            && mouse_y>= RECORD_Y && mouse_y<= (RECORD_Y + RECORD_H)) ? (
                mode = RECORD;    
                slider1 = RECORD;
                display_start_beats = 0;
             );

// --------------------------------------------------------
// .... click on the PLAYBACK button

            (mouse_x >= PLAYBACK_X && mouse_x <= (PLAYBACK_X + PLAYBACK_W) 
            && mouse_y>= PLAYBACK_Y && mouse_y<= (PLAYBACK_Y + PLAYBACK_H)) ? (
                mode = PLAY;    
                slider1 = PLAY;
                display_start_beats = 0;
            );

// --------------------------------------------------------
// .... click on the AUTOSW button

            (mouse_x >= AUTOSW_X && mouse_x <= (AUTOSW_X + AUTOSW_W) 
            && mouse_y>= AUTOSW_Y && mouse_y<= (AUTOSW_Y + AUTOSW_H)) ? (
                auto_mode == YES ? auto_mode = NO : auto_mode = YES;    
            );

// --------------------------------------------------------
// .... click on the CLEAR button

            (mouse_x >= CLEAR_X && mouse_x <= (CLEAR_X + CLEAR_W) 
            && mouse_y>= CLEAR_Y && mouse_y<= (CLEAR_Y + CLEAR_H)) ? (
                mode = CLEAR;    
                memset(start_msg_buffer, EMPTY, num_messages * num_msg_items); 
                memset(start_sessions, EMPTY, num_sessions * num_sess_items); 
                memset(min_pitch,128,num_sessions); 
                widx = 0;
                ridx = 0;
                msg_count = 0;
                first_rec = YES;
                first_play = NO;
                slider1 = CLEAR;
                slider2 = 0;
                slider3 = 0;
                slider5 = 0; session_msgs = 0;
                slider6 = 0; total_msgs = 0;
                rec_session_idx = slider2 -1;
                play_session_idx = slider3 -1;
                display_start_beats = 0;
                mode = RECORD;
            );

// --------------------------------------------------------
// .... click on the AUTOCLEAR button
            (mouse_x >= AUTOCLEAR_X && mouse_x <= (AUTOCLEAR_X + AUTOCLEAR_W) 
            && mouse_y>= AUTOCLEAR_Y && mouse_y<= (AUTOCLEAR_Y + AUTOCLEAR_H)) ? (
                auto_clear == YES ? auto_clear = NO : auto_clear = YES;    
            );


// --------------------------------------------------------
// .... click on the DRUM button
            (mouse_x >= DRUM_X && mouse_x <= (DRUM_X + DRUM_W) 
            && mouse_y>= DRUM_Y && mouse_y<= (DRUM_Y + DRUM_H)) ? (
                drum_mode == YES ? drum_mode = NO : drum_mode = YES;
            );


// --------------------------------------------------------
// .... click on the VEL SHADE button
            (mouse_x >= VEL_X && mouse_x <= (VEL_X + VEL_W) 
            && mouse_y>= VEL_Y && mouse_y<= (VEL_Y + VEL_H)) ? (
                vel_shade == YES ? vel_shade = NO : vel_shade = YES;    
            );
                                    
// --------------------------------------------------------
// .... click on the DISPLAY WIDTH button
// .... cycle the beats value through 1, 2, 4, 8, 16, 32 and 64 beats

            (mouse_x >= WIDTH_X && mouse_x <= (WIDTH_X + WIDTH_W) 
            && mouse_y>= WIDTH_Y && mouse_y<= (WIDTH_Y + WIDTH_H)) ? (

                mouse_x < (WIDTH_X + WIDTH_W /2) ? (
                    display_bars /= 2;
                    display_bars < 1 ? display_bars = 64;
                ):(
                    display_bars *= 2;
                    display_bars > 64 ? display_bars = 1;                
                );
                display_start_beats =  display_start_beats / ((display_bars * beats_per_bar) |0 ) * display_bars * beats_per_bar; 
                mouse_target = DRAG_DW;
            );
             

// --------------------------------------------------------
// .... click on the Previous or Next buttons

            (mouse_x >= PREV_X && mouse_x <= (PREV_X + PREV_W) 
            && mouse_y>= PREV_Y && mouse_y<= (PREV_Y + PREV_H)) ? (
                display_start_beats = max(session_start_pb,
                                          display_start_beats - (display_bars * beats_per_bar));    
            );

            (mouse_x >= NEXT_X && mouse_x <= (NEXT_X + NEXT_W) 
            && mouse_y>= NEXT_Y && mouse_y<= (NEXT_Y + NEXT_H)) ? (
                display_start_beats = min(session_end_pb - (display_bars * beats_per_bar),
                                         display_start_beats + (display_bars * beats_per_bar));    
            );

// --------------------------------------------------------
// .... click and drag on the Playback session Id

            mode == PLAY  
            && (mouse_x >= (PBID_X - 20) && mouse_x <= (PBID_X + 40) 
            && mouse_y>= 20 && mouse_y<= 50) ? (
                mouse_target = DRAG_PBID;
            );
        );  //end of "mouse_cap == CLICK && first_time ? "
 
// --------------------------------------------------------
// .... end of handling the specific mouse clicks


// --------------------------------------------------------
// .... handle vertical mouse dragging for the Playback Session Id

        mouse_cap != NO_CLICK && mouse_target == DRAG_PBID ? (
            mouse_y > save_Y ? s3x = max(s3x -= 0.2,1);
            mouse_y < save_Y ? s3x = min(s3x += 0.2,rec_session_idx +1);
            slider3 = floor(s3x + 0.001);
            play_session_idx = slider3 -1;
            session_idx = play_session_idx;
//            display_start_beats = 0;

            msg_idx = session_start[session_idx];
            display_start_beats = (msg_precise_beats[msg_idx] 
                                  / (display_bars * beats_per_bar) |0 ) * display_bars * beats_per_bar;

            session_msgs = session_end[session_idx] - session_start[session_idx] +1;
            slider5 = session_msgs;
            sliderchange(-1);
        );

// -------------------------------------------------------

        save_X = mouse_x;
        save_Y = mouse_y;
    );
    mouse_cap == NO_CLICK ? (first_time = YES; mouse_target = 0; );


// END ==========================================================================
