home *** CD-ROM | disk | FTP | other *** search
- /*
- ** $PROJECT: midi.datatype
- **
- ** $VER: handlemidi.c 40.0 (11.09.97)
- **
- ** This file is intended to hold all three subroutines to load midi data
- ** into memory, to save it on disk and to play it. Currently it just
- ** does playing the contents of a SMF file from a memory block.
- **
- ** Based on playmf created by Dan Baker.
- ** Bugfixed, extended (sysex,pause,repeat) and adapted by Martin Gierich.
- ** All Rights Reserved !
- **
- ** $HISTORY:
- ** 11.09.97 : 40.00 : Initial BETA release
- **
- ** $TABSIZE: 3
- */
-
-
- /*=======================================================================
- Original playmf message:
-
- This file is a prototype score player which reads standard MIDI files,
- format type 0 or 1. It conforms to the 1.0 specification for standard
- MIDI files by Dave Oppenheim of Opcode Systems. It uses both the CAMD and
- RealTime libraries by Roger Dannenberg, Bill Barton, David Joiner, et al.
-
- Design and coding by Dan Baker, Commodore Business Machines.
-
- The tempo handling used by this program is a bit crude and is known to have
- deficiencies. It should not be used as an example of proper tempo handling.
- This program also makes extensive use of globals and other techniques which
- are convenient to the program's author. Apologies to the reader.
- =======================================================================*/
-
-
- /*-------------------*/
- /* Includes */
- /*-------------------*/
- /* System include files */
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <dos/dos.h>
-
- /* CAMD library includes */
- #include <midi/camd.h>
- #include <midi/camdbase.h>
- #include <midi/mididefs.h>
- #include <proto/camd.h>
-
- /* RealTime library includes */
- #include <libraries/realtime.h>
- #include <proto/realtime.h>
-
- /* System function prototypes */
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include <proto/intuition.h>
-
- /* Mididt specific includes */
- #include "debug.h"
- #include "register.h"
- #include "midiclass.h"
-
- /* Trigger definitions */
- #define STM_PAUSE 1
- #define STM_PLAY 2
- #define STM_REWIND 12
- #define STM_FASTFORWARD 13
- #define STM_STOP 14
- #define STM_LOCATE 16
-
-
-
- /*-------------------*/
- /* Prototypes */
- /*-------------------*/
- void kill(char *killstring);
- ULONG ComVarLen (UBYTE *value);
- UBYTE *DecodeEvent(UBYTE *,struct DecTrack *, ULONG);
- LONG transfer(struct DecTrack *,ULONG,LONG);
- ULONG changetempo(ULONG,ULONG,ULONG);
-
-
- /*---------------------*/
- /* S M F Header File */
- /*---------------------*/
- /* Four-character IDentifier builder*/
- #define MakeID(a,b,c,d) ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) )
-
- struct SMFHeader {LONG ChunkID; /* 4 ASCII characters */
- LONG VarLeng;
- WORD Format;
- UWORD Ntrks;
- WORD Division;
- };
-
-
- struct DecTrack { ULONG absdelta; /* 32-bit delta */
- ULONG nexclock; /* storage */
- UBYTE status; /* status from file */
- UBYTE rstatus; /* running status from track */
- UBYTE d1; /* data byte 1 */
- UBYTE d2; /* data byte 2 */
- ULONG absmlength; /* 32-bit absolute metalength */
- UBYTE *endmarker;
- UBYTE metatype; /* meta event type */
- BOOL playable;
- UBYTE tracknum;
- };
-
-
- /*------------------*/
- /* MIDI Header File */
- /*------------------*/
- #define MAXTRAX 64L
- #define TWOxMAXTRAX 128L
- #define MIDIBUFSIZE 512L
-
-
- /*------------------*/
- /* Compiler glues */
- /*------------------*/
- /* Compiler glue: stub functions for camd.library */
- struct MidiNode *CreateMidi(Tag tag, ...)
- { return CreateMidiA((struct TagItem *)&tag );
- }
-
- BOOL SetMidiAttrs(struct MidiNode *mi, Tag tag, ...)
- { return SetMidiAttrsA(mi, (struct TagItem *)&tag );
- }
-
- struct MidiLink *AddMidiLink(struct MidiNode *mi, LONG type, Tag tag, ...)
- { return AddMidiLinkA(mi, type, (struct TagItem *)&tag );
- }
-
-
- /* Compiler glue: stub functions for realtime.library */
- BOOL SetPlayerAttrs(struct Player *pi, Tag tag, ...)
- { return SetPlayerAttrsA(pi, (struct TagItem *)&tag );
- }
-
- struct Player *CreatePlayer(Tag tag, ...)
- { return CreatePlayerA( (struct TagItem *)&tag );
- }
-
- #define TSHIFT 4
-
- /*--------------*/
- /* Globals */
- /*--------------*/
- struct Library *RealTimeBase;
- struct Library *CamdBase;
- struct MidiDTData *mdt;
- struct MidiLink *pMidiLink;
- struct MidiNode *pMidiNode;
- struct Player *pPlayer;
- UBYTE *smfdata,*ptrack[TWOxMAXTRAX],*pData,trackct;
- LONG midiSignal,smfdatasize,tempo_offs;
- ULONG fillclock[2];
- ULONG sizeDTrack,tfactor,initfactor,division,
- donecount,startdone;
- UBYTE *pfillbuf[2],lastRSchan;
- BOOL Playing;
-
-
- #define ALLNOTESOFFLEN 48
- UBYTE AllNotesOff[ALLNOTESOFFLEN] = {
- MS_Ctrl | 0, MM_AllOff, 0,
- MS_Ctrl | 1, MM_AllOff, 0,
- MS_Ctrl | 2, MM_AllOff, 0,
- MS_Ctrl | 3, MM_AllOff, 0,
- MS_Ctrl | 4, MM_AllOff, 0,
- MS_Ctrl | 5, MM_AllOff, 0,
- MS_Ctrl | 6, MM_AllOff, 0,
- MS_Ctrl | 7, MM_AllOff, 0,
- MS_Ctrl | 8, MM_AllOff, 0,
- MS_Ctrl | 9, MM_AllOff, 0,
- MS_Ctrl | 10, MM_AllOff, 0,
- MS_Ctrl | 11, MM_AllOff, 0,
- MS_Ctrl | 12, MM_AllOff, 0,
- MS_Ctrl | 13, MM_AllOff, 0,
- MS_Ctrl | 14, MM_AllOff, 0,
- MS_Ctrl | 15, MM_AllOff, 0
- };
-
-
-
- /*-----------------------------*/
- /* CODE Starts Here */
- /*-----------------------------*/
-
- ULONG ComVarLen (UBYTE *value)
- {
- register ULONG newval;
- register UBYTE x;
-
- x=0;newval=0L;
- while(x<4)
- {
- newval <<= 7;
- newval |= *(value+x) & 0x7f;
- if(*(value+x) < 0x80)x=4;
- x++;
- }
- return(newval);
- }
-
-
- /*-----------*/
- /* Main code */
- /*-----------*/
- ClassCall void PlayMidi(void)
- {
- struct Process *myproc;
- struct PlayMsg *mymsg;
- BYTE oldpri;
-
- struct SMFHeader *pSMFHeader;
- UBYTE *pbyte,x;
- WORD numtrack;
- UWORD countcyc;
- LONG y,z,res;
- BOOL timerr;
- ULONG masterswitch,lowclock,
- ylength[2],starttfactor,
- pause_offs,pause_start;
-
- UBYTE fillbuf1[MIDIBUFSIZE]; /* These buffers hold the notes translated */
- UBYTE fillbuf2[MIDIBUFSIZE]; /* from the midifile file for playback */
- struct DecTrack *pDTrack[MAXTRAX];
-
- /*--------------------*/
- /* Initialize globals */
- /*--------------------*/
- pMidiLink=NULL;
- pMidiNode=NULL;
- pPlayer=NULL;
- midiSignal=-1;
- donecount=0L;
- pfillbuf[0]=fillbuf1;
- pfillbuf[1]=fillbuf2;
- fillclock[0]=0L;
- fillclock[1]=0L;
- lastRSchan=0xf1; /* Status of $F1 is undefined in Standard MIDI file Spec */
- starttfactor= 12 << TSHIFT;
- Playing = FALSE;
-
- /*-----------------*/
- /* Get startup msg */
- /*-----------------*/
- myproc=(struct Process *) FindTask(NULL);
- WaitPort(&myproc->pr_MsgPort);
- mymsg=(struct PlayMsg *) GetMsg(&myproc->pr_MsgPort);
- mdt=mymsg->mdt;
- ReplyMsg(&mymsg->msg);
- smfdatasize=mdt->mdt_BufferLen;
- smfdata=mdt->mdt_Buffer;
- D(bug("Received msg %lx on %lx\n",mymsg,myproc));
-
- RealTimeBase=NULL;
- CamdBase=OpenLibrary("camd.library",37L);
- if(!CamdBase)
- kill("No camd.library");
-
- RealTimeBase=OpenLibrary("realtime.library",37L);
- if(!RealTimeBase)
- kill("No realtime.library");
-
- /*-----------------*/
- /* Evaluate Header */
- /*-----------------*/
- pSMFHeader=(struct SMFHeader *)mdt->mdt_Buffer;
- if ( (pSMFHeader->ChunkID != MakeID('M','T','h','d')) ||
- (pSMFHeader->VarLeng != 6) )
- kill("Unknown header");
-
- if (pSMFHeader->Format == 0 )
- D(bug("Parsing midifile format type 0\n"));
- else if (pSMFHeader->Format == 1 )
- D(bug("Parsing midifile format type 1\n"));
- else
- kill("Unknown format type");
-
-
- if(pSMFHeader->Ntrks >MAXTRAX )
- kill("Wrong number of tracks");
- else
- D(bug("Midifile has %ld tracks\n",pSMFHeader->Ntrks));
-
-
- /*--------------------*/
- /* Evaluate time base */
- /*--------------------*/
- if (pSMFHeader->Division < 0)
- kill("Unsupported time code");
- else
- {
- D(bug("Midifile has 1/%ldth quarter notes per delta tick\n",
- pSMFHeader->Division));
- division=(ULONG)pSMFHeader->Division;
-
- /* According to "Standrd MIDI Files 1.0", July 1988, page 5 */
- /* para. 4: "...time signature is assumed to be 4/4 and the */
- /* tempo 120 beats per minute." */
- starttfactor=changetempo(500000L,starttfactor,0L); /* One quarter note every half second */
- }
-
- /*----------------------------------------------*/
- /* Set up a MidiNode and a MidiLink. Link the */
- /* node to the default "out.0" MidiCluster . */
- /*----------------------------------------------*/
- pMidiNode=CreateMidi( MIDI_Name, "MidiDT Player",
- MIDI_MsgQueue, 0L, /* This is a send-only */
- MIDI_SysExSize, 4096L, /* MIDI node so no input */
- TAG_END); /* buffers are needed. */
- if(!pMidiNode)
- kill("Out of memory");
-
- pMidiLink=AddMidiLink( pMidiNode, MLTYPE_Sender,
- MLINK_Comment, "MidiDT Link",
- MLINK_Parse, TRUE,
- MLINK_Location, mdt->mdt_Cluster,
- TAG_END);
- if(!pMidiLink)
- kill("Out of memory");
-
- /*---------------------------------------------------------------*/
- /* Set up a Player and a Conductor to get timing information */
- /*---------------------------------------------------------------*/
- midiSignal = AllocSignal(-1L);
- if(midiSignal==-1)
- kill("Out of memory");
-
- pPlayer=CreatePlayer( PLAYER_Name, "MidiDT Player",
- PLAYER_Conductor, "MidiDT Conductor",
- PLAYER_AlarmSigTask, myproc,
- PLAYER_AlarmSigBit,midiSignal,
- TAG_END);
- if(!pPlayer)
- kill("Player fail");
-
-
- for (countcyc=0; countcyc<mdt->mdt_Cycles || mdt->mdt_Repeat; countcyc++)
- {
- D(bug("Cycle %ld of %ld\n",countcyc+1,mdt->mdt_Cycles));
-
- /*----------------------*/
- /* Find the MIDI tracks */
- /*----------------------*/
- donecount=0;
- trackct=0;
- pbyte=smfdata;
-
- while((pbyte-smfdata < smfdatasize) && (trackct < MAXTRAX))
- {
- if((*pbyte=='M')&&(*(pbyte+1)=='T')&&
- (*(pbyte+2)=='r')&&(*(pbyte+3)=='k'))
- {
- /* Beginning of track */
- ptrack[trackct]=pbyte+8;
- /* End of track marker */
- ptrack[MAXTRAX+trackct-1]=pbyte;
- trackct++;
- pbyte+=4;
- }
- else pbyte++;
- }
-
- /* End of track marker */
- ptrack[MAXTRAX+trackct-1]=pbyte;
-
- if(trackct != pSMFHeader->Ntrks)
- kill("Wrong number of tracks");
- D(bug("There are %ld tracks in this Midifile.\n",trackct));
-
-
- /*----------------------------------------*/
- /* Set up DTrack structure for each track */
- /*----------------------------------------*/
- sizeDTrack=trackct*sizeof(struct DecTrack);
- pData=AllocMem( sizeDTrack, MEMF_PUBLIC | MEMF_CLEAR );
- if(!pData)
- kill("Out of memory");
-
- numtrack=1;
- for(x=0;x<trackct;x++)
- {
- pDTrack[x]=(struct DecTrack *)
- (x * sizeof(struct DecTrack) + pData);
- /* add end marker */
- pDTrack[x]->endmarker = ptrack[MAXTRAX+x];
- pDTrack[x]->tracknum = numtrack++;
- }
-
- /*------------------------------------------------*/
- /* Get events from track into DecTrack structures */
- /*------------------------------------------------*/
- y=0;
- masterswitch=0L;
- lowclock=0xffffffff;
- tempo_offs=0;
- tfactor=starttfactor;
-
- /* Initialize DecTrack structures */
- for(x=0;x<trackct;x++)
- {
- /* Takes a pointer to the delta of a raw <MTrk event>, a pointer */
- /* to a DecTrack decoding structure to store the decoded event and */
- /* a switch that tells which of the two buffers to use. Returns a */
- /* pointer to the next raw <MTrk event> in the track or 0 if the */
- /* track is exhausted. */
- ptrack[x] = DecodeEvent( ptrack[x] , pDTrack[x] , masterswitch );
- if(pDTrack[x]->nexclock < lowclock && ptrack[x])
- /* Find the first event */
- lowclock=pDTrack[x]->nexclock;
- }
-
- /*-----------------------------------*/
- /* Transfer first events to A buffer */
- /*-----------------------------------*/
- for(x=0;x<trackct;x++)
- {
- if((pDTrack[x]->nexclock==lowclock) && ptrack[x])
- {
- /* Transfer event to parse buffer and handle successor */
- y=transfer(pDTrack[x],masterswitch,y);
- z=1;
- while(z==1)
- {
- ptrack[x]=DecodeEvent(ptrack[x],pDTrack[x],masterswitch);
- /* Next delta is zero... */
- if( !(pDTrack[x]->absdelta) && ptrack[x])
- {
- y=transfer(pDTrack[x],masterswitch,y);
- }
- else {z=0;}
- }
- }
- }
- ylength[masterswitch]=y;
- fillclock[masterswitch]=(ULONG)(((tfactor*lowclock)+tempo_offs) >> TSHIFT);
-
- /*------------------------------------*/
- /* Transfer second events to B buffer */
- /*------------------------------------*/
- y=0;
- masterswitch=1L;
- lowclock=0xffffffff;
- for(x=0;x<trackct;x++)
- {
- if(pDTrack[x]->nexclock < lowclock && ptrack[x])
- lowclock=pDTrack[x]->nexclock;
- }
-
- for(x=0;x<trackct;x++)
- {
- if(pDTrack[x]->nexclock==lowclock && ptrack[x])
- {
- /* Transfer event to parse buffer and handle successor */
- y=transfer(pDTrack[x],masterswitch,y);
- z=1;
- while(z==1)
- {
- ptrack[x]=DecodeEvent(ptrack[x],pDTrack[x],masterswitch);
- /* Next delta is zero... */
- if( !(pDTrack[x]->absdelta) && ptrack[x])
- {
- y=transfer(pDTrack[x],masterswitch,y);
- }
- else {z=0;}
- }
- }
- }
-
- ylength[masterswitch]=y;
- fillclock[masterswitch]=(ULONG)(((tfactor*lowclock)+tempo_offs) >> TSHIFT);
-
- /*-----------------------------------------------------*/
- /* Priority Must Be Above Intuition and Graphics Stuff */
- /*-----------------------------------------------------*/
- oldpri=SetTaskPri(myproc,25);
-
- /*---------------------------------*/
- /* Make sure the clock is stopped. */
- /*---------------------------------*/
- res = SetConductorState( pPlayer, CONDSTATE_STOPPED, 0L );
- if(res!=0)
- kill("Conductor fail");
-
- /*-------------------------------------------*/
- /* Play the first batch of notes in Buffer A */
- /*-------------------------------------------*/
- if(ylength[masterswitch^1]!=0)
- {
- ParseMidi(pMidiLink,pfillbuf[masterswitch^1],
- ylength[masterswitch^1]);
- }
-
- /*------------------------------------*/
- /* and start the RealTime alarm clock */
- /*------------------------------------*/
- res = SetConductorState( pPlayer, CONDSTATE_RUNNING, 0L );
- if(res!=0)
- kill("Conductor fail");
-
- /*---------------------*/
- /* and set the alarm. */
- /*---------------------*/
- timerr = SetPlayerAttrs( pPlayer,
- PLAYER_AlarmTime, fillclock[masterswitch],
- PLAYER_Ready, TRUE,
- TAG_END);
- if(!timerr)
- kill("Player fail");
-
- Playing = TRUE;
- pause_offs=0;
-
- /*-----------------*/
- /* MAIN EVENT LOOP */
- /*-----------------*/
- while(donecount<trackct)
- {
- masterswitch ^= 1L;
- y=0;
- lowclock=0xffffffff;
-
- /*------------------------------------------------*/
- /* Get events from track into DecTrack structures */
- /*------------------------------------------------*/
- for(x=0;x<trackct;x++)
- {
- if((pDTrack[x]->nexclock < lowclock) && ptrack[x])
- lowclock=pDTrack[x]->nexclock;
- }
-
- /*-----------------------------------*/
- /* Transfer events to current buffer */
- /*-----------------------------------*/
- for(x=0;x<trackct;x++)
- {
- if((pDTrack[x]->nexclock==lowclock) && ptrack[x])
- {
- /* Transfer event to parse buffer and handle successor */
- y=transfer(pDTrack[x],masterswitch,y);
- z=1;
- while(z==1)
- {
- ptrack[x]=DecodeEvent(ptrack[x],pDTrack[x],masterswitch);
- /* Next delta is zero... */
- if( !(pDTrack[x]->absdelta) && ptrack[x] )
- {
- y=transfer(pDTrack[x],masterswitch,y);
- }
- else {z=0;}
- }
- }
- }
-
- ylength[masterswitch]=y;
- fillclock[masterswitch]=(LONG)((((tfactor*lowclock)+tempo_offs)>>TSHIFT)+pause_offs);
-
- /*---------------------------------------------------------------*/
- /* Wait() for the CAMD alarm or a CTRL-C keypress from the user. */
- /*---------------------------------------------------------------*/
- if (mdt->mdt_Playing==STM_PAUSE)
- {
- D(bug("Received PAUSE\n"));
- pause_start = pPlayer->pl_MetricTime;
- while( mdt->mdt_Playing==STM_PAUSE )
- Delay(10);
- pause_offs += pPlayer->pl_MetricTime - pause_start;
- D(bug("No more PAUSE\n"));
- }
- if(timerr)
- Wait(1L<< midiSignal | SIGBREAKF_CTRL_C);
-
- if (mdt->mdt_Playing==STM_STOP)
- {
- /* Restore Priority and Quit */
- SetTaskPri(myproc,oldpri);
- D(bug("Received STOP\n"));
- kill(NULL);
- }
-
- /*-------------------------------*/
- /* Start the next set of events */
- /*-------------------------------*/
- if(ylength[masterswitch^1]!=0)
- {
- ParseMidi(pMidiLink,pfillbuf[masterswitch^1],
- ylength[masterswitch^1]);
- }
-
- /*---------------------*/
- /* and set the alarm. */
- /*---------------------*/
- timerr = SetPlayerAttrs( pPlayer,
- PLAYER_AlarmTime, fillclock[masterswitch],
- PLAYER_Ready, TRUE,
- TAG_END);
- if(!timerr)
- kill("Player fail");
- }
-
- /*-----------------------------------*/
- /* Finish off the last set of events */
- /*-----------------------------------*/
- D(bug("Last events\n"));
- masterswitch^=1L;
-
- if(timerr)
- Wait(1L<< midiSignal | SIGBREAKF_CTRL_C);
-
-
- if(ylength[masterswitch^1]!=0)
- {
- ParseMidi(pMidiLink,pfillbuf[masterswitch^1],
- ylength[masterswitch^1]);
- }
-
-
- /* Restore Priority */
- SetTaskPri(myproc,oldpri);
- FreeMem(pData,sizeDTrack);
- pData=NULL;
-
- }
-
- D(bug("End of File, all OK.\n"));
- kill(NULL);
- }
-
-
- /*------------------------------*/
- /* Cleanup and return resources */
- /*------------------------------*/
- void kill(char *killstring)
- {
- struct EasyStruct myES =
- {
- sizeof(struct EasyStruct),
- 0,
- "MidiDT Player",
- "Fatal Error:\n%s",
- "Ok",
- };
-
- if(Playing) ParseMidi(pMidiLink,AllNotesOff,ALLNOTESOFFLEN);
- if(pPlayer) DeletePlayer(pPlayer);
- if(midiSignal != -1)FreeSignal(midiSignal);
- if(pData) FreeMem(pData,sizeDTrack);
- if(pMidiLink) RemoveMidiLink(pMidiLink);
- if(pMidiNode) DeleteMidi(pMidiNode);
- if(RealTimeBase) CloseLibrary(RealTimeBase);
- if(CamdBase) CloseLibrary(CamdBase);
- if(killstring) EasyRequest(NULL, &myES, NULL, killstring);
- D(if(killstring) {bug("Player exit: ");bug(killstring);bug(".\n");});
- if(mdt->mdt_SignalTask) Signal(mdt->mdt_SignalTask, mdt->mdt_SignalBit);
- mdt->mdt_PlayProc=NULL;
- mdt->mdt_Playing=STM_STOP;
- Exit(0);
- }
-
-
- /*--------------------------------------------------*/
- /* Translate from raw track data to a decoded event */
- /*--------------------------------------------------*/
- UBYTE *DecodeEvent(UBYTE *ptdata,struct DecTrack *pDTdata, ULONG deswitch)
- {
- LONG status;
- ULONG length;
- BOOL skipit;
- UBYTE tempbyte;
-
- pDTdata->absdelta = 0L;
- pDTdata->playable = TRUE; /* Assume it's playble and not a meta-event */
-
- do
- {
- /* is this track all used up? */
- if( ptdata >= pDTdata->endmarker )
- {
- D(bug("Track %ld done\n", pDTdata->tracknum));
- donecount++;
- return(0L);
- }
- else /* there is more data to handle in the track */
- {
- /* Decode delta */
- pDTdata->absdelta += ComVarLen(ptdata);
- pDTdata->nexclock+= pDTdata->absdelta;
-
- /* Update pointer to event following delta */
- while(*ptdata>127)
- {
- ptdata++;
- }
- ptdata++;
-
- if(*ptdata>127) /* Event with status ($80-$FF): decode new status */
- {
- status=*ptdata;
-
- pDTdata->status=status;
- pDTdata->rstatus=0; /* No running status was used */
-
- ptdata++;
-
- if(status<240) /* Handle easy status $8x - $Ex */
- {
- skipit=FALSE;
- pDTdata->d1 = *ptdata;
- if(status<192 || status>223) /* $80-$BF, $E0-$EF: 2 data bytes */
- {
- ptdata++;
- pDTdata->d2=*ptdata;
- }
- else pDTdata->d2=0; /* $C0-$DF: 1 data byte */
- }
- else /* Status byte $Fx, system exclusive or meta events */
- {
- skipit=TRUE;
-
- if(status==0xff) /* It's a meta event ($ff) */
- {
- pDTdata->metatype=*ptdata;
-
- ptdata++; /* Now pointing at length byte */
-
- if(pDTdata->metatype==81)
- {
- /* Tempo change event. There are 60 milllion */
- /* microseconds in a minute. The lower 3 bytes */
- /* pointed to by ptdata give the microseconds */
- /* per MIDI quarter note. So, assuming a quarter */
- /* note gets the beat, this equation */
- /* 60000000L / */
- /* ( *((ULONG *)ptdata) & 0x00ffffff ) )*/
- /* gives beats per minute. */
-
- tfactor = changetempo( *((ULONG *)ptdata) & 0x00ffffff, tfactor,
- pDTdata->nexclock - pDTdata->absdelta);
-
- /* Tempo event is not playable. This prevents the */
- /* event from being transferred to the play buffer */
- pDTdata->playable = FALSE;
-
- /* Even though this event can't be played, it */
- /* takes some time and should not be skipped. */
- skipit=FALSE;
- }
- length=ComVarLen(ptdata);
- pDTdata->absmlength=length;
- while(*ptdata>127)ptdata++;
-
- ptdata+=length;
- }
- else if(status==0xf0 || status==0xf7) /* It's a sysex event */
- {
- pDTdata->playable = FALSE;
- skipit=FALSE;
- pDTdata->metatype=0xff;
- length=ComVarLen(ptdata);
- pDTdata->absmlength=length;
- while(*ptdata>127)ptdata++;
- D(bug("Sysex event %lx %lx %lx %lx %lx %lx %lx %lx\n",
- (long)ptdata[1], (long)ptdata[2], (long)ptdata[3], (long)ptdata[4], (long)ptdata[5],
- (long)ptdata[6], (long)ptdata[7], (long)ptdata[8]));
- tempbyte=ptdata[0];
- ptdata[0]=0xf0;
- PutSysEx(pMidiLink, ptdata);
- ptdata[0]=tempbyte;
-
- ptdata+=length;
- }
- else /* It's an unkown event type ($f1-$f6, $f8-$fe) */
- {
- pDTdata->metatype=0xff;
- D(bug("Unknown event\n"));
- }
- }
- }
- else /* Event without status ($00-$7F): use running status */
- {
- skipit=FALSE;
- /* Running status data bytes */
- status=pDTdata->status;
- pDTdata->rstatus=status;
-
- if(status==0)
- kill("No initial status");
-
- pDTdata->d1=*ptdata;
-
- if(status<192 || status>223) /* $80-$BF, $E0-$EF: 2 data bytes */
- {
- ptdata++;
- pDTdata->d2=*ptdata;
- }
- else pDTdata->d2=0; /* $C0-$DF: 1 data byte */
- }
- ptdata++;
- }
- }
- while(skipit);
-
- return(ptdata);
- }
-
-
- /*------------------------------------------------------------*/
- /* Transfer the decoded event to the fill buffer for playback */
- /*------------------------------------------------------------*/
- LONG
- transfer(struct DecTrack *pDT,ULONG mswitch,LONG ylen)
- {
- ULONG y;
- y=(ULONG )ylen;
-
- if (pDT->playable)
- {
- if(pDT->rstatus == lastRSchan)
- {
- /* Running status so just put the 2 data bytes in buffer */
- *(pfillbuf[mswitch] + y)=pDT->d1;
- y++;
- *(pfillbuf[mswitch] + y)=pDT->d2;
- }
- else
- {
- /* New status so store status and data bytes */
- *(pfillbuf[mswitch] + y)=pDT->status;
- y++;
- *(pfillbuf[mswitch] + y)=pDT->d1;
- if(pDT->status<192 || pDT->status>223)
- {
- y++;
- *(pfillbuf[mswitch] + y)=pDT->d2;
- }
- lastRSchan=pDT->status;
- }
- y++;
- }
- return((LONG)y);
- }
-
-
- /*-------------------------------------------------------------------------*/
- /* Handle the Change Tempo event. With the realtime.library, the timing */
- /* quantum is fixed at .83 milliseconds (1.2kHz). This makes handling of */
- /* Playmf tempo a bit rough. Tempo is controlled through a ULONG integer */
- /* named tfactor which is used to multiply the time deltas in the Playmf */
- /* file. We can't multiply the time deltas by a fractional amount so we */
- /* will shift up before dividing down for tfactor, and shift down after */
- /* using the tfactor later. This in effect gives us fractional control. */
- /* To deal with the fact that tempo changes would cause us to wait for */
- /* inappropriate past or future times on upcoming notes, we calculate a */
- /* tempo_offs time offset (also shifted up) for use in time calculations. */
- /*-------------------------------------------------------------------------*/
- ULONG
- changetempo(ULONG ctbpm, ULONG oldtfactor, ULONG eventtime)
- {
- ULONG newtfactor,oldtime,newtime;
-
- /* ctbpm is the new microseconds per midi quarter note */
- /* Division is the part of a quarter note represented by a delta of 1 */
- /* So ctbpm/division is the new microseconds per delta */
- /* CAMD uses 1200 ticks/sec or 833 microseconds per tick */
- /* We will compute a value which is: */
- /* ((microseconds per delta) << TSHIFT ) / 833 */
- /* The TSHIFT will give us some fractional control, and will be */
- /* shifted out later when the new tfactor) is used. */
-
- newtfactor = ((ctbpm << TSHIFT) / division) / 833; /* new tfactor */
-
- if((eventtime)&&(Playing))
- {
- /* We know our midi event time relative to the start of the file.
- * We can calculate what CAMD time that equals at our initial tempo,
- * and also at the new tempo. We will save this difference as
- * tempo_offs, and use tempo_offs to adjust all main program
- * midi time -> CAMD time calculations.
- * In other words, we figure out what CAMD time we would have
- * been at now, if we had been playing at the new tempo all along.
- * Using that information, we can adjust all upcoming MIDI
- * Alarm wakeup times up or down by the amount the new tempo
- * tfactor would have thrown us off. Note that tempo_offs is
- * based on values shifted up by TSHIFT, and this will be shifted
- * out later in the main program time calculations.
- */
- oldtime = initfactor * eventtime;
- newtime = newtfactor * eventtime;
- tempo_offs = oldtime - newtime;
- D(bug("oldtime=%ld newtime=%ld tempo_offs = %ld\n",
- oldtime>>TSHIFT,newtime>>TSHIFT,tempo_offs>>TSHIFT));
- }
- else
- {
- initfactor = newtfactor;
- }
- D(bug("changetempo: ctbpm=%ld newtfactor=%ld\n", ctbpm,newtfactor));
- return(newtfactor);
- }
-
-
-