Trivial Solutions Corp.
                        Happy Programmers Make Us Happy

The LoseThos 64-bit PC Operating System
File:/LT/Apps/MusicOrgan/Organ.CPZ
This, and all LoseThos files, are public domain.  Do whatever you like.


/*
This uses four types of controls

  1) The pull-down menu.

  2) The Ctrl type for the tempo/stacatto sliders.

  3) The active Ltf picture buttons with macros for left/record/play/right.

  4) Hand-made controls for the kbdgraphic, notes and staff.




This uses four types of output

  1) The Ltf for the text and buttons and kbdgraphic, drawn by Fs->update_win().

  2) The Fs->draw_it() for the staff region, drawn every refresh.

  3) The gr_persistent_base persistent layer for the note chooser, meter 
chooser.  The persistent layer is used during drag-and-drop.

  4) The Fs->next_ctrl for the tempo/stacatto sliders.


See GrUpdateWins() and GrUpdateScreen().


*/

#define OT_ROOT         0
#define OT_NOTE         1
#define OT_METER        2


#define Of_SELECTED     0

#define OF_SELECTED     1

class OrgNote
{
  OrgNote *next,*last;
  I8 x,y;
  U1 *word;
  U1 type,flags;
  union {
     U1 note;
     U1 meter_top;
  };
  union {
     U1 octave;
     U1 meter_bottom;
  }
  U1 duration,accent,width,waveform;
  U1 ascii[32];
};

//Tool types
#define OTT_PTR_TOOL    0
#define OTT_BOX_TOOL    1

class OrgCtrl
{
  OrgNote root;
  OrgNote clipboard;
  MenuEntry *incomplete_entry,*record_entry;
  I8 screen_x,
     tool;
  OrgNote *cur_note;
  GrBitMap *base2;
  BoolI1 playing;
} oc;

U1 *org_note_list="A\0A#\0B\0C\0C#\0D\0D#\0E\0F\0F#\0G\0G#\0";
U1 org_note_map[12]={6,6,5,4,4,3,3,2,1,1,0,0};
U1 org_note_inverse_map[7]={10,8,7,5,3,2,0};


        <1>/* Graphics Not Rendered in HTML */



        <2>/* Graphics Not Rendered in HTML */



        <3>/* Graphics Not Rendered in HTML */



        <4>/* Graphics Not Rendered in HTML */



        <5>/* Graphics Not Rendered in HTML */


        <6>/* Graphics Not Rendered in HTML */


        <7>/* Graphics Not Rendered in HTML */

        
        <8>/* Graphics Not Rendered in HTML */


        <9>/* Graphics Not Rendered in HTML */


        <10>/* Graphics Not Rendered in HTML */


        <11>/* Graphics Not Rendered in HTML */







        <12>/* Graphics Not Rendered in HTML */



        


        <13>/* Graphics Not Rendered in HTML */


        <14>/* Graphics Not Rendered in HTML */


        <15>/* Graphics Not Rendered in HTML */


        <16>/* Graphics Not Rendered in HTML */


        <17>/* Graphics Not Rendered in HTML */




        <18>/* Graphics Not Rendered in HTML */

#define ORG_NOTE_SPACING        9
#define ORG_NUM_DURATIONS       12

F8 org_durations[ORG_NUM_DURATIONS+1]={
2*.25/3,.25,2*.5/3,.5,2.0/3.0,0.5*1.5,1.0,1.5,2.0,3.0,4.0,6.0,1000000.0};
U1 *org_duration_list="st\0s\0et\0e\0qt\0e.\0q\0q.\0h\0h.\0w\0w.\0";
U1 *org_duration_imgs[ORG_NUM_DURATIONS]={<1>,<1>,<2>,
                        <2>,<3>,<2>,<3>,<3>,
                        <4>,<4>,<5>,<5>};
BoolI1 org_triplet_durations[ORG_NUM_DURATIONS]={TRUE,FALSE,TRUE,
                        FALSE,TRUE,FALSE,FALSE,FALSE,
                        FALSE,FALSE,FALSE,FALSE};

BoolI8 OrgIsSharp(I8 note)
{
  U1 *st=ListEntryPoint(note,org_note_list);
  if (st[1]=='#')
    return TRUE;
  else
    return FALSE;
}

BoolI8 OrgIsFlat(I8 note)
{
  U1 *st=ListEntryPoint(note,org_note_list);
  if (st[1]=='b')
    return TRUE;
  else
    return FALSE;
}

BoolI8 OrgIsDotted(I8 duration)
{
  U1 *st=ListEntryPoint(duration,org_duration_list);
  if (st[1]=='.')
    return TRUE;
  else
    return FALSE;
}

U0 OrgSetWidth(OrgNote *tempo)
{
  if (tempo->type==OT_METER)
    tempo->width=12;
  else {
    tempo->width=ORG_NOTE_SPACING;
    if (OrgIsDotted(tempo->duration))
      tempo->width+=ORG_NOTE_SPACING/2;
    if (OrgIsSharp(tempo->note) || OrgIsFlat(tempo->note))
      tempo->width+=ORG_NOTE_SPACING;
  }
}

U0 OrgRecalcNoteXY()
{
  F8 measure_len=4,measure_left=measure_len;
  OrgNote *tempo=oc.root.next;
  I8 x=8-oc.screen_x;
  while (TRUE) {
    tempo->x=x;
    tempo->y=50;
    if (tempo==&oc.root)
      break;
    else {
      if (tempo->type!=OT_METER && tempo->octave) //note, not rest
        tempo->y=(15+(org_note_map[tempo->note]-7*(tempo->octave-2)))*4;
      x+=tempo->width;
      if (tempo->type==OT_METER) {
        measure_len=tempo->meter_top*4.0/tempo->meter_bottom;
        measure_left=0;
      } else
        measure_left-=org_durations[tempo->duration];
      if (measure_left<0.001) {
        x+=ORG_NOTE_SPACING;
        measure_left=measure_len;
      }
    }
    tempo=tempo->next;
  }
}

U0 OrgMarkSelected(I8 x1,I8 x2,BoolI1 sel)
{
  OrgNote *tempo=oc.root.next;
  while (tempo!=&oc.root) {
    if (sel) {
      if (x1<=tempo->x<=x2)
        tempo->flags|=OF_SELECTED;
    } else
      tempo->flags&=~OF_SELECTED;
    tempo=tempo->next;
  }
}

OrgNote *OrgNoteCopy(OrgNote *tempo)
{
  OrgNote *tempo1=MAllocIdentical(tempo);
  if (tempo->word)
    tempo1->word=StrNew(tempo->word);
  return tempo1;
}

U0 OrgNoteDel(OrgNote *tempo)
{
  Free(tempo->word);
  Free(tempo);
}

U0 OrgSongDel(OrgNote *root)
{
  OrgNote *tempo,*tempo1;
  tempo=root->next;
  while (tempo!=root) {
    tempo1=tempo->next;
    OrgNoteDel(tempo);
    tempo=tempo1;
  }
  root->next=root->last=root;
}

U0 OrgCutToClipboard()
{
  OrgNote *tempo,*tempo1;
  OrgSongDel(&oc.clipboard);
  tempo=oc.root.next;
  while (tempo!=&oc.root) {
    tempo1=tempo->next;
    if (tempo->flags&OF_SELECTED) {
      if (oc.cur_note==tempo)
        oc.cur_note=tempo->next;
      RemQue(tempo);
      tempo->flags&=~OF_SELECTED;
      InsQue(tempo,oc.clipboard.last);
    }
    tempo=tempo1;
  }
}

U0 OrgPasteClipboard()
{
  OrgNote *tempo,*tempo1;
  tempo=oc.clipboard.next;
  while (tempo!=&oc.clipboard) {
    tempo1=OrgNoteCopy(tempo);
    InsQue(tempo1,oc.cur_note->last);
    tempo=tempo->next;
  }
}

U0 OrgCopyToClipboard()
{
  OrgNote *tempo,*tempo1;
  OrgSongDel(&oc.clipboard);
  tempo=oc.root.next;
  while (tempo!=&oc.root) {
    if (tempo->flags&OF_SELECTED) {
      tempo->flags&=~OF_SELECTED;
      tempo1=OrgNoteCopy(tempo);
      InsQue(tempo1,oc.clipboard.last);
    }
    tempo=tempo->next;
  }
}

OrgNote *OrgFindNote(I8 x,I8 y)
{
  nounusedwarn y;
  OrgNote *tempo=oc.root.next;
  OrgRecalcNoteXY;
  x+=ORG_NOTE_SPACING/2;
  while (x>tempo->next->x && tempo!=&oc.root)
    tempo=tempo->next;
  return tempo;
}

BoolI8 OrgBlink(F8 t)
{
  if (Blink(,t) || oc.playing)
    return TRUE;
  else
    return FALSE;
}

U0 DrawNote(GrBitMap *base,I8 x,I8 y,I8 duration)
{
  if (0<=duration<=ORG_NUM_DURATIONS) {
    base->bkcolor=WHITE;
    GrElemsPlot(base,x,y,0,org_duration_imgs[duration]);
    if (org_triplet_durations[duration])
      GrElemsPlot(base,x,y,0,<18>);
    if (OrgIsDotted(duration))
      GrElemsPlot(base,x,y,0,<19>);
  }
}

U0 DrawTimeSignature(GrBitMap *base,I8 x,I8 y,I8 top,I8 bottom)
{
  GrPrintF(base,x,y,"%d",top);
  GrPrintF(base,x,y+FONT_HEIGHT,"%d",bottom);
}

U0 DrawIt(TaskStruct *task,GrBitMap *base)
{
  OrgNote *tempo;
  I8 i,x,y,
     w=task->win_pixel_width;
  F8 measure_len=4,measure_left=measure_len;
  BoolI1 old_preempt=Preempt(OFF);

  base->color=BLACK;
  for (i=1;i<6;i++)
    GrLine(base,0,i*8,w,i*8);
  for (i=7;i<12;i++)
    GrLine(base,0,i*8,w,i*8);

  OrgRecalcNoteXY;
  if (oc.cur_note->x<64) {
    oc.screen_x-=128;
    OrgRecalcNoteXY;
  }
  if (oc.cur_note->x>=GR_WIDTH-64) {
    oc.screen_x+=128;
    OrgRecalcNoteXY;
  }
 
  tempo=oc.root.next;
  while (tempo!=&oc.root) {
    x=tempo->x;
    y=tempo->y;
    if (measure_left<0.001) {
      base->color=BLACK;
      GrLine(base,x-ORG_NOTE_SPACING,8,x-ORG_NOTE_SPACING,11*8);
      measure_left=measure_len;
    }
    if (tempo->type==OT_METER) {
      if (tempo==oc.cur_note && OrgBlink(tP(task)))
        base->color=BROWN;
      else
        base->color=BLACK;
      DrawTimeSignature(base,x,5*8,tempo->meter_top,tempo->meter_bottom);
      measure_len=tempo->meter_top*4.0/tempo->meter_bottom;
      measure_left=0;
    } else {
      if (tempo==oc.cur_note && OrgBlink(tP(task))) {
        base->color=BROWN;
        GrPutS(base,x+8,y,tempo->word);
      } else if (tempo->flags&OF_SELECTED)
        base->color=RED;
      else {
        if (tempo->octave) //rest?
          base->color=BLACK;
        else
          base->color=LTGRAY;
        if (tempo->word)
          if (StrLen(tempo->word)>1 ||
              (*tempo->word!=CH_SPACE && *tempo->word!=CH_SHIFT_SPACE))
            base->color=GREEN;
      }
      if (tempo->accent==1)
        GrElemsPlot(base,x,y,0,<20>);
      else if (tempo->accent==2)
        GrElemsPlot(base,x,y,0,<21>);
      DrawNote(base,x,y,tempo->duration);
      if (OrgIsDotted(tempo->duration))
        x+=ORG_NOTE_SPACING/2;

      if (OrgIsSharp(tempo->note))
        GrElemsPlot(base,x,y,0,<22>);
      else if (OrgIsFlat(tempo->note))
        GrElemsPlot(base,x,y,0,<23>);
      measure_left-=org_durations[tempo->duration];
    }
    tempo=tempo->next;
  }

  if (oc.cur_note==&oc.root && OrgBlink(tP(task)))
    base->color=BROWN;
  else
    base->color=BLACK;
  GrElemsPlot(base,oc.root.x,50,0,<24>);
  Preempt(old_preempt);
}

#define ORGR_STRONG_ACCENT      -9
#define ORGR_ACCENT             -8
#define ORGR_NO_ACCENT          -7
#define ORGR_SHARP              -6
#define ORGR_REST               -5
#define ORGR_INSERT_NOTE        -4
#define ORGR_DELETE_NOTE        -3
#define ORGR_SET_WORD           -2

F8 PopUpDuration()
{
  I8 i;
  Ltf *l=LtfNew;
  LtfPrintF(l,"$$FM,GREEN$$$$MU,\"Set Word\",%d$$\r\n",ORGR_SET_WORD);
  LtfPrintF(l,"$$MU,\"Toggle Sharp\",%d$$\r\n",ORGR_SHARP);
  LtfPrintF(l,"$$MU,\"Make Rest\",%d$$\r\n",ORGR_REST);
  LtfPrintF(l,"$$MU,\"Insert Note\",%d$$\r\n",ORGR_INSERT_NOTE);
  LtfPrintF(l,"$$MU,\"Delete Note\",%d$$\r\n",ORGR_DELETE_NOTE);
  LtfPrintF(l,"$$MU,\"No Accent\",%d$$\r\n",ORGR_NO_ACCENT);
  LtfPrintF(l,"$$MU,\"Accent\",%d$$\r\n",ORGR_ACCENT);
  LtfPrintF(l,"$$MU,\"Strong Accent\",%d$$\r\n\r\n",ORGR_STRONG_ACCENT);
  for (i=0;i<ORG_NUM_DURATIONS;i++)
    LtfPrintF(l,"$$MU,\"%7.5f\",%d$$\r\n",org_durations[i],i);
  LtfPrintF(l,"\r\n$$MU,\"CANCEL\",%d$$\r\n",-1);
  i=PopUpMenu(l);
  LtfDel(l);
  return i;
}

U0 OrgRightClick(I8 x,I8 y)
{
  U1 *st,*st2;
  OrgNote *tempo,*tempo1;
  I8 i;
  U8 old_ltf_flags;
  if (LtfCur) old_ltf_flags=LtfCur->flags;
  oc.cur_note=tempo=OrgFindNote(x,y);
  if (tempo!=&oc.root) {
    LBtr(&Fs->display_flags,DISPLAYf_NO_DBL_CLICK);
    win_inhibit=0;
    i=PopUpDuration;
    if (i>=0 && i<ORG_NUM_DURATIONS) {
      if (tempo->type==OT_NOTE)
        tempo->duration=i;
    } else {
      switch (i) {
        case ORGR_REST:
          if (tempo->type==OT_NOTE) {
            tempo->octave=0; //rest
            tempo->note=0;
            tempo->accent=0;
            tempo->waveform=WF_SQUARE;
          }
          break;
        case ORGR_SHARP:
          if (tempo->type==OT_NOTE && tempo->octave) {
            if (OrgIsSharp(tempo->note))
              tempo->note--;
            else
              tempo->note++;
            if (tempo->note<0) {
              tempo->note=11;
              tempo->octave--;
            } else if (tempo->note>11) {
              tempo->note=0;
              tempo->octave++;
            }
          }
          break;
        case ORGR_SET_WORD:
          if (tempo->type==OT_NOTE) {
            if (LtfCur) LtfCur->flags&=~LTFF_FORM;
            if (tempo->word)
              st2=MSPrintF("\r\nWord(\"%Q\"):",tempo->word);
            else
              st2=MSPrintF("\r\nWord(\"\"):");
            LtfBottom;
            st=PmtStr(st2);
            Free(st2);
            Free(tempo->word);
            if (*st) {
              tempo->word=MSPrintF("%q",st);
              Free(st);
            } else
              tempo->word=StrNew(" ");
            if (LtfCur) LtfCur->flags=LtfCur->flags&~LTFF_FORM|old_ltf_flags&LTFF_FORM;
          }
          break;
        case ORGR_INSERT_NOTE:
          tempo1=OrgNoteCopy(tempo);
          InsQue(tempo1,tempo);
          break;
        case ORGR_DELETE_NOTE:
          oc.cur_note=tempo->next;
          RemQue(tempo);
          OrgNoteDel(tempo);
          break;
        case ORGR_NO_ACCENT:
          if (tempo->type==OT_NOTE && tempo->octave)
            tempo->accent=0;
          break;
        case ORGR_ACCENT:
          if (tempo->type==OT_NOTE && tempo->octave)
            tempo->accent=1;
          break;
        case ORGR_STRONG_ACCENT:
          if (tempo->type==OT_NOTE && tempo->octave)
            tempo->accent=2;
          break;
      }
    }
    OrgSetWidth(oc.cur_note);
    win_inhibit=WIF_ALL-WIF_BORDER;
    LBts(&Fs->display_flags,DISPLAYf_NO_DBL_CLICK);
  }
}

#define ORG_NOTE_BOX_X  220
#define ORG_NOTE_BOX_Y  (13*FONT_HEIGHT+14)

U0 OrgSetPickNoteBoxX(I8 duration,I8 *x)
{
  I8 i;
  *x=ORG_NOTE_BOX_X;
  for (i=0;i<duration;i++) {
    if (OrgIsDotted(i))
      *x+=ORG_NOTE_SPACING/2;
    *x+=ORG_NOTE_SPACING+4;
  }
}

I8 OrgGetPickNoteBoxDuration(I8 xx,I8 yy)
{
  I8 i,x1,x2;
  if (ORG_NOTE_BOX_Y-14<=yy<ORG_NOTE_BOX_Y+6) {
    for (i=0;i<ORG_NUM_DURATIONS;i++) {
      OrgSetPickNoteBoxX(i,&x1);
      OrgSetPickNoteBoxX(i+1,&x2);
      if (x1<=xx+ORG_NOTE_SPACING/2<x2)
        return i;
    }
  }
  return -1;
}

U0 DrawPickNoteBox()
{
  I8 i,x;
  for (i=0;i<ORG_NUM_DURATIONS;i++) {
    OrgSetPickNoteBoxX(i,&x);
    if (OrgIsDotted(i))
      oc.base2->color=RED;
    else if (org_triplet_durations[i])
      oc.base2->color=LTRED;
    else
      oc.base2->color=BLACK;
    DrawNote(oc.base2,x,ORG_NOTE_BOX_Y,i);
  }
}

#define ORG_TOOLS_X     450
#define ORG_TOOLS_Y     13*FONT_HEIGHT

U0 DrawPickTools()
{
  if (oc.tool==OTT_BOX_TOOL)
    oc.base2->color=ROPF_DITHER+WHITE<<16+ROP_EQU+RED;
  else
    oc.base2->color=ROPF_DITHER+WHITE<<16+ROP_EQU+BLACK;
  GrLineRect4(oc.base2,ORG_TOOLS_X,ORG_TOOLS_Y,
    ORG_TOOLS_X+10,ORG_TOOLS_Y+10);

  if (oc.tool==OTT_PTR_TOOL)
    oc.base2->color=ROPF_DITHER+WHITE<<16+ROP_EQU+RED;
  else
    oc.base2->color=ROPF_DITHER+WHITE<<16+ROP_EQU+BLACK;
  fp_draw_input_ptr(oc.base2,ORG_TOOLS_X+15,ORG_TOOLS_Y);
  oc.base2->color=BLACK;
}


BoolI8 OrgGetPickToolBox(I8 xx,I8 yy)
{
  if (ORG_TOOLS_X<=xx<ORG_TOOLS_X+27 &&
      ORG_TOOLS_Y<=yy<ORG_TOOLS_Y+15) {
    OrgMarkSelected(0,0,FALSE);
    if (xx<ORG_TOOLS_X+13)
      oc.tool=OTT_BOX_TOOL;
    else
      oc.tool=OTT_PTR_TOOL;
    return TRUE;
  } else
    return FALSE;
}

#define ORG_NUM_METERS  7
I8 meter_tops[ORG_NUM_METERS]   ={2,3,4,5,6,7,9},
   meter_bottoms[ORG_NUM_METERS]={4,4,4,4,8,8,8};

#define ORG_METER_X     485
#define ORG_METER_Y     13*FONT_HEIGHT
#define ORG_METER_W     12
BoolI8 OrgGetPickMeterBox(I8 xx,I8 yy,I8 *top,I8 *bottom)
{
  I8 i;
  if (ORG_METER_X<=xx<ORG_METER_X+ORG_METER_W*ORG_NUM_METERS &&
      ORG_METER_Y<=yy<ORG_METER_Y+2*FONT_HEIGHT) {
    i=(xx-ORG_METER_X)/ORG_METER_W;
    *top=meter_tops[i];
    *bottom=meter_bottoms[i];
    return TRUE;
  } else
    return FALSE;
}

U0 DrawPickMeterBox()
{
  I8 i;
  oc.base2->color=BLACK;
  for (i=0;i<ORG_NUM_METERS;i++)
    DrawTimeSignature(oc.base2,ORG_METER_X+i*ORG_METER_W,ORG_METER_Y,meter_tops[i],meter_bottoms[i]);
}

U0 DrawBase2()
{
  GrClear;
  DrawPickNoteBox;
  DrawPickMeterBox;
  DrawPickTools;
}

U0 OrgLeftClickPickNoteBox(I8 duration)
{
  I8 o,n,msg_code,p1,p2;
  OrgNote *tempo,*tempo1;
  do {
    msg_code=GetMsg(&p1,&p2,1<<MSG_IP_L_UP|1<<MSG_IP_MOVE);
    if (msg_code==MSG_IP_MOVE) {
      DrawBase2;
      DrawNote(oc.base2,p1,p2,duration);
    }
  } while (msg_code!=MSG_IP_L_UP);
  if (p2<13*FONT_HEIGHT) {
    if (p1>oc.root.last->x)
      tempo1=oc.root.last;
    else if (p1<oc.root.next->x)
      tempo1=&oc.root;
    else
      tempo1=OrgFindNote(p1-ORG_NOTE_SPACING/2,p2);
    tempo=CAlloc(sizeof(OrgNote));
    tempo->type=OT_NOTE;
    p2=p2/4-15;
    n=-p2%7;
    o=2+p2/-7;
    n=-n;
    if (n<0) {
      n+=7;
      o++;
    }
    n=org_note_inverse_map[n];
    tempo->note=n;
    tempo->octave=o;
    tempo->duration=duration;
    tempo->waveform=tempo1->waveform;
    OrgSetWidth(tempo);
    InsQue(tempo,tempo1);
    oc.cur_note=tempo->next;
  }
  DrawBase2;
}

U0 OrgLeftClickPickMeterBox(I8 top,I8 bottom)
{
  I8 msg_code,p1,p2;
  OrgNote *tempo,*tempo1;
  do {
    msg_code=GetMsg(&p1,&p2,1<<MSG_IP_L_UP|1<<MSG_IP_MOVE);
    if (msg_code==MSG_IP_MOVE) {
      DrawBase2;
      DrawTimeSignature(oc.base2,p1,p2,top,bottom);
    }
  } while (msg_code!=MSG_IP_L_UP);
  if (p2<13*FONT_HEIGHT) {
    if (p1>=oc.root.x)
      tempo1=oc.root.last;
    else if (p1<oc.root.next->x)
      tempo1=&oc.root;
    else
      tempo1=OrgFindNote(p1-ORG_NOTE_SPACING/2,p2);
    tempo=CAlloc(sizeof(OrgNote));
    tempo->type=OT_METER;
    tempo->meter_top=top;
    tempo->meter_bottom=bottom;
    tempo->waveform=tempo1->waveform;
    OrgSetWidth(tempo);
    InsQue(tempo,tempo1);
    oc.cur_note=tempo->next;
  }
  DrawBase2;
}

U0 OrgLeftClickStaffPtr(I8 x,I8 y)
{
  OrgNote *tempo,*tempo1;
  I8 o,n,msg_code,p1,p2,n_original,o_original,a_original,wf_original;
  oc.cur_note=tempo=OrgFindNote(x,y);
  if (tempo!=&oc.root) {
    if (tempo->type==OT_NOTE) {
      n_original=tempo->note;
      o_original=tempo->octave;
      a_original=tempo->accent;
      wf_original=tempo->waveform;
      do {
        msg_code=GetMsg(&p1,&p2,1<<MSG_IP_L_UP|1<<MSG_IP_MOVE);
        if (msg_code==MSG_IP_L_UP) {
          tempo1=OrgFindNote(p1,p2);
          if (tempo1==&oc.root || tempo1==tempo)
            goto move_note;
          else {
            Free(tempo1->word);
            tempo1->word=tempo->word;
            tempo->word=NULL;
            tempo->note=n_original;
            tempo->octave=o_original;
            tempo->accent=a_original;
            tempo->waveform=wf_original;
          }
        } else {
move_note:
          p2=p2/4-15;
          n=-p2%7;
          o=2+p2/-7;
          n=-n;
          if (n<0) {
            n+=7;
            o++;
          }
          n=org_note_inverse_map[n];
          tempo->note=n;
          tempo->octave=o;
        }
      } while (msg_code!=MSG_IP_L_UP);
      OrgSetWidth(tempo);
    }
  }
}

U0 OrgLeftClickStaffBox(I8 x,I8 y)
{
  I8 msg_code,p1,p2;
  do {
    msg_code=GetMsg(&p1,&p2,1<<MSG_IP_L_UP|1<<MSG_IP_MOVE);
    DrawBase2;
    oc.base2->color=ROPF_DITHER+WHITE<<16+ROP_EQU+BLACK;
    GrLineRect4(oc.base2,x,y,p1,p2);
    if (msg_code==MSG_IP_L_UP) {
      if (x>p1) SwapU8(&x,&p1);
      OrgMarkSelected(x,p1,TRUE);
    }
  } while (msg_code!=MSG_IP_L_UP);
  DrawBase2;
}

U0 OrgLeftClick(I8 x,I8 y)
{
  I8 duration,top,bottom;
  if (y<13*FONT_HEIGHT) {
    if (oc.tool==OTT_PTR_TOOL)
      OrgLeftClickStaffPtr(x,y);
    else
      OrgLeftClickStaffBox(x,y);
  } else {
    duration=OrgGetPickNoteBoxDuration(x,y);
    if (0<=duration<ORG_NUM_DURATIONS)
      OrgLeftClickPickNoteBox(duration);
    else if (OrgGetPickMeterBox(x,y,&top,&bottom))
      OrgLeftClickPickMeterBox(top,bottom);
    else if (OrgGetPickToolBox(x,y))
      DrawBase2;
  }
}


#define ORGPM_NORMAL            0
#define ORGPM_REVERB1           1
#define ORGPM_NUM_PLAY_MODES    2

U8 org_play_mode=0;
F8 org_play_f=0;

U0 OrgPlayTask()
{
  U8 i=0;
  F8 f=0,f2=0;
  Fs->task_end_cb=&SndTaskEndCB;
  while (TRUE) {
    i++;
    switch (org_play_mode) {
      case ORGPM_NORMAL:
        if (org_play_f!=f) {
          f=org_play_f;
          Snd(f,music_waveform);
        }
        break;
      case ORGPM_REVERB1:
        if (org_play_f!=f) {
          if (f>0)
            f2=f;
          f=org_play_f;
        }
        if (!(i&15)) {
          if (i&16)
            Snd(f,music_waveform);
          else
            Snd(f2,music_waveform);
        }
        break;
    }
    Sleep(1);
  }
}


U1 OrgCvtDuration(F8 d)
{
  F8 d1,d2;
  I8 j;
  for (j=0;j<ORG_NUM_DURATIONS;j++) {
    d1=org_durations[j];
    d2=org_durations[j+1];
    if (d<d1*d2/(d1+d2))
      return j;
  }
  return 0;
}


U1 *OrgMusicSetOctave(U1 *st,U8 *org_octave)
{
  U1 ch=*st++;
  while (ch>='0' && ch<='9') {
    *org_octave=ch-'0';
    ch=*st++;
  }
  return --st;
}

U1 *OrgMusicSetNoteLen(U1 *st,F8 *org_duration)
{
  BoolI1 cont=TRUE;
  do {
    switch (*st++) {
      case 'w': *org_duration=4.0;  break;
      case 'h': *org_duration=2.0;  break;
      case 'q': *org_duration=1.0;  break;
      case 'e': *org_duration=0.5;   break;
      case 's': *org_duration=0.25;   break;
      case 't': *org_duration=2.0* *org_duration/3.0; break;
      case '.': *org_duration=1.5* *org_duration; break;
      default:
        st--;
        cont=FALSE;
    }
  } while (cont);
  return st;
}

U0 OrgLoadSongStr(U1 *st,U8 *org_octave,F8 *org_duration)
{
  OrgNote *tempo,*tempo1;
  U8 note,i=0,waveform=WF_SQUARE;
  while (*st) {
    tempo=CAlloc(sizeof(OrgNote));
    tempo->waveform=WF_SQUARE;
    while (*st && !('A'<=*st<='G') && *st!='R') {
      if (*st=='M') {
        tempo1=CAlloc(sizeof(OrgNote));
        tempo1->type=OT_METER;
        tempo1->waveform=tempo->waveform;
        st++;
        if ('1'<=*st<='9')
          tempo1->meter_top=*st++-'0';
        else
          tempo1->meter_top=4;
        if (*st=='/')
          st++;
        if ('1'<=*st<='9')
          tempo1->meter_bottom=*st++-'0';
        else
          tempo1->meter_bottom=4;
        OrgSetWidth(tempo1);
        InsQue(tempo1,oc.root.last);
      }
      if (*st=='W') {
        st++;
        tempo->waveform=*st++-'0';
        waveform=tempo->waveform;
      }
      while (*st=='!') {
        tempo->accent++;
        st++;
      }
      st=OrgMusicSetOctave(st,org_octave);
      st=OrgMusicSetNoteLen(st,org_duration);
    }
    if (!*st) {
      OrgNoteDel(tempo);
      break;
    }
    note=*st++-'A';
    if (note<7) {
      note=music_note_map[note];
      if (*st=='b') {
        note--;
        st++;
      } else if (*st=='#') {
        note++;
        st++;
      }
      tempo->note=note;
      tempo->octave=*org_octave;
    } else {
      tempo->note=0;
      tempo->octave=0; //rest
      tempo->accent=0;
      tempo->waveform=WF_SQUARE;
    }
    if (*org_duration<=2*.25/3)
      i=0;
    else if (*org_duration<=.25)
      i=1;
    else if (*org_duration<=2*.5/3)
      i=2;
    else if (*org_duration<=.5)
      i=3;
    else if (*org_duration<=2.0/3)
      i=4;
    else if (*org_duration<=.5*1.5)
      i=5;
    else if (*org_duration<=1.0)
      i=6;
    else if (*org_duration<=1.5)
      i=7;
    else if (*org_duration<=2.0)
      i=8;
    else if (*org_duration<=3.0)
      i=9;
    else if (*org_duration<=4.0)
      i=10;
    else
      i=11;
    tempo->duration=i;
    tempo->type=OT_NOTE;
    OrgSetWidth(tempo);
    InsQue(tempo,oc.cur_note->last);
  }
}

U0 OrgLoadSong(U1 *filename,U8 *org_octave,F8 *org_duration)
{
  U1 *st;
  OrgNote *tempo;
  LexStruct *lx=LexNew(TextFileRead(filename),0,StrNew(filename));
  Lex(lx);
  if (FileOccurrences("incomplete",filename,""))
    oc.incomplete_entry->checked=TRUE;
  else
    oc.incomplete_entry->checked=FALSE;
  while (lx->token) {
    if (lx->token==TK_IDENT)
      if (!StrCmp(lx->ident,"Play")) {
        if (Lex(lx)=='(')
          if (Lex(lx)==TK_STR) {
            tempo=oc.root.last;
            st=LexExtendStr(lx);
            OrgLoadSongStr(st,org_octave,org_duration);
            if (lx->token==',') {
              if (Lex(lx)==TK_STR) {
                st=LexExtendStr(lx);
                do {
                  do tempo=tempo->next;
                  while (tempo!=&oc.root && tempo->type==OT_METER);
                  if (tempo!=&oc.root)
                    tempo->word=StrNew(st);
                  st+=StrLen(st)+1;
                } while (*st);
              }
            }
          }
      } else if (!StrCmp(lx->ident,"music_tempo")) {
        if (Lex(lx)=='=' && Lex(lx)==TK_F8) {
          music_tempo=lx->cur_f-0.0005;
          tempo_state.tempo=Round(TEMPO_RANGE*(music_tempo-0.5)/4.4);
        }
      } else if (!StrCmp(lx->ident,"music_stacatto_factor")) {
        if (Lex(lx)=='=' && Lex(lx)==TK_F8) {
          music_stacatto_factor=lx->cur_f-0.0005;
          tempo_state.stacatto=Round(TEMPO_RANGE*(music_stacatto_factor-0.12)/0.88);
        }
      }
    Lex(lx);
  }
  LexDel(lx);
}

U1 *OrgCvtSong()
{
  OrgNote *tempo;
  U1 *st,*src,*dst;
  U8 i,note,octave,last_octave,last_duration,accent,waveform,last_waveform;

  i=0;
  tempo=oc.root.next;
  last_octave=-1;
  last_duration=-1;
  last_waveform=-1;
  while (tempo!=&oc.root) {
    dst=&tempo->ascii;
    if (tempo->type==OT_METER) {
      *dst++='M';
      *dst++=tempo->meter_top+'0';
      *dst++='/';
      *dst++=tempo->meter_bottom+'0';
    } else {
      octave=tempo->octave;
      note=tempo->note;
      accent=tempo->accent;
      waveform=tempo->waveform;
      if (waveform!=last_waveform && (note || octave)) {
        *dst++='W';
        *dst++=waveform+'0';
        last_waveform=waveform;
      }
      while (accent--)
        *dst++='!';
      if (octave!=last_octave && (note || octave)) {
        *dst++=octave+'0';
        last_octave=octave;
      }
      if (tempo->duration!=last_duration) {
        src=ListEntryPoint(tempo->duration,org_duration_list);
        *dst++=src[0];
        if (src[1])
          *dst++=src[1];
        last_duration=tempo->duration;
      }
      if (note || octave) {
        src=ListEntryPoint(note,org_note_list);
        *dst++=src[0];
        if (src[1])
          *dst++=src[1];
      } else
        *dst++='R';
    }
    *dst++=0;
    i+=StrLen(tempo->ascii);
    tempo=tempo->next;
  }

  st=MAlloc(i+1);
  dst=st;
  tempo=oc.root.next;
  while (tempo!=&oc.root) {
    StrCpy(dst,tempo->ascii);
    dst+=StrLen(tempo->ascii);
    tempo=tempo->next;
  }
  *dst++=0;
  return st;
}

U0 OrgDelSong(U1 *full_filename)
{
  EditFileNameStruct fn;
  if (full_filename) {
    StrCpy(fn.name,full_filename);
    if (LtfFormDo(&fn,"EditFileNameStruct"))
      Del(fn.name);
  }
}

U1 *OrgSaveSong(U1 *dirname,U1 *full_filename)
{
  Ltf *l=LtfNew;
  BoolI1 has_words;
  OrgNote *tempo,*tempo1;
  F8 measure_len=4,two_measure_left=2*measure_len;
  U1 *ptr,ch;
 
  Free(OrgCvtSong); //set tempo->ascii;

  music_tempo=4.4*tempo_state.tempo/TEMPO_RANGE+0.5;
  music_stacatto_factor=0.88*tempo_state.stacatto/TEMPO_RANGE+0.12;

  has_words=FALSE;
  tempo=oc.root.next;
  while (tempo!=&oc.root) {
    if (tempo->word) has_words=TRUE;
    tempo=tempo->next;
  }
  if (oc.incomplete_entry->checked)
    LtfPutS(l,"//0 incomplete\r\n");
  else if (has_words)
    LtfPutS(l,"//0 has words\r\n");
  else
    LtfPutS(l,"//0 no nothing\r\n");

  LtfPutS(l,
"U0 Song()\r\n"
"{\r\n"
"  Fs->task_end_cb=&SndTaskEndCB;\r\n"
"  MusicSettingsReset;\r\n");

LtfPrintF(l,"  music_tempo=%6.3f;\r\n",music_tempo+0.0005);
LtfPrintF(l,"  music_stacatto_factor=%6.3f;\r\n",music_stacatto_factor+0.0005);

LtfPutS(l,
"  try {\r\n"
"  while (!ScanKey) {\r\n"
"\tPlay(\"");

  tempo=oc.root.next;
  tempo1=tempo;
  has_words=FALSE;
  while (tempo!=&oc.root) {
    LtfPutS(l,tempo->ascii);
    if (tempo->word) has_words=TRUE;
    if (tempo->type==OT_METER) {
      measure_len=tempo->meter_top*4.0/tempo->meter_bottom;
      two_measure_left=0;
    } else
      two_measure_left-=org_durations[tempo->duration];
    tempo=tempo->next;
    if (two_measure_left<0.001 && tempo!=&oc.root) {
      if (has_words) {
        LtfPutS(l,"\",\r\n\t\t\"");
        while (tempo1!=tempo) {
          if (tempo1->type!=OT_METER) {
            if (ptr=tempo1->word) {
              while (ch=*ptr) {
                if (ch==CH_SPACE)
                  *ptr=CH_SHIFT_SPACE;
                ptr++;
              }
              LtfPrintF(l,"%Q\\0",tempo1->word);
            } else
              LtfPrintF(l,"%c\\0",CH_SHIFT_SPACE);
          }
          tempo1=tempo1->next;
        }
      }
      LtfPutS(l,"\");\r\n");
      LtfPutS(l,"\tPlay(\"");
      two_measure_left=2*measure_len;
      tempo1=tempo;
      has_words=FALSE;
    }
  }
  if (has_words) {
    LtfPutS(l,"\",\r\n\t\t\"");
    while (tempo1!=tempo) {
      if (tempo1->type!=OT_METER) {
        if (ptr=tempo1->word) {
          while (ch=*ptr) {
            if (ch==CH_SPACE)
              *ptr=CH_SHIFT_SPACE;
            ptr++;
          }
          LtfPrintF(l,"%Q\\0",tempo1->word);
        } else
          LtfPrintF(l,"%c\\0",CH_SHIFT_SPACE);
      }
      tempo1=tempo1->next;
    }
  }
  LtfPutS(l,"\");\r\n"
"  }\r\n"
"  } catch\r\n"
"    Fs->catch_except=TRUE;\r\n"
"  Snd(0);\r\n"
"}\r\n"
"\r\n"
"Song;\r\n");
  LtfRecalc(l);
  if (full_filename) {
    StrCpy(l->filename.name,full_filename);
    Free(full_filename);
  } else
    SPrintF(l->filename.name,"%s/Temp.CPZ",dirname);
  LtfWrite(l,TRUE);
  full_filename=StrNew(l->filename.name);
  LtfDel(l);
  return full_filename;
}


U0 OrgSetOctave(I8 octave)
{
  I8 i;
  U1 buf[64];
  MenuEntry *tempse;
  for (i=1;i<=5;i++) {
    SPrintF(buf,"Snd/Octave%d",i);
    if (tempse=MenuEntryFind(Fs->cur_menu,buf)) {
      if (i==octave)
        tempse->checked=TRUE;
      else
        tempse->checked=FALSE;
    }
  }
  if (tempse=MenuEntryFind(Fs->cur_menu,"Snd/Reverb"))
    tempse->checked=org_play_mode==1;
}

U0 OrgSetWaveform(I8 waveform)
{
  MenuEntry *tempse;
  if (tempse=MenuEntryFind(Fs->cur_menu,"Snd/Square"))
    tempse->checked=waveform==WF_SQUARE;
  if (tempse=MenuEntryFind(Fs->cur_menu,"Snd/Sine"))
    tempse->checked=waveform==WF_SINE;
  if (tempse=MenuEntryFind(Fs->cur_menu,"Snd/Triangle"))
    tempse->checked=waveform==WF_TRIANGLE;
  if (tempse=MenuEntryFind(Fs->cur_menu,"Snd/Sawtooth"))
    tempse->checked=waveform==WF_SAWTOOTH;
  if (tempse=MenuEntryFind(Fs->cur_menu,"Snd/Noise"))
    tempse->checked=waveform==WF_NOISE;
}




<25>/* Graphics Not Rendered in HTML */
 




#define ORG_NUM_KEYS    20
class OrgKey
{
  U1 x,w,h,ascii;
};

#define ORG_W_W 16
#define ORG_W_H 36
#define ORG_B_W 8
#define ORG_B_H 20

OrgKey org_kbd[ORG_NUM_KEYS]=
{

{ 2*ORG_W_W-4,ORG_B_W,ORG_B_H,'e' },
{ 3*ORG_W_W-4,ORG_B_W,ORG_B_H,'r' },
{ 4*ORG_W_W-4,ORG_B_W,ORG_B_H,'t' },
{ 6*ORG_W_W-4,ORG_B_W,ORG_B_H,'u' },
{ 7*ORG_W_W-4,ORG_B_W,ORG_B_H,'i' },
{ 9*ORG_W_W-4,ORG_B_W,ORG_B_H,'p' },
{10*ORG_W_W-4,ORG_B_W,ORG_B_H,'[' },
{11*ORG_W_W-4,ORG_B_W,ORG_B_H,']' },

{ 0*ORG_W_W,ORG_W_W,ORG_W_H,'a' },
{ 1*ORG_W_W,ORG_W_W,ORG_W_H,'s' },
{ 2*ORG_W_W,ORG_W_W,ORG_W_H,'d' },
{ 3*ORG_W_W,ORG_W_W,ORG_W_H,'f' },
{ 4*ORG_W_W,ORG_W_W,ORG_W_H,'g' },
{ 5*ORG_W_W,ORG_W_W,ORG_W_H,'h' },
{ 6*ORG_W_W,ORG_W_W,ORG_W_H,'j' },
{ 7*ORG_W_W,ORG_W_W,ORG_W_H,'k' },
{ 8*ORG_W_W,ORG_W_W,ORG_W_H,'l' },
{ 9*ORG_W_W,ORG_W_W,ORG_W_H,';' },
{10*ORG_W_W,ORG_W_W,ORG_W_H,'\'' },
{11*ORG_W_W,ORG_W_W,ORG_W_H,CH_CR},

};


U0 OrgDownKey(I8 x,I8 y)
{
  I8 i;
  OrgKey *o;
  y-=FONT_HEIGHT*13;
  if (0<=y<ORG_W_H) {
    x-=16;
    for (i=0;i<ORG_NUM_KEYS;i++) {
      o=&org_kbd[i];
      if (o->x<=x<o->x+o->w && y<o->h) {
        kbd_evt_time=GetTimeStamp;
        Msg(MSG_KEY_DOWN,o->ascii,0);
        return;
      }
    }
  }
}

U0 OrgUpKey(I8 x,I8 y)
{
  I8 i;
  OrgKey *o;
  y-=FONT_HEIGHT*13;
  if (0<=y<ORG_W_H) {
    x-=16;
    for (i=0;i<ORG_NUM_KEYS;i++) {
      o=&org_kbd[i];
      if (o->x<=x<o->x+o->w && y<o->h) {
        kbd_evt_time=GetTimeStamp;
        Msg(MSG_KEY_UP,o->ascii,0);
        return;
      }
    }
  }
}

U0 OrgMenu(I8 org_octave,I8 org_waveform)
{
  LtfBottom;
  LtfClear;
  coutln "$$BD,WHITE$$$$FD,GREEN$$$$BU,WHITE$$$$FU,GREEN$$$$FM,GREEN$$$$BM,WHITE$$$$CM,0,12$$";
  PutGrElems(<25>);
  CrLf(5);

  DrawBase2;

  coutln "$$FG,LTGREEN$$SPACE$$FG$$\t\tRest";
  coutln "$$FG,LTGREEN$$BACKSPACE$$FG$$\tDeletes Last Note";
  coutln "$$FG,LTGREEN$$Left Mouse$$FG$$\tDrag note or shift word";
  coutln "$$FG,LTGREEN$$Right Mouse$$FG$$\tChange duration or set word";
  PutGrElems(<6>,<6>,"$$PI+LM+LA,\"\",\"Msg(MSG_KEY_DOWN,0,SCF_CTRL|SC_CURSOR_LEFT);\",%d$$");
  cout   "   ";
  if (oc.playing)
    PutGrElems(<9>,<9>,"$$PI+LM,\"\",\"x\",%d$$");
  else
    PutGrElems(<8>,<8>,"$$PI+LM,\"\",\"x\",%d$$");
  cout   "   ";
  if (oc.record_entry->checked) {
    oc.record_entry->checked=TRUE;
    PutGrElems(<10>,<10>,"$$PI+LM,\"\",\"z\",%d$$");
  } else
    PutGrElems(<11>,<11>,"$$PI+LM,\"\",\"z\",%d$$");
  cout   "   ";
  PutGrElems(<7>,<7>,"$$PI+LM+LA,\"\",\"Msg(MSG_KEY_DOWN,0,SCF_CTRL|SC_CURSOR_RIGHT);\",%d$$");
  PutS("$$CM+LX,0,6$$");
  OrgSetOctave(org_octave);
  OrgSetWaveform(org_waveform);
  WinSync(TRUE);
}


U0 OrgPushMode(I8 org_octave,I8 org_waveform)
{
  win_inhibit=WIF_ALL-WIF_BORDER;
  LBts(&Fs->display_flags,DISPLAYf_NO_DBL_CLICK);
  OrgMenu(org_octave,org_waveform);
}

U0 OrgPopMode()
{
  LBtr(&Fs->display_flags,DISPLAYf_NO_DBL_CLICK);
  win_inhibit=0;
  GrClear;
}

#define ORGF_CD         1
#define ORGF_INCOMPLETE 2

U0 MusicOrgan(U1 *dirname=NULL)
{
  BoolI1 is_note,was_playing;
  U8 p1,p2,ch,sc,msg_code=0,note_down_time,col;
  U8 note=0,octave=0,accent=0,org_waveform=WF_SQUARE,org_octave=3,timeout_val,timeout_val2;
  F8 f=0,last_f=0,org_duration=1.0;
  U1 *filename=NULL,*st,*st2;
  TaskStruct *play_task=Spawn(&OrgPlayTask,NULL,"Organ Play",Fs);
  OrgNote *tempo;
  U8 old_ltf_flags;
  F8 d;
  Ctrl *c=TempoNew;

  if (LtfCur) old_ltf_flags=LtfCur->flags;
  SettingsPush; //See SettingsPush
  WinBorder(OFF);
  WordStat(OFF);
  Preempt(OFF);

  MkDir("HOME/MusicOrgan");
  MusicSettingsReset;
  tempo_state.tempo=Round(TEMPO_RANGE*(music_tempo-0.5)/4.4);
  tempo_state.stacatto=Round(TEMPO_RANGE*(music_stacatto_factor-0.12)/0.88);

  if (LtfCur) LtfCur->flags|=LTFF_FORM;

  MemSet(&oc,0,sizeof(OrgCtrl));
  oc.screen_x=0;
  oc.root.next=oc.root.last=&oc.root;
  oc.clipboard.next=oc.clipboard.last=&oc.clipboard;
  oc.root.waveform=WF_SQUARE;
  oc.cur_note=&oc.root;
  oc.base2=GrAlias(gr_persistent_base,Fs);

  MenuPush(
  "File {"
  "  New(,'.');"
  "  ChangeDir(MSG_CMD,ORGF_CD);"
  "  Open(,CH_CTRLO);"
  "  SaveAs(,CH_CTRLA);"
  "  Abort(,CH_CTRLQ);"
  "  Exit(,CH_ESC);"
  "}"
  "Edit {"
  "  Cut(,0,0x3D300000253);"
  "  Copy(,0,0x4D200000452);"
  "  Paste(,0,0x2D200000252);"
  "  RightMenu(,CH_CR);"
  "  BackSpace(,CH_BACKSPACE);"
  "  DeleteNote(,,SC_DELETE);"
  "  ClearSong(,'.');"
  "  Left(,,SC_CURSOR_LEFT);"
  "  Right(,,SC_CURSOR_RIGHT);"
  "  GoBegin(,,0x4CB0000044B);"
  "  GoEnd(,,0x4CD0000044D);"
  "}"
  "Song {"
  "  Play(,'x');"
  "  Record(,'z');"
  "  Random(,',');"
  "  MarkIncomplete(MSG_CMD,ORGF_INCOMPLETE);"
  "}"
  "Snd {"
  "  Square(,'!');"
  "  Sine(,'@');"
  "  Triangle(,'#');"
  "  Sawtooth(,'$$');"
  "  Noise(,'%');"
  "  Reverb(,'c');"
  "  Octave1(,'1');"
  "  Octave2(,'2');"
  "  Octave3(,'3');"
  "  Octave4(,'4');"
  "  Octave5(,'5');"
  "}"
  "Help {"
  "  Help(,,SC_F1);"
      "}"
      );
  oc.incomplete_entry=MenuEntryFind(Fs->cur_menu,"Song/MarkIncomplete");
  oc.record_entry=MenuEntryFind(Fs->cur_menu,"Song/Record");

  WinMax;

  if (!dirname)
    dirname=StrNew("HOME/MusicOrgan");
  else
    dirname=StrNew(dirname);
  OrgPushMode(org_octave,org_waveform);
  col=0;
  Fs->draw_it=&DrawIt;

  try {
    do {
      was_playing=FALSE;
start:
      if (ipty-Fs->win_top<18)
        msg_code=GetMsg(&p1,&p2,1<<MSG_KEY_DOWN|1<<MSG_KEY_UP|1<<MSG_IP_L_DOWN|1<<MSG_IP_L_UP|1<<MSG_IP_R_UP|
            1<<MSG_IP_MOVE|1<<MSG_CMD);
      else
        msg_code=GetMsg(&p1,&p2,1<<MSG_KEY_DOWN|1<<MSG_KEY_UP|1<<MSG_IP_MOVE|1<<MSG_CMD);
got_msg:
      if (msg_code==MSG_KEY_DOWN && p1==CH_SPACE && !p2) {
      //The Window Manager sets the Ltf cur_entry to a button
      //and generates a SPACE when the Ltf Buttons are clicked.
      //This is so that kbd and mouse are the same for Ltf's.
      //We must now pass the SPACE onto the Ltf handler.
        PutKey(p1,p2);
        goto start;
      }
      if (msg_code!=MSG_IP_MOVE) {
        LtfBottom;
        if (was_playing || LtfCur->cur_entry->y>=Fs->win_height-2) {
          OrgMenu(org_octave,org_waveform);
          col=0;
        }
      }
      octave=org_octave;
      switch (msg_code) {
        case MSG_CMD:
          OrgPopMode;
          switch (p1) {
            case ORGF_CD:
              Free(filename);
              filename=NULL;
              st2=dirname;
              if (dirname=PopUpPickDir)
                Free(st2);
              else
                dirname=st2;
              break;
            case ORGF_INCOMPLETE:
              oc.incomplete_entry->checked=!oc.incomplete_entry->checked;
              break;
          }
          OrgPushMode(org_octave,org_waveform);
          is_note=FALSE;
          col=0;
          break;
        case MSG_KEY_DOWN:
          ch=p1;
          sc=p2;
          if (ch>='0' && ch<='9') {
            org_octave=ch-'0';
            OrgMenu(org_octave,org_waveform);
            col=0;
          } else {
            is_note=TRUE;
            switch (ch) {
              case 0:
                switch (sc.u1[0]) {
                  case SC_CURSOR_LEFT:
                    if (sc&SCF_CTRL) {
                      while (oc.cur_note->last!=&oc.root) {
                        oc.cur_note=oc.cur_note->last;
                        if (oc.cur_note!=&oc.root)
                          LBEqu(&oc.cur_note->flags,Of_SELECTED,sc&SCF_SHIFT);
                      }
                    } else {
                      if (oc.cur_note->last!=&oc.root) {
                        oc.cur_note=oc.cur_note->last;
                        if (oc.cur_note!=&oc.root)
                          LBEqu(&oc.cur_note->flags,Of_SELECTED,sc&SCF_SHIFT);
                      }
                    }
                    break;
                  case SC_CURSOR_RIGHT:
                    if (sc&SCF_CTRL) {
                      while (oc.cur_note!=&oc.root) {
                        if (oc.cur_note!=&oc.root)
                          LBEqu(&oc.cur_note->flags,Of_SELECTED,sc&SCF_SHIFT);
                        oc.cur_note=oc.cur_note->next;
                      }
                    } else {
                      if (oc.cur_note!=&oc.root) {
                        if (oc.cur_note!=&oc.root)
                          LBEqu(&oc.cur_note->flags,Of_SELECTED,sc&SCF_SHIFT);
                        oc.cur_note=oc.cur_note->next;
                      }
                    }
                    break;
                  case SC_DELETE:
                    if (sc&SCF_SHIFT)
                      OrgCutToClipboard;
                    else {
                      tempo=oc.cur_note;
                      oc.cur_note=tempo->next;
                      if (tempo!=&oc.root) {
                        RemQue(tempo);
                        OrgNoteDel(tempo);
                      }
                    }
                    break;
                  case SC_INSERT:
                    if (sc&SCF_SHIFT)
                      OrgPasteClipboard;
                    else if (sc&SCF_CTRL)
                      OrgCopyToClipboard;
                    break;
                  case SC_F1:
                    OrgPopMode;
                    PopUp("Ed(\"::/LT/Apps/MusicOrgan/Help.TXZ\");",Fs);
                    OrgPushMode(org_octave,org_waveform);
                    col=0;
                    is_note=FALSE;
                    break;
                }
                is_note=FALSE;
                break;
              case 'a': note=7; octave--; break;
              case 's': note=8; octave--; break;
              case 'e': note=9; octave--; break;
              case 'd': note=10; octave--; break;
              case 'r': note=11; octave--; break;
              case 'f': note=0; break;
              case 't': note=1; break;
              case 'g': note=2; break;
              case 'h': note=3; break;
              case 'u': note=4; break;
              case 'j': note=5; break;
              case 'i': note=6; break;
              case 'k': note=7; break;
              case 'l': note=8; break;
              case 'p': note=9; break;
              case ';': note=10; break;
              case '[': note=11; break;
              case '\'': note=0; octave++; break;
              case ']': note=1; octave++; break;
              case CH_SPACE:
                note=0;
                octave=0;
                break;
              case ',':
                Free(filename);
                filename=NULL;
                OrgPopMode;
                st2=CallExtStr("MakeSong");
                OrgLoadSongStr(st2,&org_octave,&org_duration);
                Free(st2);
                OrgPushMode(org_octave,org_waveform);
                is_note=FALSE;
                col=0;
                break;
              case CH_CTRLO:
                AcctOneTimePopUp(ARf_MUSIC_ORGAN_JUKEBOX,
                "Select a song and preview it.\r\n"
                "Press $$FG,GREEN$$CTRL-Q$$FG$$ to load it into MusicOrgan.\r\n\r\n"
                ST_WARN_ST " Graphics and other embelishments\r\n"
                "will be lost because MusicOrgan cannot\r\n"
                    "parse C/C++ programs completely.\r\n");
                Free(filename);
                filename=NULL;
                JukeBox(dirname,&filename);
                if (filename) {
                  oc.screen_x=0;
                  org_duration=1.0;
                  org_octave=3;
                  OrgSongDel(&oc.root);
                  oc.cur_note=&oc.root;
                  OrgLoadSong(filename,&org_octave,&org_duration);
                  oc.record_entry->checked=FALSE;
                  oc.cur_note=oc.root.next;
                }
                OrgPushMode(org_octave,org_waveform);
                is_note=FALSE;
                col=0;
                break;
              case CH_CTRLA:
                OrgPopMode;
                filename=OrgSaveSong(dirname,filename);
                OrgPushMode(org_octave,org_waveform);
                is_note=FALSE;
                break;
              case '.':
                OrgMenu(org_octave,org_waveform);
                col=0;
                Free(filename);
                filename=NULL;
                org_duration=1.0;
                org_octave=3;
                OrgSongDel(&oc.root);
                oc.cur_note=&oc.root;
                oc.screen_x=0;
                is_note=FALSE;
                break;
              case CH_CR:
                if (oc.cur_note!=&oc.root)
                  OrgRightClick(oc.cur_note->x,oc.cur_note->y);
                is_note=FALSE;
                break;
              case 'x':
                if (was_playing) {
                  is_note=FALSE;
                  break;
                }
                col=0;
                oc.playing=TRUE;
                OrgMenu(org_octave,org_waveform);
                tempo=oc.cur_note;
                while (tempo!=&oc.root) {
                  if (tempo->type!=OT_METER) {
                    timeout_val=GetTimeStamp;
                    if (ipty-Fs->win_top<18)
                      msg_code=ScanMsg(&p1,&p2,1<<MSG_KEY_DOWN|1<<MSG_IP_L_DOWN|1<<MSG_IP_R_UP|1<<MSG_CMD);
                    else
                      msg_code=ScanMsg(&p1,&p2,1<<MSG_KEY_DOWN|1<<MSG_IP_L_DOWN|1<<MSG_CMD);
                    if (msg_code) {
                      f=0;
                      org_play_f=0;
                      is_note=FALSE;
                      oc.playing=FALSE;
                      was_playing=TRUE;
                      if (ipty-Fs->win_top>=18 && msg_code==MSG_IP_L_DOWN)
                        goto start;
                      else
                        goto got_msg;
                    }
                    oc.cur_note=tempo;
                    oc.screen_x+=tempo->x-0.33*GR_WIDTH;
                    if (tempo->word)
                      if (StrLen(tempo->word)!=1 ||
                          (*tempo->word!=CH_SPACE && *tempo->word!=CH_SHIFT_SPACE))
                        PutS(tempo->word);
                    note=tempo->note;
                    octave=tempo->octave;
                    accent=tempo->accent;
                    org_waveform=tempo->waveform;
                    if (note || octave)
                      f=Note2Freq(note,octave);
                    else
                      f=-1; //rest
                    if (f>0)
                      org_play_f=f;
                    else
                      org_play_f=0;


                    music_tempo=4.4*tempo_state.tempo/TEMPO_RANGE+0.5;
                    music_stacatto_factor=0.88*tempo_state.stacatto/TEMPO_RANGE+0.12;
                    music_waveform=org_waveform;
                    d=time_stamp_freq*org_durations[tempo->duration]/music_tempo;
                    timeout_val+=d*music_stacatto_factor;
                    timeout_val2=timeout_val+
                        d*(1.0-music_stacatto_factor);
                    SleepUntil(timeout_val);
                    org_play_f=0;
                    SleepUntil(timeout_val2);
                  }
                  tempo=tempo->next;
                }
                oc.cur_note=&oc.root;
                oc.screen_x+=oc.cur_note->x-GR_WIDTH/2;
                oc.playing=FALSE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                f=0;
                org_play_f=0;
                is_note=FALSE;
                break;
              case CH_BACKSPACE:
                tempo=oc.cur_note->last;
                if (tempo!=&oc.root) {
                  RemQue(tempo);
                  OrgNoteDel(tempo);
                }
                is_note=FALSE;
                if (col) {
                  PutChar(CH_BACKSPACE);
                  col--;
                }
                break;
              case 'c':
                org_play_mode++;
                if (org_play_mode>=ORGPM_NUM_PLAY_MODES) org_play_mode=0;
                is_note=FALSE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                break;
              case 'z':
                if (oc.record_entry->checked) {
                  oc.record_entry->checked=FALSE;
                  st2=OrgCvtSong;
                  coutln st2;
                  Free(st2);
                } else {
                  oc.record_entry->checked=TRUE;
                  org_duration=1.0;
                  org_octave=3;
                  oc.screen_x=0;
                }
                is_note=FALSE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                break;
              case CH_ESC:
                OrgPopMode;
                filename=OrgSaveSong(dirname,filename);
                OrgPushMode(org_octave,org_waveform);
                break;
              case '!':
                music_waveform=org_waveform=WF_SQUARE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                is_note=FALSE;
                break;
              case '@':
                music_waveform=org_waveform=WF_SINE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                is_note=FALSE;
                break;
              case '#':
                music_waveform=org_waveform=WF_TRIANGLE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                is_note=FALSE;
                break;
              case '$$':
                music_waveform=org_waveform=WF_SAWTOOTH;
                OrgMenu(org_octave,org_waveform);
                col=0;
                is_note=FALSE;
                break;
              case '%':
                music_waveform=org_waveform=WF_NOISE;
                OrgMenu(org_octave,org_waveform);
                col=0;
                is_note=FALSE;
                break;
              default:
                is_note=FALSE;
            }
            if (is_note) {
              if (note || octave)
                f=Note2Freq(note,octave);
              else
                f=-1; //rest
            }
          }
          break;
        case MSG_KEY_UP:
          f=0;
          break;
        case MSG_IP_MOVE:
          if (p2>18*FONT_HEIGHT)
            win_inhibit=0;
          else
            win_inhibit=WIF_ALL-WIF_BORDER;
          break;
        case MSG_IP_L_DOWN:
          OrgDownKey(p1,p2);
          OrgLeftClick(p1,p2);
          break;
        case MSG_IP_L_UP:
          OrgUpKey(p1,p2);
          break;
        default:
          OrgRightClick(p1,p2);
      }
      if (f!=last_f) {
        if (oc.record_entry->checked) {
          if (last_f) {
            music_tempo=4.4*tempo_state.tempo/TEMPO_RANGE+0.5;
            music_stacatto_factor=0.88*tempo_state.stacatto/TEMPO_RANGE+0.12;

            tempo->duration=OrgCvtDuration(music_tempo*(kbd_evt_time-note_down_time)/time_stamp_freq);
            OrgSetWidth(tempo);
            InsQue(tempo,oc.cur_note->last);
          }
          if (f) {
            note_down_time=kbd_evt_time;
            tempo=CAlloc(sizeof(OrgNote));
            tempo->type=OT_NOTE;
            tempo->note=note;
            tempo->octave=octave;
            tempo->accent=0;
            tempo->waveform=org_waveform;
          }
        }
        if (f>0) {
          st=ListEntryPoint(note,org_note_list);
          org_play_f=f;
        } else {
          if (f<0)
            st="R";
          else
            st="";
          org_play_f=0;
        }
        last_f=f;
        cout st;
        col+=StrLen(st);
        if (col>=Fs->win_width-1) {
          CrLf;
          col=0;
        }
      }
    } while (ch!=CH_ESC && ch!=CH_CTRLQ);
  } catch
    Fs->catch_except=TRUE;
  OrgPopMode;
  OrgSongDel(&oc.root);
  OrgSongDel(&oc.clipboard);
  TempoDel(c);
  Kill(play_task);
  GrClear;
  GrDel(oc.base2);
  SettingsPop;
  if (LtfCur) LtfCur->flags=LtfCur->flags&~LTFF_FORM|old_ltf_flags&LTFF_FORM;
  Free(dirname);
  coutln "$$CL$$";
  MenuPop;
}