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;
	}
	QueueInit(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;
			QueueRemove(tmpn);
			tmpn->flags &= ~PSMF_SEL;
			QueueInsert(tmpn, psm.clip.last);
		}
		tmpn = tmpn1;
	}
}

U0 PsmPasteClip()
{
	PsmNote *tmpn, *tmpn1;

	tmpn = psm.clip.next;
	while (tmpn != &psm.clip)
	{
		tmpn1 = PsmNoteCopy(tmpn);
		QueueInsert(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);
			QueueInsert(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);
				QueueInsert(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);
		QueueInsert(tmpn, psm.cur_note->last);
	}
}

U0 PsmLoadSong(U8 *filename, I64 *psm_octave, F64 *psm_duration)
{
	U8			*st;
	PsmNote		*tmpn;
	CCompCtrl	*cc = CompCtrlNew(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 (!StrCompare(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 (!StrCompare(cc->cur_str, "music") && Lex(cc) == '.' && Lex(cc) == TK_IDENT)
			{
				if (!StrCompare(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 (!StrCompare(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);
					}
				}
			}
	}
	CompCtrlDel(cc);
}

U8 *PsmConvertSong()
{
	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[*ListSub(note, psm_note_list) - '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 = ListSub(tmpn->duration, psm_duration_list);
				*dst++ = src[0];
				if (src[1])
					*dst++ = src[1];
				last_duration = tmpn->duration;
			}
			if (tmpn->ona)
			{
				src = ListSub(note, psm_note_list);
				*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)
	{
		StrCopy(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(PsmConvertSong); //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"
				"\tFs->task_end_cb=&SoundTaskEndCB;\n"
				"\tMusicSettingsReset;\n"
				"\tmusic.tempo=%6.3f;\n"
				"\tmusic.stacatto_factor=%6.3f;\n"
				"\ttry {\n"
				"\t\twhile (!KeyScan) {\n"
				"\t\t\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)
								ptr++;
							DocPrint(doc, "%Q\\0", tmpn1->word);
						}
						else
							DocPrint(doc, " \\0");
					}
					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)
						ptr++;
					
					DocPrint(doc, "%Q\\0", tmpn1->word);
				}
				else
					DocPrint(doc, " \\0");
			}
			tmpn1 = tmpn1->next;
		}
	}
	DocPrint(doc,
				"\");\n"
				"\t\t}\n"
				"\t} catch\n"
				"\t\tPutExcept;\n"
				"\tSound;\n"
				"}\n"
				"\n"
				"Song;\n");
	DocRecalc(doc);
	if (full_filename)
		Free(full_filename);
	else
		StrPrint(doc->filename.name, "%s/Tmp.CC", dirname);
	DocWrite(doc, TRUE);
	full_filename = StrNew(doc->filename.name);
	DocDel(doc);

	return full_filename;
}
