home *** CD-ROM | disk | FTP | other *** search
- ;/*
- LC -b1 -icamd:include -cfist -v -j73 smf.c
- blink FROM LIB:c.o smf.o TO smf LIB LIB:lc.lib LIB:amiga.lib
- quit
- */
-
- /*=======================================================================
-
- 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, David Joiner, et al.
-
- Design and coding by Dan Baker, Commodore Business Machines.
-
- The tempo handling used by this program is 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.
- =======================================================================*/
-
- /* System Include Files */
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <libraries/dosextens.h>
-
- /* CAMD MIDI Library Includes */
- #include <clib/camd_protos.h>
- #include <midi/camd.h>
- #include <midi/camdbase.h>
- #include <pragmas/camd_pragmas.h>
-
- /* CAMD Real Time Library Includes */
- #include <clib/realtime_protos.h>
- #include <midi/realtime.h>
- #include <midi/realtimebase.h>
- #include <pragmas/realtime_pragmas.h>
-
- /* System function prototypes */
- #include <clib/exec_protos.h>
- #include <clib/dos_protos.h>
-
- /* Lattice Standard I/O */
- #include <latticei:stdlib.h>
- #include <latticei:stdio.h>
-
- #ifdef LATTICE
- int CXBRK(void) { return(0); } /* Disable Lattice CTRL/C handling */
- int chkabort(void) { return(0); } /* really */
- #endif
-
-
- /*-------------------*/
- /* Prototypes */
- /*-------------------*/
- void kill(char *killstring);
- ULONG ComVarLen (UBYTE *value);
- UBYTE *DecodeEvent(UBYTE *,struct SMFTrack *, ULONG);
- LONG transfer(struct SMFTrack *,ULONG,LONG);
- ULONG changetempo(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) )
- #define MThd MakeID('M','T','h','d')
- #define MTrk MakeID('M','T','r','k')
-
- 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 pad3;
- };
-
- /*------------------*/
- /* MIDI Header File */
- /*------------------*/
- #define MAXTRAX 16L
- #define TWOxMAXTRAX 32L
- #define MIDIBUFSIZE 512L
-
-
- /* Library Bases */
- struct Library *CamdBase,
- *RealTimeBase;
-
- /* 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 PlayerInfo *pi, Tag tag, ...)
- { return SetPlayerAttrsA(pi, (struct TagItem *)&tag );
- }
-
- struct PlayerInfo *CreatePlayer(Tag tag, ...)
- { return CreatePlayerA( (struct TagItem *)&tag );
- }
-
-
- /*--------------*/
- /* Globals */
- /*--------------*/
- struct MidiLink *pMidiLink;
- struct MidiNode *pMidiNode;
- struct PlayerInfo *pPlayerInfo;
- BPTR smfhandle;
- UBYTE *smfdata,*ptrack[TWOxMAXTRAX],*pData,trackct;
- LONG midiSignal,smfdatasize,fillclock[2];
- ULONG oldclock,sizeDTrack,tfactor,division,donecount;
- UBYTE *pfillbuf[2],lastRSchan;
-
-
-
- /*-----------------------------*/
- /* CODE Starts Here */
- /*-----------------------------*/
-
- ULONG
- ComVarLen (value)
- 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);
- }
-
-
- void
- main(int argc, char **argv)
- {
- char *smfname,iobuffer[14];
- struct SMFHeader *pSMFHeader;
- UBYTE *pbyte,x;
- WORD w;
- LONG rdcount,y,z,res;
- BOOL notdone,timerr;
- ULONG masterswitch,lowclock,
- ylength[2],wakeup;
-
- BYTE oldpri; /* Priority to restore */
- struct Task *mt; /* Pointer to this task */
-
- UBYTE fillbuf1[MIDIBUFSIZE]; /* These buffers hold the notes translated */
- UBYTE fillbuf2[MIDIBUFSIZE]; /* from the SMF file for playback */
- struct DecTrack *pDTrack[MAXTRAX];
-
- oldclock=0L;
- smfdata=NULL;
- smfhandle=0;
- pMidiLink=NULL;
- pMidiNode=NULL;
- pPlayerInfo=NULL;
- RealTimeBase=NULL;
- CamdBase=NULL;
- mt=NULL;
- notdone=TRUE;
- pfillbuf[0]=fillbuf1;
- pfillbuf[1]=fillbuf2;
- fillclock[0]=0L;
- fillclock[1]=0L;
- donecount=0L;
- lastRSchan=0xf1; /* Status of $F1 is undefined in Standard MIDI file Spec */
- tfactor=12;
-
- /*--------------------------------------*/
- /* Open the CAMD and RealTime libraries */
- /*--------------------------------------*/
- CamdBase=OpenLibrary("camd.library",0L);
- if(!CamdBase)
- kill("Can't open CAMD MIDI Library\n");
-
- RealTimeBase=OpenLibrary("realtime.library",0L);
- if(!RealTimeBase)
- kill("Can't open CAMD Real Time Library\n");
-
- /*---------------*/
- /* Open the File */
- /*---------------*/
- if(argv[1]==NULL)
- kill("No file name given.\n");
- smfname=argv[1];
- smfhandle= Open( (UBYTE *)smfname , MODE_OLDFILE );
- if(smfhandle==0)
- kill("Cannot open file.\n");
-
-
- /*-------------------------------*/
- /* Read the SMF Header ID/Length */
- /*-------------------------------*/
- rdcount = Read(smfhandle,iobuffer,14);
- if(rdcount==-1)
- kill ("Bad file during read.\n");
- if(rdcount<14)
- kill ("Not an SMF file, too short.\n");
-
- /*-----------------*/
- /* Evaluate Header */
- /*-----------------*/
- pSMFHeader=(struct SMFHeader *)iobuffer;
- if (pSMFHeader->ChunkID != MThd )
- kill("SMF file has unknown header ID.\n");
-
- if (pSMFHeader->VarLeng != 6 )
- kill("SMF file has unknown header.\n");
-
- if (pSMFHeader->Format == 0 )
- printf("Parsing SMF file format type 0\n");
- else if (pSMFHeader->Format == 1 )
- printf("Parsing SMF file format type 1\n");
- else
- kill("Can't parse this SMF file type.\n");
-
-
- if(pSMFHeader->Ntrks >MAXTRAX )
- kill("SMF file has more than MAXTRAX tracks.\n");
- else
- printf("SMF file has %ld tracks\n",pSMFHeader->Ntrks);
-
-
- /*--------------------*/
- /* Evaluate time base */
- /*--------------------*/
- if (pSMFHeader->Division < 0)
- {
- /* Real time: find frame rate */
- w=(pSMFHeader->Division >> 8) & 0x007f;
- if(w!=24 && w!=25 && w!=29 && w!=30)
- kill("Non-metrical time specified; not MIDI/SMPTE frame rate\n");
- else
- kill("Non-metrical time; MIDI/SMPTE frame rate\n");
-
- /* Real-time: find seconds resolution */
- w=pSMFHeader->Division & 0x007f;
- if(w==4)
- kill("Non-metrical time in quarter seconds (MTC resolution)\n");
- else if(w==8 || w==10 || w==80)
- kill("Non-metrical time in 1/nths of a second (bit resolution)\n");
- else
- kill("Non-metrical time in 1/nths of a second\n");
- }
- else
- {
- printf("SMF file 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." */
- tfactor=changetempo(500000L); /* One quarter note every half second */
- }
-
-
- /*--------------------------------------*/
- /* Calculate size and Read rest of file */
- /*--------------------------------------*/
- y = Seek(smfhandle,0L,OFFSET_END);
- if(y==-1)
- kill("SMF file or drive problem\n");
- z = Seek(smfhandle,y,OFFSET_BEGINNING);
- if(z==-1)
- kill("SMF file or drive problem\n");
-
- smfdatasize=z-y; /* CDTV demo disk uses 12000 here */
-
- smfdata=AllocMem(smfdatasize,MEMF_PUBLIC | MEMF_CLEAR);
- if(smfdata==NULL)
- kill("No memory for SMF file read.\n");
-
- rdcount = Read(smfhandle,smfdata,smfdatasize);
- if(rdcount==-1)
- kill ("Bad file during read.\n");
-
- /* Commented out for CD ROM demo */
- if(rdcount<smfdatasize)
- kill ("SMF file too short.\n");
-
-
- /*----------------------*/
- /* Find the MIDI tracks */
- /*----------------------*/
- 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("Too many or missing tracks\n");
- printf("There are %ld tracks in this SMF file.\n",trackct);
-
-
- /*----------------------------------------------*/
- /* Set up a MidiNode and a MidiLink. Link the */
- /* node to the default "out.0" MidiCluster . */
- /*----------------------------------------------*/
- pMidiNode=CreateMidi( MIDI_Name, "SMF Player",
- MIDI_MsgQueue, 0L, /* This is a send-only */
- MIDI_SysExSize,0L, /* MIDI node so no input */
- TAG_END); /* buffers are needed. */
- if(!pMidiNode)
- kill("No memory for MIDI Node\n");
-
- pMidiLink=AddMidiLink( pMidiNode, MLTYPE_Sender,
- MLINK_Comment, "SMF Player Link",
- MLINK_Parse, TRUE,
- MLINK_Location, "out.0",
- TAG_END);
- if(!pMidiLink)
- kill("No memory for MIDI Link\n");
-
-
- /*----------------------------------------*/
- /* Set up DTrack structure for each track */
- /*----------------------------------------*/
- sizeDTrack=trackct*sizeof(struct DecTrack);
- pData=AllocMem( sizeDTrack, MEMF_PUBLIC | MEMF_CLEAR );
- if(!pData)
- kill("No memory for work area...\n");
-
- for(x=0;x<trackct;x++)
- {
- pDTrack[x]=(struct DecTrack *)
- (x * sizeof(struct DecTrack) + pData);
- /* add end marker */
- pDTrack[x]->endmarker = ptrack[MAXTRAX+x];
- }
-
-
- /*------------------------------------------------*/
- /* Get events from track into DecTrack structures */
- /*------------------------------------------------*/
- y=0;
- masterswitch=0L;
- lowclock=0xffffffff;
-
- /* 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]=(LONG)(tfactor*lowclock);
-
-
- /*------------------------------------*/
- /* 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]=(LONG)(tfactor*lowclock);
-
- /*-----------------------------------------------------*/
- /* Priority Must Be Above Intuition and Graphics Stuff */
- /*-----------------------------------------------------*/
- mt=FindTask(NULL);
- oldpri=SetTaskPri(mt,21);
-
-
- /*---------------------------------------------------------------*/
- /* Set up a PlayerInfo and a Conductor to get timing information */
- /*---------------------------------------------------------------*/
- midiSignal = AllocSignal(-1L);
- if(midiSignal==-1)
- kill("Couldn't allocate a signal bit\n");
-
- pPlayerInfo=CreatePlayer( PLAYER_Name, "SMF Player PlayerInfo",
- PLAYER_Conductor, "SMF Player's Conductor",
- PLAYER_SignalTask, mt,
- PLAYER_AlarmSigBit,midiSignal,
- TAG_END);
- if(!pPlayerInfo)
- kill("Can't create a PlayerInfo\n");
-
- /*---------------------------------*/
- /* Make sure the clock is stopped. */
- /*---------------------------------*/
- res = SetConductorState( pPlayerInfo, CLOCKSTATE_STOPPED, 0L );
- if(res!=0)
- kill("Couldn't stop conducotr\n");
-
- /*-------------------------------------------*/
- /* 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( pPlayerInfo, CLOCKSTATE_RUNNING, 0L );
- if(res!=0)
- kill("Couldn't start conducotr\n");
-
- /*---------------------*/
- /* and set the alarm. */
- /*---------------------*/
- timerr = SetPlayerAttrs( pPlayerInfo,
- PLAYER_AlarmTime, fillclock[masterswitch],
- PLAYER_Ready, TRUE,
- TAG_END);
- if(!timerr)
- kill("Couldn't set player attrs\n");
-
- /*-----------------*/
- /* 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);
-
-
- /*---------------------------------------------------------------*/
- /* Wait() for the CAMD alarm or a CTRL-C keypress from the user. */
- /*---------------------------------------------------------------*/
- if(timerr)
- wakeup=Wait(1L<< midiSignal | SIGBREAKF_CTRL_C);
-
- if(wakeup & SIGBREAKF_CTRL_C)
- {
- /* Restore Priority */
- if(mt!=NULL) SetTaskPri(mt,oldpri);
- /* And Quit */
- kill("User abort\n");
- }
-
- /*-------------------------------*/
- /* 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( pPlayerInfo,
- PLAYER_AlarmTime, fillclock[masterswitch],
- PLAYER_Ready, TRUE,
- TAG_END);
- if(!timerr)
- kill("Couldn't set player attrs 2\n");
- }
-
- /*-----------------------------------*/
- /* Finish off the last set of events */
- /*-----------------------------------*/
- masterswitch^=1L;
-
- if(timerr)
- wakeup=Wait(1L<< midiSignal | SIGBREAKF_CTRL_C);
-
-
- if(ylength[masterswitch^1]!=0)
- {
- ParseMidi(pMidiLink,pfillbuf[masterswitch^1],
- ylength[masterswitch^1]);
- }
-
- /* Restore Priority */
- if(mt!=NULL) SetTaskPri(mt,oldpri);
-
- kill("AOK\n");
- }
-
-
- /*------------------------------*/
- /* Cleanup and return resources */
- /*------------------------------*/
- void
- kill(char *killstring)
- {
- if(midiSignal != -1)FreeSignal(midiSignal);
- if(pPlayerInfo) DeletePlayer(pPlayerInfo);
- if(pData) FreeMem(pData,sizeDTrack);
- if(pMidiLink) RemoveMidiLink(pMidiLink);
- if(pMidiNode) DeleteMidi(pMidiNode);
- if(RealTimeBase) CloseLibrary(RealTimeBase);
- if(CamdBase) CloseLibrary(CamdBase);
- if(smfhandle) Close(smfhandle);
- if(smfdata) FreeMem(smfdata,smfdatasize);
- printf(killstring);
- 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;
-
- pDTdata->absdelta = 0L;
- pDTdata->playable = TRUE; /* Assume it's playble and not a meta-event */
-
- skipit=FALSE;
- do
- {
- /* is this track all used up? */
- if( ptdata >= pDTdata->endmarker )
- {
- printf("Track done,...\n");
- 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 );
-
- /* 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->metatype=0xff;
- printf("Sysex event");
- length=ComVarLen(ptdata);
- pDTdata->absmlength=length;
- while(*ptdata>127)ptdata++;
-
- ptdata+=length;
- }
- else /* It's an unkown event type ($f1-$f6, $f8-$fe) */
- {
- pDTdata->metatype=0xff;
- printf("Unknown event");
- }
- }
- }
- 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("Bad file, data bytes without initial status.\n");
-
- 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 1.66 milliseconds (600 Hz). This makes handling */
- /* of SMF tempo pretty rough. Tempo is controlled through a ULONG integer */
- /* named tfactor which is used to multiply the time deltas in the SMF */
- /* file. We can't multiply the time deltas by a fractional amount and */
- /* that makes tempo handling even rougher. For correct tempo handling the */
- /* clock quantum has to be variable and controlled by this program. */
- /*-------------------------------------------------------------------------*/
- ULONG
- changetempo(ULONG ctbpm)
- {
- ULONG timefac,timerem,tickfreq;
-
- tickfreq=(ULONG)((struct RealTimeBase *)RealTimeBase)->TickFreq;
-
- /* CAMD uses 1.66ms quantum for 600 ticks/sec */
- /* SMF uses one tick = quarter note/pSMFHeader->Division */
- /* Hence, microseconds per delta in SMF is given by ctbpm/division */
- /* and BPM is given by 60,000,000/ctbpm. */
- timefac=(tickfreq * (ctbpm/division)) / 1000000;
- timerem=(tickfreq * (ctbpm/division)) % 1000000;
- if ( timerem >= 500000 )
- timefac++;
-
- return(timefac);
- }
-
-
-
-