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