U0 PsmNoteDel(PsmNote *tmpn)
{
  Free(tmpn->word);
  Free(tmpn);
}

PsmNote *PsmNoteCopy(PsmNote *tmpn)
{
  PsmNote *tmpn1=MAllocIdent(tmpn);
  if (tmpn->word)
    tmpn1->word=StrNew(tmpn->word);
  else
    tmpn1->word=NULL;
  return tmpn1;
}

U0 PsmSongDel(PsmNote *head)
{
  PsmNote *tmpn,*tmpn1;
  tmpn=head->next;
  while (tmpn!=head) {
    tmpn1=tmpn->next;
    PsmNoteDel(tmpn);
    tmpn=tmpn1;
  }
  QueInit(head);
}

U0 PsmCutToClip()
{
  PsmNote *tmpn,*tmpn1;
  PsmSongDel(&psm.clip);
  tmpn=psm.head.next;
  while (tmpn!=&psm.head) {
    tmpn1=tmpn->next;
    if (tmpn->flags&PSMF_SEL) {
      if (psm.cur_note==tmpn)
	psm.cur_note=tmpn->next;
      QueRem(tmpn);
      tmpn->flags&=~PSMF_SEL;
      QueIns(tmpn,psm.clip.last);
    }
    tmpn=tmpn1;
  }
}

U0 PsmPasteClip()
{
  PsmNote *tmpn,*tmpn1;
  tmpn=psm.clip.next;
  while (tmpn!=&psm.clip) {
    tmpn1=PsmNoteCopy(tmpn);
    QueIns(tmpn1,psm.cur_note->last);
    tmpn=tmpn->next;
  }
}

U0 PsmCopyToClip()
{
  PsmNote *tmpn,*tmpn1;
  PsmSongDel(&psm.clip);
  tmpn=psm.head.next;
  while (tmpn!=&psm.head) {
    if (tmpn->flags&PSMF_SEL) {
      tmpn->flags&=~PSMF_SEL;
      tmpn1=PsmNoteCopy(tmpn);
      QueIns(tmpn1,psm.clip.last);
    }
    tmpn=tmpn->next;
  }
}

PsmNote *PsmFindNote(I64 x,I64)
{
  PsmNote *tmpn=psm.head.next;
  PsmRecalcNoteXY;
  x+=PSM_NOTE_SPACING/2;
  while (x>tmpn->next->x && tmpn!=&psm.head)
    tmpn=tmpn->next;
  return tmpn;
}

U8 *PsmMusicSetOctave(U8 *st,I64 *psm_octave)
{
  while ('0'<=*st<='9')
    *psm_octave=*st++ -'0';
  return st;
}

U8 *PsmMusicSetNoteLen(U8 *st,F64 *psm_duration)
{
  Bool cont=TRUE;
  do {
    switch (*st++) {
      case 'w': *psm_duration=4.0;  break;
      case 'h': *psm_duration=2.0;  break;
      case 'q': *psm_duration=1.0;  break;
      case 'e': *psm_duration=0.5;  break;
      case 's': *psm_duration=0.25; break;
      case 't': *psm_duration=2.0* *psm_duration/3.0;	break;
      case '.': *psm_duration=1.5* *psm_duration;	break;
      default:
	st--;
	cont=FALSE;
    }
  } while (cont);
  return st;
}

U0 PsmLoadSongStr(U8 *st,I64 *psm_octave,F64 *psm_duration)
{
  PsmNote *tmpn,*tmpn1;
  I64 note,i=0;
  while (*st) {
    tmpn=CAlloc(sizeof(PsmNote));
    while (*st && !('A'<=*st<='G') && *st!='R') {
      if (*st=='M') {
	tmpn1=CAlloc(sizeof(PsmNote));
	tmpn1->type=PSMT_METER;
	st++;
	if ('1'<=*st<='9')
	  tmpn1->meter_top=*st++-'0';
	else
	  tmpn1->meter_top=4;
	if (*st=='/')
	  st++;
	if ('1'<=*st<='9')
	  tmpn1->meter_bottom=*st++-'0';
	else
	  tmpn1->meter_bottom=4;
	PsmSetWidth(tmpn1);
	QueIns(tmpn1,psm.head.last);
      }
      while (*st=='(') {
	Bts(&tmpn->flags,PSMf_TIE);
	st++;
      }
      st=PsmMusicSetOctave(st,psm_octave);
      st=PsmMusicSetNoteLen(st,psm_duration);
    }
    if (!*st) {
      PsmNoteDel(tmpn);
      break;
    }
    note=*st++-'A';
    if (note<7) {
      note=music.note_map[note];
      if (*st=='b') {
	Bts(&tmpn->flags,PSMf_FLAT);
	note--;
	st++;
	if (note<0) //Ab
	  note=11;
	else if (note==2) //Cb
	  *psm_octave-=1;
      } else if (*st=='#') {
	Bts(&tmpn->flags,PSMf_SHARP);
	note++;
	st++;
	if (note>11) //G#
	  note=0;
	else if (note==3) //B#
	  *psm_octave+=1;
      }
      tmpn->ona=Note2Ona(note,*psm_octave);
    } else
      tmpn->ona=0;
    if (*psm_duration<=2*.25/3)
      i=0;
    else if (*psm_duration<=.25)
      i=1;
    else if (*psm_duration<=2*.5/3)
      i=2;
    else if (*psm_duration<=.5)
      i=3;
    else if (*psm_duration<=2.0/3)
      i=4;
    else if (*psm_duration<=.5*1.5)
      i=5;
    else if (*psm_duration<=1.0)
      i=6;
    else if (*psm_duration<=1.5)
      i=7;
    else if (*psm_duration<=2.0)
      i=8;
    else if (*psm_duration<=3.0)
      i=9;
    else if (*psm_duration<=4.0)
      i=10;
    else
      i=11;
    tmpn->duration=i;
    tmpn->type=PSMT_NOTE;
    PsmSetWidth(tmpn);
    QueIns(tmpn,psm.cur_note->last);
  }
}

U0 PsmLoadSong(U8 *filename,I64 *psm_octave,F64 *psm_duration)
{
  U8 *st;
  PsmNote *tmpn;
  CCmpCtrl *cc=CmpCtrlNew(MStrPrint("#include \"%s\"",filename));
  if (FileOcc("incomplete",filename,""))
    psm.incomplete_entry->checked=TRUE;
  else
    psm.incomplete_entry->checked=FALSE;
  while (Lex(cc)) {
    if (cc->token==TK_IDENT)
      if (!StrCmp(cc->cur_str,"Play")) {
	if (Lex(cc)=='(')
	  if (Lex(cc)==TK_STR) {
	    tmpn=psm.head.last;
	    st=LexExtStr(cc);
	    PsmLoadSongStr(st,psm_octave,psm_duration);
	    if (cc->token==',') {
	      if (Lex(cc)==TK_STR) {
		st=LexExtStr(cc);
		do {
		  do tmpn=tmpn->next;
		  while (tmpn!=&psm.head && tmpn->type==PSMT_METER);
		  if (tmpn!=&psm.head)
		    tmpn->word=StrNew(st);
		  st+=StrLen(st)+1;
		} while (*st);
	      }
	    }
	  }
      } else if (!StrCmp(cc->cur_str,"music") &&
	    Lex(cc)=='.' && Lex(cc)==TK_IDENT) {
	if (!StrCmp(cc->cur_str,"tempo")) {
	  if (Lex(cc)=='=' && Lex(cc)==TK_F64) {
	    music.tempo=cc->cur_f64-0.0005;
	    tempo_state.tempo=Round(TEMPO_RANGE*(music.tempo-0.5)/4.4);
	  }
	} else if (!StrCmp(cc->cur_str,"stacatto_factor")) {
	  if (Lex(cc)=='=' && Lex(cc)==TK_F64) {
	    music.stacatto_factor=cc->cur_f64-0.0005;
	    tempo_state.stacatto=
		  Round(TEMPO_RANGE*(music.stacatto_factor-0.12)/0.88);
	  }
	}
      }
  }
  CmpCtrlDel(cc);
}

U8 *PsmCvtSong()
{
  PsmNote *tmpn;
  U8 *st,*src,*dst;
  I64 i,ona,note,octave,last_octave,last_duration;

  i=0;
  tmpn=psm.head.next;
  last_octave=I64_MIN;
  last_duration=-1;
  while (tmpn!=&psm.head) {
    dst=&tmpn->ascii;
    if (tmpn->type==PSMT_METER) {
      *dst++='M';
      *dst++=tmpn->meter_top+'0';
      *dst++='/';
      *dst++=tmpn->meter_bottom+'0';
    } else {
      if (tmpn->ona) {
	ona=tmpn->ona;
	if (Bt(&tmpn->flags,PSMf_SHARP))
	  ona--;
	if (Bt(&tmpn->flags,PSMf_FLAT))
	  ona++;
	octave=Ona2Octave(ona);
	note  =Ona2Note  (ona);
	note=music.note_map[*LstSub(note,psm_note_lst)-'A'];
      }
      if (Bt(&tmpn->flags,PSMf_TIE))
	*dst++='(';
      if (octave!=last_octave && tmpn->ona) {
	*dst++=octave+'0';
	last_octave=octave;
      }
      if (tmpn->duration!=last_duration) {
	src=LstSub(tmpn->duration,psm_duration_lst);
	*dst++=src[0];
	if (src[1])
	  *dst++=src[1];
	last_duration=tmpn->duration;
      }
      if (tmpn->ona) {
	src=LstSub(note,psm_note_lst);
	*dst++=src[0];
	if (src[1])
	  *dst++=src[1];
	else if (Bt(&tmpn->flags,PSMf_FLAT))
	  *dst++='b';
	else if (Bt(&tmpn->flags,PSMf_SHARP))
	  *dst++='#';
      } else
	*dst++='R';
    }
    *dst++=0;
    i+=StrLen(tmpn->ascii);
    tmpn=tmpn->next;
  }

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

U8 *PsmSaveSong(U8 *dirname,U8 *full_filename)
{
  CDoc *doc=DocNew(full_filename);
  Bool has_words;
  PsmNote *tmpn,*tmpn1;
  F64 measure_len=4,two_measure_left=2*measure_len;
  I64 ch;
  U8 *ptr;

  Free(PsmCvtSong); //set tmpn->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;
  tmpn=psm.head.next;
  while (tmpn!=&psm.head) {
    if (PsmHasWords(tmpn->word)) has_words=TRUE;
    tmpn=tmpn->next;
  }
  if (psm.incomplete_entry->checked)
    DocPrint(doc,"//0 incomplete\n");
  else if (has_words)
    DocPrint(doc,"//0 has words\n");
  else
    DocPrint(doc,"//0 no nothing\n");

  DocPrint(doc,
	"U0 Song()\n"
	"{\n"
	"Fs->task_end_cb=&SndTaskEndCB;\n"
	"MusicSettingsRst;\n"
	"music.tempo=%6.3f;\n"
	"music.stacatto_factor=%6.3f;\n"
	"try {\n"
	"while (!ScanKey) {\n"
	"\tPlay(\"",music.tempo+0.0005,music.stacatto_factor+0.0005);

  tmpn=psm.head.next;
  tmpn1=tmpn;
  has_words=FALSE;
  while (tmpn!=&psm.head) {
    DocPrint(doc,"%s",tmpn->ascii);
    if (PsmHasWords(tmpn->word)) has_words=TRUE;
    if (tmpn->type==PSMT_METER) {
      measure_len=tmpn->meter_top*4.0/tmpn->meter_bottom;
      two_measure_left=0;
    } else
      two_measure_left-=psm_durations[tmpn->duration];
    tmpn=tmpn->next;
    if (two_measure_left<0.001 && tmpn!=&psm.head) {
      if (has_words) {
	DocPrint(doc,"\",\n\t\t\"");
	while (tmpn1!=tmpn) {
	  if (tmpn1->type!=PSMT_METER) {
	    if (ptr=tmpn1->word) {
	      while (ch=*ptr) {
		if (ch==CH_SPACE)
		  *ptr=CH_SHIFT_SPACE;
		ptr++;
	      }
	      DocPrint(doc,"%Q\\0",tmpn1->word);
	    } else
	      DocPrint(doc,"%c\\0",CH_SHIFT_SPACE);
	  }
	  tmpn1=tmpn1->next;
	}
      }
      DocPrint(doc,"\");\n"
	    "\tPlay(\"");
      two_measure_left=2*measure_len;
      tmpn1=tmpn;
      has_words=FALSE;
    }
  }
  if (has_words) {
    DocPrint(doc,"\",\n\t\t\"");
    while (tmpn1!=tmpn) {
      if (tmpn1->type!=PSMT_METER) {
	if (ptr=tmpn1->word) {
	  while (ch=*ptr) {
	    if (ch==CH_SPACE)
	      *ptr=CH_SHIFT_SPACE;
	    ptr++;
	  }
	  DocPrint(doc,"%Q\\0",tmpn1->word);
	} else
	  DocPrint(doc,"%c\\0",CH_SHIFT_SPACE);
      }
      tmpn1=tmpn1->next;
    }
  }
  DocPrint(doc,"\");\n"
	"}\n"
	"} catch\n"
	"PutExcept;\n"
	"Snd;\n"
	"}\n"
	"\n"
	"Song;\n");
  DocRecalc(doc);
  if (full_filename)
    Free(full_filename);
  else
    StrPrint(doc->filename.name,"%s/Tmp.HC.Z",dirname);
  DocWrite(doc,TRUE);
  full_filename=StrNew(doc->filename.name);
  DocDel(doc);
  return full_filename;
}
