1 /* midi_camd.c -- MIDI module for Amiga using CAMD
2  * Based on PlayMF by Dan Baker, Christian Buchner
3  *
4  * Copyright (C) 2017 Szilard Biro <col.lawrence@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  * See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 
23 #include "quakedef.h"
24 #include "bgmusic.h"
25 #include "midi_drv.h"
26 
27 #include <proto/exec.h>
28 #include <proto/dos.h>
29 #include <proto/realtime.h>
30 #include <dos/dostags.h>
31 #include <libraries/realtime.h>
32 #include <libraries/iffparse.h> /* MAKE_ID */
33 #include <clib/alib_protos.h>
34 
35 #ifdef __MORPHOS__
36 #define USE_INLINE_STDARG
37 #endif
38 #include <proto/camd.h>
39 #include <midi/camd.h>
40 #include <midi/mididefs.h>
41 
42 #include <SDI/SDI_compiler.h> /* IPTR */
43 
44 
45 /* prototypes of functions exported to BGM: */
46 static void *MIDI_Play (const char *filename);
47 static void MIDI_Update (void **handle);
48 static void MIDI_Rewind (void **handle);
49 static void MIDI_Stop (void **handle);
50 static void MIDI_Pause (void **handle);
51 static void MIDI_Resume (void **handle);
52 static void MIDI_SetVolume (void **handle, float value);
53 
54 static midi_driver_t midi_amiga_camd =
55 {
56 	false, /* init success */
57 	"CAMD MIDI for Amiga",
58 	MIDI_Init,
59 	MIDI_Cleanup,
60 	MIDI_Play,
61 	MIDI_Update,
62 	MIDI_Rewind,
63 	MIDI_Stop,
64 	MIDI_Pause,
65 	MIDI_Resume,
66 	MIDI_SetVolume,
67 	NULL
68 };
69 
70 #define ID_MTHD MAKE_ID('M','T','h','d')
71 #define ID_MTRK MAKE_ID('M','T','r','k')
72 
73 struct SMFHeader
74 {
75 	LONG  ChunkID;  /* 4 ASCII characters */
76 	LONG  VarLeng;
77 	WORD  Format;
78 	UWORD Ntrks;
79 	WORD  Division;
80 };
81 typedef int _SMFHeader_check[(offsetof(struct SMFHeader,Division) == 12)*2 - 1]; /* (sizeof(struct SMFHeader) == 14) */
82 
83 typedef enum
84 {
85 	Event_ignore,
86 	Event_playable,
87 	Event_sysex,
88 	Event_tempo,
89 	Event_trackend
90 } Eventtype;
91 
92 struct DecTrack
93 {
94 	UWORD tracknum;  /* number of this track */
95 	BOOL  trackend;  /* end of track flag */
96 	ULONG absdelta;  /* 32-bit delta */
97 	ULONG nexclock;  /* storage */
98 	UBYTE status;   /* status from file */
99 	UBYTE rstatus;  /* running status from track */
100 	UBYTE comsize;  /* size of current command */
101 	UBYTE d1;     /* data byte 1 */
102 	UBYTE d2;     /* data byte 2 */
103 	UBYTE *endmarker;
104 	Eventtype eventtype;
105 	UBYTE *eventptr;
106 };
107 
108 struct SysEx
109 {
110 	struct MinNode se_node;	/* node for linking */
111 	//ULONG se_size;			/* sysex size */
112 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
113 	UBYTE se_data[];		/* sysex data */
114 #else
115 	UBYTE se_data[0];		/* sysex data */
116 #endif
117 };
118 
119 #define MAXTRAX 40
120 #define MIDIBUFSIZE 256
121 
122 struct Global
123 {
124 	UBYTE trackct;
125 	UBYTE donecount;
126 
127 	UBYTE masterswitch;
128 
129 	ULONG division;
130 	ULONG tempo;
131 
132 	ULONG lastclock;
133 	//ULONG abstimeHI, abstimeLO;
134 	//uint64_t abstime;
135 	double abstime;
136 
137 	//struct DecTrack *pDTrack[MAXTRAX];
138 	struct DecTrack dtrack[MAXTRAX];
139 	UBYTE *ptrack[MAXTRAX];
140 	UBYTE *ptrackstart[MAXTRAX];
141 	UBYTE *ptrackend[MAXTRAX];
142 
143 	ULONG fillclock[2];
144 	ULONG fillstate[2];
145 	ULONG buftempo[2];
146 
147 	UBYTE lastRSchan;
148 
149 	/* These buffers hold the notes translated */
150 	/* from the midifile file for playback */
151 	UBYTE pfillbuf[2][MIDIBUFSIZE];
152 
153 	struct MinList SysExList[2];
154 };
155 
156 static UBYTE CommandSize[7]=
157 {
158 	2, /* 80-8f */
159 	2, /* 90-9f */
160 	2, /* a0-af */
161 	2, /* b0-bf */
162 	1, /* c0-cf */
163 	1, /* d0-df */
164 	2, /* e0-ef */
165 };
166 
167 static UBYTE CommonSize[8]=
168 {
169 	0, /* f0 */
170 	1, /* f1 */
171 	2, /* f2 */
172 	1, /* f3 */
173 	0, /* f4 */
174 	0, /* f5 */
175 	0, /* f6 */
176 	0, /* f7 */
177 };
178 
179 static UBYTE ProgramReset[] =
180 {
181 	MS_Prog |  0, 0,
182 	MS_Prog |  1, 0,
183 	MS_Prog |  2, 0,
184 	MS_Prog |  3, 0,
185 	MS_Prog |  4, 0,
186 	MS_Prog |  5, 0,
187 	MS_Prog |  6, 0,
188 	MS_Prog |  7, 0,
189 	MS_Prog |  8, 0,
190 	MS_Prog |  9, 0,
191 	MS_Prog | 10, 0,
192 	MS_Prog | 11, 0,
193 	MS_Prog | 12, 0,
194 	MS_Prog | 13, 0,
195 	MS_Prog | 14, 0,
196 	MS_Prog | 15, 0
197 };
198 
199 static UBYTE ResetAllControllers[] =
200 {
201 	MS_Ctrl | 0,  MM_ResetCtrl, 0,
202 	MS_Ctrl | 1,  MM_ResetCtrl, 0,
203 	MS_Ctrl | 2,  MM_ResetCtrl, 0,
204 	MS_Ctrl | 3,  MM_ResetCtrl, 0,
205 	MS_Ctrl | 4,  MM_ResetCtrl, 0,
206 	MS_Ctrl | 5,  MM_ResetCtrl, 0,
207 	MS_Ctrl | 6,  MM_ResetCtrl, 0,
208 	MS_Ctrl | 7,  MM_ResetCtrl, 0,
209 	MS_Ctrl | 8,  MM_ResetCtrl, 0,
210 	MS_Ctrl | 9,  MM_ResetCtrl, 0,
211 	MS_Ctrl | 10, MM_ResetCtrl, 0,
212 	MS_Ctrl | 11, MM_ResetCtrl, 0,
213 	MS_Ctrl | 12, MM_ResetCtrl, 0,
214 	MS_Ctrl | 13, MM_ResetCtrl, 0,
215 	MS_Ctrl | 14, MM_ResetCtrl, 0,
216 	MS_Ctrl | 15, MM_ResetCtrl, 0
217 };
218 
219 static UBYTE AllNotesOff[] =
220 {
221 	MS_Ctrl | 0,  MM_AllOff, 0,
222 	MS_Ctrl | 1,  MM_AllOff, 0,
223 	MS_Ctrl | 2,  MM_AllOff, 0,
224 	MS_Ctrl | 3,  MM_AllOff, 0,
225 	MS_Ctrl | 4,  MM_AllOff, 0,
226 	MS_Ctrl | 5,  MM_AllOff, 0,
227 	MS_Ctrl | 6,  MM_AllOff, 0,
228 	MS_Ctrl | 7,  MM_AllOff, 0,
229 	MS_Ctrl | 8,  MM_AllOff, 0,
230 	MS_Ctrl | 9,  MM_AllOff, 0,
231 	MS_Ctrl | 10, MM_AllOff, 0,
232 	MS_Ctrl | 11, MM_AllOff, 0,
233 	MS_Ctrl | 12, MM_AllOff, 0,
234 	MS_Ctrl | 13, MM_AllOff, 0,
235 	MS_Ctrl | 14, MM_AllOff, 0,
236 	MS_Ctrl | 15, MM_AllOff, 0
237 };
238 
239 static UBYTE AllSoundsOff[] =
240 {
241 	MS_Ctrl | 0,  0x78, 0,
242 	MS_Ctrl | 1,  0x78, 0,
243 	MS_Ctrl | 2,  0x78, 0,
244 	MS_Ctrl | 3,  0x78, 0,
245 	MS_Ctrl | 4,  0x78, 0,
246 	MS_Ctrl | 5,  0x78, 0,
247 	MS_Ctrl | 6,  0x78, 0,
248 	MS_Ctrl | 7,  0x78, 0,
249 	MS_Ctrl | 8,  0x78, 0,
250 	MS_Ctrl | 9,  0x78, 0,
251 	MS_Ctrl | 10, 0x78, 0,
252 	MS_Ctrl | 11, 0x78, 0,
253 	MS_Ctrl | 12, 0x78, 0,
254 	MS_Ctrl | 13, 0x78, 0,
255 	MS_Ctrl | 14, 0x78, 0,
256 	MS_Ctrl | 15, 0x78, 0
257 };
258 
259 struct Library *CamdBase;
260 #ifdef PLATFORM_AMIGAOS3
261 struct RealTimeBase *RealTimeBase;
262 #else
263 struct Library *RealTimeBase;
264 #endif
265 static qboolean	midi_playing, midi_paused;
266 static UBYTE *smfdata;
267 static struct Global *glob;
268 static struct MidiNode *pMidiNode;
269 static struct MidiLink *pMidiLink;
270 static struct Task *playerTask;
271 static struct Task *parentTask;
272 static struct Player *pPlayer;
273 
274 #define CHECK_MIDI_ALIVE()		\
275 do {					\
276 	if (!midi_playing)		\
277 	{				\
278 		if (handle)		\
279 			*handle = NULL;	\
280 		return;			\
281 	}				\
282 } while (0)
283 
MIDI_SetVolume(void ** handle,float value)284 static void MIDI_SetVolume (void **handle, float value)
285 {
286 	CHECK_MIDI_ALIVE();
287 
288 }
289 
MIDI_Rewind(void ** handle)290 static void MIDI_Rewind (void **handle)
291 {
292 	CHECK_MIDI_ALIVE();
293 	// mididrv_rewind unused
294 }
295 
MIDI_Update(void ** handle)296 static void MIDI_Update (void **handle)
297 {
298 	CHECK_MIDI_ALIVE();
299 	// mididrv_advance nothing to do here
300 }
301 
MIDI_GetDeviceName(char * dst,size_t dstsize)302 static char *MIDI_GetDeviceName(char *dst, size_t dstsize)
303 {
304 	APTR key;
305 	struct MidiCluster *cluster;
306 	char *retname = NULL;
307 
308 	key = LockCAMD(CD_Linkages);
309 	if (key)
310 	{
311 		cluster = NextCluster(NULL);
312 		while (cluster)
313 		{
314 			//Con_Printf("%s CAMD device: %s\n", __thisfunc__, cluster->mcl_Node.ln_Name);
315 			if (strstr(cluster->mcl_Node.ln_Name, "out"))
316 			{
317 				q_strlcpy(dst, cluster->mcl_Node.ln_Name, dstsize);
318 				retname = dst;
319 				break;
320 			}
321 			cluster = NextCluster(cluster);
322 		}
323 		UnlockCAMD(key);
324 	}
325 
326 	return retname;
327 }
328 
MIDI_Init(void)329 qboolean MIDI_Init(void)
330 {
331 	static const char midi_name[] = "Hexen II Player";
332 	static const char mlink_comment[] = "Hexen II Player Link";
333 	static const char player_name[] = "Hexen II player";
334 
335 	char linkName[32];
336 
337 	if (midi_amiga_camd.available)
338 		return true;
339 
340 	BGM_RegisterMidiDRV(&midi_amiga_camd);
341 
342 	if (safemode || COM_CheckParm("-nomidi"))
343 		return false;
344 
345 	if (!(CamdBase = OpenLibrary("camd.library", 37)))
346 	{
347 		Con_Printf ("Can't open camd.library\n");
348 		return false;
349 	}
350 
351 #ifdef PLATFORM_AMIGAOS3
352 	RealTimeBase = (struct RealTimeBase *) OpenLibrary("realtime.library", 37);
353 #else
354 	RealTimeBase = OpenLibrary("realtime.library", 37);
355 #endif
356 	if (!RealTimeBase)
357 	{
358 		Con_Printf ("Can't open realtime.library\n");
359 		MIDI_Cleanup();
360 		return false;
361 	}
362 
363 	if (!MIDI_GetDeviceName(linkName, sizeof(linkName)))
364 	{
365 		Con_Printf ("No output MIDI device found\n");
366 		MIDI_Cleanup();
367 		return false;
368 	}
369 
370 	if (!(pMidiNode = CreateMidi(
371 		MIDI_Name, (IPTR)midi_name,
372 		MIDI_MsgQueue, 0,
373 		MIDI_SysExSize, 4096,
374 		TAG_END)))
375 	{
376 		Con_Printf("Can't create MIDI Node\n");
377 		MIDI_Cleanup();
378 		return false;
379 	}
380 
381 	if (!(pMidiLink = AddMidiLink(
382 		pMidiNode, MLTYPE_Sender,
383 		MLINK_Comment, (IPTR)mlink_comment,
384 		MLINK_Parse, TRUE,
385 		MLINK_Location, (IPTR)linkName,
386 		TAG_END)))
387 	{
388 		Con_Printf("Can't create MIDI Link\n");
389 		MIDI_Cleanup();
390 		return false;
391 	}
392 
393 	ParseMidi(pMidiLink, ProgramReset, sizeof(ProgramReset));
394 	Delay(5);
395 	ParseMidi(pMidiLink, ResetAllControllers, sizeof(ResetAllControllers));
396 	Delay(5);
397 
398 	if (!(pPlayer = CreatePlayer(
399 		PLAYER_Name, (IPTR)player_name,
400 		PLAYER_Conductor, (IPTR)-1,
401 		TAG_END)))
402 	{
403 		Con_Printf("Can't create the RealTime player\n");
404 		MIDI_Cleanup();
405 		return false;
406 	}
407 
408 	parentTask = FindTask(NULL);
409 
410 	Con_Printf("%s initialized.\nMIDI link: %s\n", midi_amiga_camd.desc, linkName);
411 
412 	midi_paused = false;
413 	midi_amiga_camd.available = true;
414 
415 	return true;
416 }
417 
GetDelta(UBYTE ** value)418 static ULONG GetDelta(UBYTE **value)
419 {
420 	ULONG newval = 0;
421 	UWORD i;
422 	UBYTE dat;
423 
424 	for (i = 0; i < 4; i++) {
425 		dat = *((*value)++);
426 		newval = newval<<7;
427 		newval |= dat & 0x7f;
428 		if (dat < 0x80) break;
429 	}
430 	return newval;
431 }
432 
DecodeEvent(UBYTE * ptdata,struct DecTrack * pDT)433 static UBYTE *DecodeEvent(UBYTE *ptdata, struct DecTrack *pDT)
434 {
435 	ULONG length;
436 	BOOL skipit;
437 
438 	UBYTE status;
439 	UBYTE data;
440 	UBYTE comsize;
441 
442 	pDT->absdelta = 0;
443 
444 	/* is this track all used up? */
445 	if (pDT->trackend)
446 		return NULL;
447 
448 	if (ptdata >= pDT->endmarker)
449 	{
450 		Con_DPrintf("Warning: missing proper track end marker in track %d.\n", pDT->tracknum);
451 
452 		pDT->eventptr = ptdata;
453 		pDT->eventtype = Event_trackend;
454 		pDT->trackend = TRUE;
455 		return ptdata;
456 	}
457 
458 	skipit = FALSE;
459 	do
460 	{
461 		/* Decode delta */
462 		ULONG delta = GetDelta(&ptdata);
463 		pDT->absdelta += delta;
464 		pDT->nexclock += delta;
465 
466 		pDT->eventtype = Event_ignore;
467 
468 		data = *ptdata;
469 
470 		if (data & 0x80) /* Event with status ($80-$FF): decode new status */
471 		{
472 			ptdata++;
473 
474 			if (data < 0xf0)	/* "Normal" events? */
475 			{
476 				status  = data;
477 				comsize = CommandSize[(data&0x7f)>>4];
478 
479 				pDT->status  = status;
480 				pDT->comsize = comsize;
481 				pDT->rstatus = 0;	 /* No running status was used */
482 
483 				pDT->eventtype = Event_playable;
484 				skipit = FALSE;
485 			}
486 			else
487 			{
488 				if (data < 0xf8)	/* System Common Event? */
489 				{
490 					status  = data;
491 					comsize = CommonSize[status-0xf0];
492 
493 					skipit = TRUE;
494 
495 					if (status==0xf0 || status==0xf7)   /* It's a sysex event */
496 					{
497 						pDT->eventptr = ptdata-1;
498 						pDT->eventtype = Event_sysex;
499 						skipit = FALSE;
500 
501 						length = GetDelta(&ptdata);
502 						ptdata += length;
503 					}
504 				}
505 				else
506 				{
507 					status  = data;
508 					comsize = 0;
509 
510 					skipit = TRUE;
511 
512 					if (data == 0xff)	/* It's a meta event ($ff) */
513 					{
514 						UBYTE metatype;
515 
516 						metatype = *(ptdata++);
517 
518 						if (metatype == 0x2F)		/* track end marker */
519 						{
520 							pDT->eventptr = ptdata;
521 							pDT->eventtype = Event_trackend;
522 							pDT->trackend = TRUE;
523 							skipit = FALSE;
524 						}
525 						else
526 						{
527 							if (metatype == 0x51)	/* Tempo change */
528 							{
529 								pDT->eventptr = ptdata;
530 								pDT->eventtype = Event_tempo;
531 								skipit = FALSE;
532 							}
533 
534 							length = GetDelta(&ptdata);
535 							ptdata += length;
536 						}
537 					}
538 				}
539 			}
540 		}
541 		else /* Event without status ($00-$7F): use running status */
542 		{
543 			status = pDT->status;
544 
545 			if (status == 0)
546 			{
547 				Con_DPrintf("Warning: Data bytes without initial status in track %d.\n", pDT->tracknum);
548 				comsize = 1;
549 				skipit = TRUE;
550 			}
551 			else
552 			{
553 				skipit = FALSE;
554 				comsize = pDT->comsize;
555 				pDT->rstatus = status;
556 				pDT->eventtype = Event_playable;
557 			}
558 		}
559 
560 		if (comsize > 0)
561 			pDT->d1 = *ptdata++;
562 		else
563 			pDT->d1 = 0;
564 
565 		if (comsize > 1)
566 			pDT->d2 = *ptdata++;
567 		else
568 			pDT->d2 = 0;
569 	}
570 	while (skipit);
571 
572 	return ptdata;
573 }
574 
CollectEvents(struct Global * glob)575 static void CollectEvents(struct Global *glob)
576 {
577 	UWORD track;
578 	ULONG lowclock;
579 	UBYTE mswitch = glob->masterswitch;
580 	ULONG nexttempo = glob->tempo;
581 	ULONG pos = 0;
582 	ULONG delta = 0;
583 
584 	if (glob->donecount < glob->trackct)
585 	{
586 		lowclock = 0xffffffff;
587 
588 		for (track = 0; track < glob->trackct; track++)
589 		{
590 			if (glob->dtrack[track].nexclock < lowclock && glob->ptrack[track])
591 				lowclock = glob->dtrack[track].nexclock;
592 		}
593 
594 		delta = lowclock - glob->lastclock;
595 		glob->lastclock = lowclock;
596 
597 		for (track = 0; track < glob->trackct; track++)
598 		{
599 			struct DecTrack *pDT = &glob->dtrack[track];
600 
601 			if ((pDT->nexclock == lowclock) && glob->ptrack[track])
602 			{
603 				do
604 				{
605 					/* Transfer event to parse buffer and handle successor */
606 					switch (pDT->eventtype)
607 					{
608 						case Event_playable:
609 						{
610 							if (pos >= MIDIBUFSIZE-3)
611 							{
612 								Con_DPrintf("MIDI buffer overflow (>=%d), dropped event\n", MIDIBUFSIZE);
613 								break;
614 							}
615 							if (pDT->rstatus == glob->lastRSchan)
616 							{
617 								/* Running status */
618 								if (pDT->comsize>0) *(glob->pfillbuf[mswitch] + pos++) = pDT->d1;
619 								if (pDT->comsize>1) *(glob->pfillbuf[mswitch] + pos++) = pDT->d2;
620 							}
621 							else
622 							{
623 								/* New status so store status and data bytes */
624 								*(glob->pfillbuf[mswitch] + pos++) = pDT->status;
625 								glob->lastRSchan = pDT->status;
626 
627 								if (pDT->comsize>0) *(glob->pfillbuf[mswitch] + pos++) = pDT->d1;
628 								if (pDT->comsize>1) *(glob->pfillbuf[mswitch] + pos++) = pDT->d2;
629 	 						}
630 						}
631 						break;
632 
633 						case Event_sysex:
634 						{
635 							/* Link SysEx into Queue */
636 							UBYTE *src = pDT->eventptr;
637 							UBYTE  hdr = *src++;
638 							struct SysEx *se = NULL;
639 
640 							if (hdr == 0xf0)
641 							{
642 								ULONG length = GetDelta(&src);
643 
644 								if ((se = (struct SysEx *) calloc(1, sizeof(struct SysEx)+length+1)) != NULL) {
645 									UBYTE *dst = se->se_data;
646 									//se->se_size = length+1;
647 									*dst++ = 0xf0;
648 									while (length--) *dst++ = *src++;
649 								}
650 							}
651 
652 							if (hdr == 0xf7)
653 							{
654 								ULONG length = GetDelta(&src);
655 
656 								if ((se = (struct SysEx *) calloc(1, sizeof(struct SysEx)+length+1)) != NULL) {
657 									UBYTE *dst = se->se_data;
658 									//se->se_size = length;
659 									while (length--) *dst++ = *src++;
660 								}
661 							}
662 
663 							if (se) {
664 								AddTail((struct List *)&glob->SysExList[mswitch], (struct Node *)se);
665 							}
666 						}
667 						break;
668 
669 						case Event_tempo:
670 						{
671 							UBYTE *meta = pDT->eventptr;
672 							LONG metalen = GetDelta(&meta);
673 
674 							nexttempo = 0;
675 							while (metalen-- > 0)
676 								nexttempo = (nexttempo << 8) | *(meta++);
677 
678 							//Message("Tempo meta-event (%ld BPM) at nexclock %ld!", NULL, 60*1000000/nexttempo, pDT->nexclock);
679 						}
680 						break;
681 
682 						case Event_trackend:
683 						{
684 							glob->donecount++;
685 						}
686 						break;
687 
688 						default:
689 						{
690 							Con_DPrintf("Unknown MIDI event $%02x\n", pDT->eventtype);
691 						}
692 						break;
693 					}
694 
695 					glob->ptrack[track] = DecodeEvent(glob->ptrack[track], pDT);
696 				} while ((pDT->absdelta == 0) && glob->ptrack[track]);
697 			}
698 		}
699 	}
700 
701 	glob->fillstate[mswitch] = pos;
702 	glob->buftempo[mswitch] = nexttempo;
703 
704 	//AddAbsTime(&glob->abstimeLO, &glob->abstimeHI, glob->tempo, delta, glob->division);
705 	//glob->abstime += ((uint64_t)glob->tempo*delta)/glob->division;
706 	glob->abstime += ((double)glob->tempo*delta)/glob->division;
707 	//glob->fillclock[mswitch] = CalcFillClock(glob->abstimeLO, glob->abstimeHI, 833);
708 	glob->fillclock[mswitch] = (ULONG)(glob->abstime / 833);
709 
710 	glob->tempo = nexttempo;
711 }
712 
PlayerFunc(void)713 static void PlayerFunc(void)
714 {
715 	ULONG sigs;
716 	struct SysEx *se, *nse;
717 
718 	SetTaskPri(FindTask(NULL), 25);
719 
720 	/* Transfer the events the fist buffer */
721 	glob->masterswitch = 0;
722 	CollectEvents(glob);
723 
724 	while (TRUE)
725 	{
726 		/* Set the alarm */
727 		if (!SetPlayerAttrs(pPlayer,
728 			PLAYER_AlarmTime, glob->fillclock[glob->masterswitch],
729 			PLAYER_Ready, TRUE,
730 			TAG_END))
731 		{
732 			break;
733 		}
734 
735 		/* Fill the other buffer */
736 		glob->masterswitch ^= 1;
737 		CollectEvents(glob);
738 
739 		sigs = Wait(SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_C);
740 		if (sigs & SIGBREAKF_CTRL_C) {
741 			break;
742 		}
743 
744 		/* Send off one buffer... */
745 		for (se = (struct SysEx *)glob->SysExList[glob->masterswitch^1].mlh_Head;
746 			(nse = (struct SysEx *)se->se_node.mln_Succ) != NULL;
747 			se = nse)
748 		{
749 			PutSysEx(pMidiLink, se->se_data);
750 			Remove((struct Node *)se);
751 			free(se);
752 		}
753 
754 		if (glob->fillstate[glob->masterswitch^1] != 0)
755 		{
756 			ParseMidi(pMidiLink, glob->pfillbuf[glob->masterswitch^1], glob->fillstate[glob->masterswitch^1]);
757 		}
758 
759 		/* Loop the music */
760 		if (glob->donecount >= glob->trackct)
761 		{
762 			//break;
763 			UWORD track;
764 			//Con_Printf("Music loop\n");
765 			glob->donecount = 0;
766 			glob->lastclock = 0;
767 			for (track = 0; track < glob->trackct; track++)
768 			{
769 				glob->dtrack[track].nexclock = 0;
770 				glob->dtrack[track].trackend = FALSE;
771 				glob->ptrack[track] = DecodeEvent(glob->ptrackstart[track], &glob->dtrack[track]);
772 			}
773 			CollectEvents(glob);
774 		}
775 	}
776 
777 	// let the parent thread know that we are done
778 	Forbid();
779 	midi_playing = false;
780 	Signal(parentTask, SIGBREAKF_CTRL_F);
781 }
782 
MIDI_Play(const char * filename)783 static void *MIDI_Play (const char *filename)
784 {
785 	static const char task_name[] = "Hexen II CAMD player task";
786 
787 	struct SMFHeader *hdr;
788 	UBYTE *pbyte;
789 	size_t smfdatasize;
790 	UWORD track;
791 	LONG error;
792 	ULONG id;
793 
794 	if (!midi_amiga_camd.available)
795 		return NULL;
796 
797 	if (!filename || !*filename)
798 	{
799 		Con_DPrintf("null music file name\n");
800 		return NULL;
801 	}
802 
803 	MIDI_Stop (NULL);
804 
805 	smfdata = (UBYTE *) FS_LoadMallocFile (filename, NULL);
806 	if (!smfdata)
807 	{
808 		Con_DPrintf("Couldn't open %s\n", filename);
809 		return NULL;
810 	}
811 	smfdatasize = fs_filesize;
812 	if (smfdatasize < 34)
813 	{
814 		Con_Printf("MIDI file too short.\n");
815 		MIDI_Stop (NULL);
816 		return false;
817 	}
818 
819 	pbyte = smfdata;
820 	if (memcmp(pbyte,"RIFF",4) == 0 && memcmp(pbyte +8,"RMID",4) == 0
821 					&& memcmp(pbyte+12,"data",4) == 0)
822 		pbyte += 20; /* Microsoft RMID */
823 
824 	hdr = (struct SMFHeader *)pbyte;
825 	hdr->ChunkID = BigLong(hdr->ChunkID);
826 	hdr->VarLeng = BigLong(hdr->VarLeng);
827 	hdr->Format = BigShort(hdr->Format);
828 	hdr->Ntrks = (UWORD) BigShort(hdr->Ntrks);
829 	hdr->Division = BigShort(hdr->Division);
830 
831 	if (hdr->ChunkID != ID_MTHD || hdr->VarLeng != 6 ||
832 		(hdr->Format != 0 && hdr->Format != 1) ||
833 		!hdr->Ntrks || hdr->Ntrks > MAXTRAX || hdr->Division <= 0)
834 	{
835 		Con_Printf("Can't recognize the MIDI format.\n");
836 		MIDI_Stop (NULL);
837 		return false;
838 	}
839 
840 	if (!(glob = (struct Global *) calloc(1, sizeof(struct Global))))
841 	{
842 		Con_DPrintf("No memory for global MIDI variables\n");
843 		MIDI_Stop (NULL);
844 		return false;
845 	}
846 
847 	glob->division = hdr->Division;
848 	glob->lastRSchan = 0xf1;
849 	glob->tempo = 500000;
850 
851 	NewList((struct List *)&glob->SysExList[0]);
852 	NewList((struct List *)&glob->SysExList[1]);
853 
854 	glob->trackct = 0;
855 
856 	pbyte += 14; /* sizeof(struct SMFHeader) */
857 	while ((pbyte-smfdata < smfdatasize) && (glob->trackct < MAXTRAX))
858 	{
859 		id = (ULONG) BigLong(*(ULONG *)pbyte);
860 		if (id == ID_MTRK)
861 		{
862 			if (glob->trackct > 0)
863 				glob->ptrackend[glob->trackct-1] = pbyte;
864 
865 			if (glob->trackct == hdr->Ntrks)
866 				break;
867 
868 			glob->ptrackstart[glob->trackct] = pbyte+8;
869 			glob->trackct++;
870 			pbyte += 4;
871 		}
872 		else
873 		{
874 			pbyte++;
875 		}
876 	}
877 
878 	if (glob->trackct > 0)
879 		glob->ptrackend[glob->trackct-1] = pbyte;
880 
881 	if (glob->trackct != hdr->Ntrks)
882 	{
883 		Con_Printf("Missing tracks. Only %d tracks found (%d expected).\n", glob->trackct, hdr->Ntrks);
884 		MIDI_Stop (NULL);
885 		return false;
886 	}
887 
888 	for (track = 0; track < glob->trackct; track++)
889 	{
890 		glob->dtrack[track].tracknum = track+1;
891 		glob->dtrack[track].endmarker = glob->ptrackend[track];
892 		glob->ptrack[track] = DecodeEvent(glob->ptrackstart[track], &glob->dtrack[track]);
893 	}
894 
895 	/* start thread */
896 #ifdef __MORPHOS__
897 	playerTask = (struct Task *)CreateNewProcTags(
898 		NP_Entry, (IPTR)PlayerFunc,
899 		NP_CodeType, CODETYPE_PPC,
900 		NP_Name, (IPTR)task_name,
901 		TAG_DONE);
902 #else
903 	playerTask = (struct Task *)CreateNewProcTags(
904 		NP_Entry, (IPTR)PlayerFunc,
905 		NP_Name, (IPTR)task_name,
906 		TAG_DONE);
907 #endif
908 
909 	if (!playerTask)
910 	{
911 		Con_Printf("Can't create the MIDI player task\n");
912 		MIDI_Stop (NULL);
913 		return false;
914 	}
915 
916 	midi_playing = true;
917 
918 	if (!SetPlayerAttrs(pPlayer,
919 		PLAYER_AlarmSigTask, (IPTR)playerTask,
920 		PLAYER_AlarmSigBit, SIGBREAKB_CTRL_E,
921 		//PLAYER_Ready, TRUE,
922 		PLAYER_ErrorCode, (IPTR)&error,
923 		TAG_END))
924 	{
925 		Con_Printf("Can't set the RealTime player attrs, error %ld\n", (long)error);
926 		MIDI_Stop (NULL);
927 		return false;
928 	}
929 
930 	SetConductorState(pPlayer, CONDSTATE_RUNNING, 0);
931 
932 	Con_Printf ("Started midi music %s\n", filename);
933 
934 	return glob;
935 }
936 
MIDI_Pause(void ** handle)937 static void MIDI_Pause (void **handle)
938 {
939 	CHECK_MIDI_ALIVE();
940 	SetConductorState(pPlayer, CONDSTATE_PAUSED, 0);
941 	ParseMidi(pMidiLink, AllNotesOff, sizeof(AllNotesOff));
942 	ParseMidi(pMidiLink, AllSoundsOff, sizeof(AllSoundsOff));
943 }
944 
MIDI_Resume(void ** handle)945 static void MIDI_Resume (void **handle)
946 {
947 	CHECK_MIDI_ALIVE();
948 	SetConductorState(pPlayer, CONDSTATE_RUNNING, 0);
949 }
950 
MIDI_Stop(void ** handle)951 static void MIDI_Stop (void **handle)
952 {
953 	//CHECK_MIDI_ALIVE();
954 
955 	if (pPlayer)
956 	{
957 		SetConductorState(pPlayer, CONDSTATE_STOPPED, 0);
958 		SetPlayerAttrs(pPlayer, PLAYER_Ready, FALSE, TAG_END);
959 	}
960 
961 	if (pMidiLink)
962 	{
963 		ParseMidi(pMidiLink, AllNotesOff, sizeof(AllNotesOff));
964 		ParseMidi(pMidiLink, AllSoundsOff, sizeof(AllSoundsOff));
965 		Delay(10);
966 	}
967 
968 	if (playerTask)
969 	{
970 		if (midi_playing)
971 		{
972 			SetSignal(SIGBREAKF_CTRL_F, 0);
973 			Signal(playerTask, SIGBREAKF_CTRL_C);
974 			Wait(SIGBREAKF_CTRL_F);
975 		}
976 		playerTask = NULL;
977 	}
978 
979 	if (glob)
980 	{
981 		struct MinNode *se, *nse;
982 
983 		for (se = glob->SysExList[0].mlh_Head; (nse = se->mln_Succ) != NULL; se = nse)
984 		{
985 			Remove((struct Node *)se);
986 			free(se);
987 		}
988 		for (se = glob->SysExList[1].mlh_Head; (nse = se->mln_Succ) != NULL; se = nse)
989 		{
990 			Remove((struct Node *)se);
991 			free(se);
992 		}
993 
994 		free(glob);
995 		glob = NULL;
996 	}
997 
998 	if (smfdata)
999 	{
1000 		free(smfdata);
1001 		smfdata = NULL;
1002 	}
1003 }
1004 
MIDI_Cleanup(void)1005 void MIDI_Cleanup(void)
1006 {
1007 	if (midi_amiga_camd.available)
1008 	{
1009 		midi_amiga_camd.available = false;
1010 
1011 		MIDI_Stop (NULL);
1012 	}
1013 
1014 	if (pMidiLink)
1015 	{
1016 		RemoveMidiLink(pMidiLink);
1017 		pMidiLink = NULL;
1018 	}
1019 
1020 	if (pMidiNode)
1021 	{
1022 		DeleteMidi(pMidiNode);
1023 		pMidiNode = NULL;
1024 	}
1025 
1026 	if (pPlayer)
1027 	{
1028 		DeletePlayer(pPlayer);
1029 		pPlayer = NULL;
1030 	}
1031 
1032 	if (RealTimeBase)
1033 	{
1034 		CloseLibrary((struct Library *)RealTimeBase);
1035 		RealTimeBase = NULL;
1036 	}
1037 
1038 	if (CamdBase)
1039 	{
1040 		CloseLibrary(CamdBase);
1041 		CamdBase = NULL;
1042 	}
1043 }
1044