mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-16 09:21:36 +00:00
09f0c239b6
git-svn-id: https://svn.eduke32.com/eduke32@5 1a8010ca-5511-0410-912e-c29ae57300e0
2048 lines
51 KiB
C
2048 lines
51 KiB
C
/*
|
|
Copyright (C) 1994-1995 Apogee Software, Ltd.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
Modifications for JonoF's port by Jonathon Fowler (jonof@edgenetwk.com)
|
|
*/
|
|
/**********************************************************************
|
|
module: MIDI.C
|
|
|
|
author: James R. Dose
|
|
date: May 25, 1994
|
|
|
|
Midi song file playback routines.
|
|
|
|
(c) Copyright 1994 James R. Dose. All Rights Reserved.
|
|
**********************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "standard.h"
|
|
#include "usrhooks.h"
|
|
#include "music.h"
|
|
#include "_midi.h"
|
|
#include "midi.h"
|
|
#include "mpu401.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
static __int64 nexttick, tickdelta;
|
|
|
|
extern int MUSIC_SoundDevice;
|
|
|
|
static const int _MIDI_CommandLengths[ NUM_MIDI_CHANNELS ] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0
|
|
};
|
|
|
|
static int ( *_MIDI_RerouteFunctions[ NUM_MIDI_CHANNELS ] )
|
|
(
|
|
int event,
|
|
int c1,
|
|
int c2
|
|
) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
|
|
|
static track *_MIDI_TrackPtr = NULL;
|
|
static int _MIDI_TrackMemSize;
|
|
static int _MIDI_NumTracks;
|
|
|
|
static int _MIDI_SongActive = FALSE;
|
|
static int _MIDI_SongLoaded = FALSE;
|
|
static int _MIDI_Loop = FALSE;
|
|
|
|
static int _MIDI_Division;
|
|
static int _MIDI_Tick = 0;
|
|
static int _MIDI_Beat = 1;
|
|
static int _MIDI_Measure = 1;
|
|
static unsigned _MIDI_Time;
|
|
static int _MIDI_BeatsPerMeasure;
|
|
static int _MIDI_TicksPerBeat;
|
|
static int _MIDI_TimeBase;
|
|
static long _MIDI_FPSecondsPerTick;
|
|
static unsigned _MIDI_TotalTime;
|
|
static int _MIDI_TotalTicks;
|
|
static int _MIDI_TotalBeats;
|
|
static int _MIDI_TotalMeasures;
|
|
|
|
unsigned long _MIDI_PositionInTicks;
|
|
unsigned long _MIDI_GlobalPositionInTicks;
|
|
|
|
static int _MIDI_Context;
|
|
|
|
static int _MIDI_ActiveTracks;
|
|
static int _MIDI_TotalVolume = MIDI_MaxVolume;
|
|
|
|
static int _MIDI_ChannelVolume[ NUM_MIDI_CHANNELS ];
|
|
static int _MIDI_UserChannelVolume[ NUM_MIDI_CHANNELS ] =
|
|
{
|
|
256, 256, 256, 256, 256, 256, 256, 256,
|
|
256, 256, 256, 256, 256, 256, 256, 256
|
|
};
|
|
|
|
static midifuncs *_MIDI_Funcs = NULL;
|
|
|
|
static int Reset = FALSE;
|
|
|
|
int MIDI_Tempo = 120;
|
|
|
|
char MIDI_PatchMap[ 128 ];
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
Memory locked functions:
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_ReadNumber
|
|
|
|
Reads a variable length number from a MIDI track.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static long _MIDI_ReadNumber
|
|
(
|
|
void *from,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
unsigned char *FromPtr;
|
|
long value;
|
|
|
|
if ( size > 4 )
|
|
{
|
|
size = 4;
|
|
}
|
|
|
|
FromPtr = ( unsigned char * )from;
|
|
|
|
value = 0;
|
|
while( size-- )
|
|
{
|
|
value <<= 8;
|
|
value += *FromPtr++;
|
|
}
|
|
|
|
return( value );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_ReadDelta
|
|
|
|
Reads a variable length encoded delta delay time from the MIDI data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static long _MIDI_ReadDelta
|
|
(
|
|
track *ptr
|
|
)
|
|
|
|
{
|
|
long value;
|
|
unsigned char c;
|
|
|
|
GET_NEXT_EVENT( ptr, value );
|
|
|
|
if ( value & 0x80 )
|
|
{
|
|
value &= 0x7f;
|
|
do
|
|
{
|
|
GET_NEXT_EVENT( ptr, c );
|
|
value = ( value << 7 ) + ( c & 0x7f );
|
|
}
|
|
while ( c & 0x80 );
|
|
}
|
|
|
|
return( value );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_ResetTracks
|
|
|
|
Sets the track pointers to the beginning of the song.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_ResetTracks
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int i;
|
|
track *ptr;
|
|
|
|
_MIDI_Tick = 0;
|
|
_MIDI_Beat = 1;
|
|
_MIDI_Measure = 1;
|
|
_MIDI_Time = 0;
|
|
_MIDI_BeatsPerMeasure = 4;
|
|
_MIDI_TicksPerBeat = _MIDI_Division;
|
|
_MIDI_TimeBase = 4;
|
|
|
|
_MIDI_PositionInTicks = 0;
|
|
//_MIDI_GlobalPositionInTicks = 0;
|
|
_MIDI_ActiveTracks = 0;
|
|
_MIDI_Context = 0;
|
|
|
|
ptr = _MIDI_TrackPtr;
|
|
for( i = 0; i < _MIDI_NumTracks; i++ )
|
|
{
|
|
ptr->pos = ptr->start;
|
|
ptr->delay = _MIDI_ReadDelta( ptr );
|
|
ptr->active = ptr->EMIDI_IncludeTrack;
|
|
ptr->RunningStatus = 0;
|
|
ptr->currentcontext = 0;
|
|
ptr->context[ 0 ].loopstart = ptr->start;
|
|
ptr->context[ 0 ].loopcount = 0;
|
|
|
|
if ( ptr->active )
|
|
{
|
|
_MIDI_ActiveTracks++;
|
|
}
|
|
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_AdvanceTick
|
|
|
|
Increment tick counters.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_AdvanceTick
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
_MIDI_PositionInTicks++;
|
|
_MIDI_Time += _MIDI_FPSecondsPerTick;
|
|
|
|
_MIDI_Tick++;
|
|
while( _MIDI_Tick > _MIDI_TicksPerBeat )
|
|
{
|
|
_MIDI_Tick -= _MIDI_TicksPerBeat;
|
|
_MIDI_Beat++;
|
|
}
|
|
while( _MIDI_Beat > _MIDI_BeatsPerMeasure )
|
|
{
|
|
_MIDI_Beat -= _MIDI_BeatsPerMeasure;
|
|
_MIDI_Measure++;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_SysEx
|
|
|
|
Interpret SysEx Event.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_SysEx
|
|
(
|
|
track *Track
|
|
)
|
|
|
|
{
|
|
int length;
|
|
|
|
length = _MIDI_ReadDelta( Track );
|
|
Track->pos += length;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_MetaEvent
|
|
|
|
Interpret Meta Event.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_MetaEvent
|
|
(
|
|
track *Track
|
|
)
|
|
|
|
{
|
|
int command;
|
|
int length;
|
|
int denominator;
|
|
long tempo;
|
|
|
|
GET_NEXT_EVENT( Track, command );
|
|
GET_NEXT_EVENT( Track, length );
|
|
|
|
switch( command )
|
|
{
|
|
case MIDI_END_OF_TRACK :
|
|
Track->active = FALSE;
|
|
|
|
_MIDI_ActiveTracks--;
|
|
break;
|
|
|
|
case MIDI_TEMPO_CHANGE :
|
|
tempo = 60000000L / _MIDI_ReadNumber( Track->pos, 3 );
|
|
MIDI_SetTempo( tempo );
|
|
break;
|
|
|
|
case MIDI_TIME_SIGNATURE :
|
|
if ( ( _MIDI_Tick > 0 ) || ( _MIDI_Beat > 1 ) )
|
|
{
|
|
_MIDI_Measure++;
|
|
}
|
|
|
|
_MIDI_Tick = 0;
|
|
_MIDI_Beat = 1;
|
|
|
|
_MIDI_BeatsPerMeasure = (int)*Track->pos;
|
|
denominator = (int)*( Track->pos + 1 );
|
|
_MIDI_TimeBase = 1;
|
|
while( denominator > 0 )
|
|
{
|
|
_MIDI_TimeBase += _MIDI_TimeBase;
|
|
denominator--;
|
|
}
|
|
_MIDI_TicksPerBeat = ( _MIDI_Division * 4 ) / _MIDI_TimeBase;
|
|
break;
|
|
}
|
|
|
|
Track->pos += length;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_InterpretControllerInfo
|
|
|
|
Interprets the MIDI controller info.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static int _MIDI_InterpretControllerInfo
|
|
(
|
|
track *Track,
|
|
int TimeSet,
|
|
int channel,
|
|
int c1,
|
|
int c2
|
|
)
|
|
|
|
{
|
|
track *trackptr;
|
|
int tracknum;
|
|
int loopcount;
|
|
|
|
switch( c1 )
|
|
{
|
|
case MIDI_MONO_MODE_ON :
|
|
Track->pos++;
|
|
break;
|
|
|
|
case MIDI_VOLUME :
|
|
if ( !Track->EMIDI_VolumeChange )
|
|
{
|
|
_MIDI_SetChannelVolume( channel, c2 );
|
|
}
|
|
break;
|
|
|
|
case EMIDI_INCLUDE_TRACK :
|
|
case EMIDI_EXCLUDE_TRACK :
|
|
break;
|
|
|
|
case EMIDI_PROGRAM_CHANGE :
|
|
if ( Track->EMIDI_ProgramChange )
|
|
{
|
|
_MIDI_Funcs->ProgramChange( channel, MIDI_PatchMap[ c2 & 0x7f ] );
|
|
}
|
|
break;
|
|
|
|
case EMIDI_VOLUME_CHANGE :
|
|
if ( Track->EMIDI_VolumeChange )
|
|
{
|
|
_MIDI_SetChannelVolume( channel, c2 );
|
|
}
|
|
break;
|
|
|
|
case EMIDI_CONTEXT_START :
|
|
break;
|
|
|
|
case EMIDI_CONTEXT_END :
|
|
if ( ( Track->currentcontext == _MIDI_Context ) ||
|
|
( _MIDI_Context < 0 ) ||
|
|
( Track->context[ _MIDI_Context ].pos == NULL ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
Track->currentcontext = _MIDI_Context;
|
|
Track->context[ 0 ].loopstart = Track->context[ _MIDI_Context ].loopstart;
|
|
Track->context[ 0 ].loopcount = Track->context[ _MIDI_Context ].loopcount;
|
|
Track->pos = Track->context[ _MIDI_Context ].pos;
|
|
Track->RunningStatus = Track->context[ _MIDI_Context ].RunningStatus;
|
|
|
|
if ( TimeSet )
|
|
{
|
|
break;
|
|
}
|
|
|
|
_MIDI_Time = Track->context[ _MIDI_Context ].time;
|
|
_MIDI_FPSecondsPerTick = Track->context[ _MIDI_Context ].FPSecondsPerTick;
|
|
_MIDI_Tick = Track->context[ _MIDI_Context ].tick;
|
|
_MIDI_Beat = Track->context[ _MIDI_Context ].beat;
|
|
_MIDI_Measure = Track->context[ _MIDI_Context ].measure;
|
|
_MIDI_BeatsPerMeasure = Track->context[ _MIDI_Context ].BeatsPerMeasure;
|
|
_MIDI_TicksPerBeat = Track->context[ _MIDI_Context ].TicksPerBeat;
|
|
_MIDI_TimeBase = Track->context[ _MIDI_Context ].TimeBase;
|
|
TimeSet = TRUE;
|
|
break;
|
|
|
|
case EMIDI_LOOP_START :
|
|
case EMIDI_SONG_LOOP_START :
|
|
if ( c2 == 0 )
|
|
{
|
|
loopcount = EMIDI_INFINITE;
|
|
}
|
|
else
|
|
{
|
|
loopcount = c2;
|
|
}
|
|
|
|
if ( c1 == EMIDI_SONG_LOOP_START )
|
|
{
|
|
trackptr = _MIDI_TrackPtr;
|
|
tracknum = _MIDI_NumTracks;
|
|
}
|
|
else
|
|
{
|
|
trackptr = Track;
|
|
tracknum = 1;
|
|
}
|
|
|
|
while( tracknum > 0 )
|
|
{
|
|
trackptr->context[ 0 ].loopcount = loopcount;
|
|
trackptr->context[ 0 ].pos = trackptr->pos;
|
|
trackptr->context[ 0 ].loopstart = trackptr->pos;
|
|
trackptr->context[ 0 ].RunningStatus = trackptr->RunningStatus;
|
|
trackptr->context[ 0 ].active = trackptr->active;
|
|
trackptr->context[ 0 ].delay = trackptr->delay;
|
|
trackptr->context[ 0 ].time = _MIDI_Time;
|
|
trackptr->context[ 0 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
|
|
trackptr->context[ 0 ].tick = _MIDI_Tick;
|
|
trackptr->context[ 0 ].beat = _MIDI_Beat;
|
|
trackptr->context[ 0 ].measure = _MIDI_Measure;
|
|
trackptr->context[ 0 ].BeatsPerMeasure = _MIDI_BeatsPerMeasure;
|
|
trackptr->context[ 0 ].TicksPerBeat = _MIDI_TicksPerBeat;
|
|
trackptr->context[ 0 ].TimeBase = _MIDI_TimeBase;
|
|
trackptr++;
|
|
tracknum--;
|
|
}
|
|
break;
|
|
|
|
case EMIDI_LOOP_END :
|
|
case EMIDI_SONG_LOOP_END :
|
|
if ( ( c2 != EMIDI_END_LOOP_VALUE ) ||
|
|
( Track->context[ 0 ].loopstart == NULL ) ||
|
|
( Track->context[ 0 ].loopcount == 0 ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( c1 == EMIDI_SONG_LOOP_END )
|
|
{
|
|
trackptr = _MIDI_TrackPtr;
|
|
tracknum = _MIDI_NumTracks;
|
|
_MIDI_ActiveTracks = 0;
|
|
}
|
|
else
|
|
{
|
|
trackptr = Track;
|
|
tracknum = 1;
|
|
_MIDI_ActiveTracks--;
|
|
}
|
|
|
|
while( tracknum > 0 )
|
|
{
|
|
if ( trackptr->context[ 0 ].loopcount != EMIDI_INFINITE )
|
|
{
|
|
trackptr->context[ 0 ].loopcount--;
|
|
}
|
|
|
|
trackptr->pos = trackptr->context[ 0 ].loopstart;
|
|
trackptr->RunningStatus = trackptr->context[ 0 ].RunningStatus;
|
|
trackptr->delay = trackptr->context[ 0 ].delay;
|
|
trackptr->active = trackptr->context[ 0 ].active;
|
|
if ( trackptr->active )
|
|
{
|
|
_MIDI_ActiveTracks++;
|
|
}
|
|
|
|
if ( !TimeSet )
|
|
{
|
|
_MIDI_Time = trackptr->context[ 0 ].time;
|
|
_MIDI_FPSecondsPerTick = trackptr->context[ 0 ].FPSecondsPerTick;
|
|
_MIDI_Tick = trackptr->context[ 0 ].tick;
|
|
_MIDI_Beat = trackptr->context[ 0 ].beat;
|
|
_MIDI_Measure = trackptr->context[ 0 ].measure;
|
|
_MIDI_BeatsPerMeasure = trackptr->context[ 0 ].BeatsPerMeasure;
|
|
_MIDI_TicksPerBeat = trackptr->context[ 0 ].TicksPerBeat;
|
|
_MIDI_TimeBase = trackptr->context[ 0 ].TimeBase;
|
|
TimeSet = TRUE;
|
|
}
|
|
|
|
trackptr++;
|
|
tracknum--;
|
|
}
|
|
break;
|
|
|
|
default :
|
|
if ( _MIDI_Funcs->ControlChange )
|
|
{
|
|
_MIDI_Funcs->ControlChange( channel, c1, c2 );
|
|
}
|
|
}
|
|
|
|
return TimeSet;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_ServiceRoutine
|
|
|
|
Task that interperates the MIDI data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static int _MIDI_ServiceRoutine ( void )
|
|
{
|
|
int event;
|
|
int channel;
|
|
int command;
|
|
track *Track;
|
|
int tracknum;
|
|
int status;
|
|
int c1;
|
|
int c2;
|
|
int TimeSet = FALSE;
|
|
|
|
if ( _MIDI_SongActive ) {
|
|
Track = _MIDI_TrackPtr;
|
|
tracknum = 0;
|
|
while( tracknum < _MIDI_NumTracks ) {
|
|
while ( ( Track->active ) && ( Track->delay == 0 ) ) {
|
|
GET_NEXT_EVENT( Track, event );
|
|
|
|
if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL ) {
|
|
switch( event ) {
|
|
case MIDI_SYSEX :
|
|
case MIDI_SYSEX_CONTINUE :
|
|
_MIDI_SysEx( Track );
|
|
break;
|
|
|
|
case MIDI_META_EVENT :
|
|
_MIDI_MetaEvent( Track );
|
|
break;
|
|
}
|
|
|
|
if ( Track->active ) {
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( event & MIDI_RUNNING_STATUS ) {
|
|
Track->RunningStatus = event;
|
|
} else {
|
|
event = Track->RunningStatus;
|
|
Track->pos--;
|
|
}
|
|
|
|
channel = GET_MIDI_CHANNEL( event );
|
|
command = GET_MIDI_COMMAND( event );
|
|
|
|
if ( _MIDI_CommandLengths[ command ] > 0 ) {
|
|
GET_NEXT_EVENT( Track, c1 );
|
|
if ( _MIDI_CommandLengths[ command ] > 1 ) {
|
|
GET_NEXT_EVENT( Track, c2 );
|
|
}
|
|
}
|
|
|
|
if ( _MIDI_RerouteFunctions[ channel ] != NULL ) {
|
|
status = _MIDI_RerouteFunctions[ channel ]( event, c1, c2 );
|
|
|
|
if ( status == MIDI_DONT_PLAY ) {
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch ( command ) {
|
|
case MIDI_NOTE_OFF :
|
|
if ( _MIDI_Funcs->NoteOff ) {
|
|
_MIDI_Funcs->NoteOff( channel, c1, c2 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_NOTE_ON :
|
|
if ( _MIDI_Funcs->NoteOn ) {
|
|
_MIDI_Funcs->NoteOn( channel, c1, c2 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_POLY_AFTER_TCH :
|
|
if ( _MIDI_Funcs->PolyAftertouch ) {
|
|
_MIDI_Funcs->PolyAftertouch( channel, c1, c2 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_CONTROL_CHANGE :
|
|
TimeSet = _MIDI_InterpretControllerInfo( Track, TimeSet, channel, c1, c2 );
|
|
break;
|
|
|
|
case MIDI_PROGRAM_CHANGE :
|
|
if ( ( _MIDI_Funcs->ProgramChange ) && ( !Track->EMIDI_ProgramChange ) ) {
|
|
_MIDI_Funcs->ProgramChange( channel, MIDI_PatchMap[ c1 & 0x7f ] );
|
|
}
|
|
break;
|
|
|
|
case MIDI_AFTER_TOUCH :
|
|
if ( _MIDI_Funcs->ChannelAftertouch ) {
|
|
_MIDI_Funcs->ChannelAftertouch( channel, c1 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_PITCH_BEND :
|
|
if ( _MIDI_Funcs->PitchBend ) {
|
|
_MIDI_Funcs->PitchBend( channel, c1, c2 );
|
|
}
|
|
break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
}
|
|
|
|
Track->delay--;
|
|
Track++;
|
|
tracknum++;
|
|
|
|
if ( _MIDI_ActiveTracks == 0 ) {
|
|
_MIDI_ResetTracks();
|
|
if ( _MIDI_Loop ) {
|
|
tracknum = 0;
|
|
Track = _MIDI_TrackPtr;
|
|
} else {
|
|
_MIDI_SongActive = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
_MIDI_AdvanceTick();
|
|
_MIDI_GlobalPositionInTicks++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_SendControlChange
|
|
|
|
Sends a control change to the proper device
|
|
---------------------------------------------------------------------*/
|
|
|
|
static int _MIDI_SendControlChange
|
|
(
|
|
int channel,
|
|
int c1,
|
|
int c2
|
|
)
|
|
|
|
{
|
|
int status;
|
|
|
|
if ( _MIDI_RerouteFunctions[ channel ] != NULL )
|
|
{
|
|
status = _MIDI_RerouteFunctions[ channel ]( 0xB0 + channel,
|
|
c1, c2 );
|
|
if ( status == MIDI_DONT_PLAY )
|
|
{
|
|
return( MIDI_Ok );
|
|
}
|
|
}
|
|
|
|
if ( _MIDI_Funcs == NULL )
|
|
{
|
|
return( MIDI_Error );
|
|
}
|
|
|
|
if ( _MIDI_Funcs->ControlChange == NULL )
|
|
{
|
|
return( MIDI_Error );
|
|
}
|
|
|
|
_MIDI_Funcs->ControlChange( channel, c1, c2 );
|
|
|
|
return( MIDI_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_RerouteMidiChannel
|
|
|
|
Sets callback function to reroute MIDI commands from specified
|
|
function.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_RerouteMidiChannel
|
|
(
|
|
int channel,
|
|
int ( *function )( int event, int c1, int c2 )
|
|
)
|
|
|
|
{
|
|
if ( ( channel >= 1 ) && ( channel <= 16 ) )
|
|
{
|
|
_MIDI_RerouteFunctions[ channel - 1 ] = function;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_AllNotesOff
|
|
|
|
Sends all notes off commands on all midi channels.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_AllNotesOff
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int channel;
|
|
|
|
for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
|
|
{
|
|
_MIDI_SendControlChange( channel, 0x40, 0 );
|
|
_MIDI_SendControlChange( channel, MIDI_ALL_NOTES_OFF, 0 );
|
|
_MIDI_SendControlChange( channel, 0x78, 0 );
|
|
}
|
|
|
|
return( MIDI_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_SetChannelVolume
|
|
|
|
Sets the volume of the specified midi channel.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_SetChannelVolume
|
|
(
|
|
int channel,
|
|
int volume
|
|
)
|
|
|
|
{
|
|
int status;
|
|
int remotevolume;
|
|
|
|
_MIDI_ChannelVolume[ channel ] = volume;
|
|
|
|
if ( _MIDI_RerouteFunctions[ channel ] != NULL )
|
|
{
|
|
remotevolume = volume * _MIDI_TotalVolume;
|
|
remotevolume *= _MIDI_UserChannelVolume[ channel ];
|
|
remotevolume /= MIDI_MaxVolume;
|
|
remotevolume >>= 8;
|
|
|
|
status = _MIDI_RerouteFunctions[ channel ]( 0xB0 + channel,
|
|
MIDI_VOLUME, remotevolume );
|
|
if ( status == MIDI_DONT_PLAY )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( _MIDI_Funcs == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( _MIDI_Funcs->ControlChange == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// For user volume
|
|
volume *= _MIDI_UserChannelVolume[ channel ];
|
|
|
|
if ( _MIDI_Funcs->SetVolume == NULL )
|
|
{
|
|
volume *= _MIDI_TotalVolume;
|
|
volume /= MIDI_MaxVolume;
|
|
}
|
|
|
|
// For user volume
|
|
volume >>= 8;
|
|
|
|
_MIDI_Funcs->ControlChange( channel, MIDI_VOLUME, volume );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetUserChannelVolume
|
|
|
|
Sets the volume of the specified midi channel.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetUserChannelVolume
|
|
(
|
|
int channel,
|
|
int volume
|
|
)
|
|
|
|
{
|
|
// Convert channel from 1-16 to 0-15
|
|
channel--;
|
|
|
|
volume = max( 0, volume );
|
|
volume = min( volume, 256 );
|
|
|
|
if ( ( channel >= 0 ) && ( channel < NUM_MIDI_CHANNELS ) )
|
|
{
|
|
_MIDI_UserChannelVolume[ channel ] = volume;
|
|
_MIDI_SetChannelVolume( channel, _MIDI_ChannelVolume[ channel ] );
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_ResetUserChannelVolume
|
|
|
|
Sets the volume of the specified midi channel.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_ResetUserChannelVolume
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int channel;
|
|
|
|
for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
|
|
{
|
|
_MIDI_UserChannelVolume[ channel ] = 256;
|
|
}
|
|
|
|
_MIDI_SendChannelVolumes();
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_SendChannelVolumes
|
|
|
|
Sets the volume on all the midi channels.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_SendChannelVolumes
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int channel;
|
|
|
|
for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
|
|
{
|
|
_MIDI_SetChannelVolume( channel, _MIDI_ChannelVolume[ channel ] );
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_Reset
|
|
|
|
Resets the MIDI device to General Midi defaults.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_Reset
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int channel;
|
|
long time;
|
|
unsigned flags;
|
|
|
|
MIDI_AllNotesOff();
|
|
|
|
for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
|
|
{
|
|
_MIDI_SendControlChange( channel, MIDI_RESET_ALL_CONTROLLERS, 0 );
|
|
_MIDI_SendControlChange( channel, MIDI_RPN_MSB, MIDI_PITCHBEND_MSB );
|
|
_MIDI_SendControlChange( channel, MIDI_RPN_LSB, MIDI_PITCHBEND_LSB );
|
|
_MIDI_SendControlChange( channel, MIDI_DATAENTRY_MSB, 2 ); /* Pitch Bend Sensitivity MSB */
|
|
_MIDI_SendControlChange( channel, MIDI_DATAENTRY_LSB, 0 ); /* Pitch Bend Sensitivity LSB */
|
|
_MIDI_ChannelVolume[ channel ] = GENMIDI_DefaultVolume;
|
|
}
|
|
|
|
_MIDI_SendChannelVolumes();
|
|
|
|
Reset = TRUE;
|
|
|
|
return( MIDI_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetVolume
|
|
|
|
Sets the total volume of the music.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_SetVolume
|
|
(
|
|
int volume
|
|
)
|
|
|
|
{
|
|
int i;
|
|
|
|
if ( _MIDI_Funcs == NULL )
|
|
{
|
|
return( MIDI_NullMidiModule );
|
|
}
|
|
|
|
volume = min( MIDI_MaxVolume, volume );
|
|
volume = max( 0, volume );
|
|
|
|
_MIDI_TotalVolume = volume;
|
|
|
|
if ( _MIDI_Funcs->SetVolume )
|
|
{
|
|
_MIDI_Funcs->SetVolume( volume );
|
|
|
|
for( i = 0; i < NUM_MIDI_CHANNELS; i++ )
|
|
{
|
|
if ( _MIDI_RerouteFunctions[ i ] != NULL )
|
|
{
|
|
_MIDI_SetChannelVolume( i, _MIDI_ChannelVolume[ i ] );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_MIDI_SendChannelVolumes();
|
|
}
|
|
|
|
return( MIDI_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_GetVolume
|
|
|
|
Returns the total volume of the music.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_GetVolume
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int volume;
|
|
|
|
if ( _MIDI_Funcs == NULL )
|
|
{
|
|
return( MIDI_NullMidiModule );
|
|
}
|
|
|
|
if ( _MIDI_Funcs->GetVolume )
|
|
{
|
|
volume = _MIDI_Funcs->GetVolume();
|
|
}
|
|
else
|
|
{
|
|
volume = _MIDI_TotalVolume;
|
|
}
|
|
|
|
return( volume );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetContext
|
|
|
|
Sets the song context.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetContext
|
|
(
|
|
int context
|
|
)
|
|
|
|
{
|
|
if ( ( context > 0 ) && ( context < EMIDI_NUM_CONTEXTS ) )
|
|
{
|
|
_MIDI_Context = context;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_GetContext
|
|
|
|
Returns the current song context.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_GetContext
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return _MIDI_Context;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetLoopFlag
|
|
|
|
Sets whether the song should loop when finished or not.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetLoopFlag
|
|
(
|
|
int loopflag
|
|
)
|
|
|
|
{
|
|
_MIDI_Loop = loopflag;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_ContinueSong
|
|
|
|
Continues playback of a paused song.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_ContinueSong
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( _MIDI_SongLoaded )
|
|
{
|
|
_MIDI_SongActive = TRUE;
|
|
MPU_Unpause();
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_PauseSong
|
|
|
|
Pauses playback of the current song.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_PauseSong
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( _MIDI_SongLoaded )
|
|
{
|
|
_MIDI_SongActive = FALSE;
|
|
MIDI_AllNotesOff();
|
|
MPU_Pause();
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SongPlaying
|
|
|
|
Returns whether a song is playing or not.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_SongPlaying
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return( _MIDI_SongActive );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetMidiFuncs
|
|
|
|
Selects the routines that send the MIDI data to the music device.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetMidiFuncs
|
|
(
|
|
midifuncs *funcs
|
|
)
|
|
|
|
{
|
|
_MIDI_Funcs = funcs;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_StopSong
|
|
|
|
Stops playback of the currently playing song.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_StopSong
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( _MIDI_SongLoaded )
|
|
{
|
|
_MIDI_SongActive = FALSE;
|
|
_MIDI_SongLoaded = FALSE;
|
|
|
|
MIDI_Reset();
|
|
_MIDI_ResetTracks();
|
|
|
|
if ( _MIDI_Funcs->ReleasePatches )
|
|
{
|
|
_MIDI_Funcs->ReleasePatches();
|
|
}
|
|
|
|
USRHOOKS_FreeMem( _MIDI_TrackPtr );
|
|
|
|
_MIDI_TrackPtr = NULL;
|
|
_MIDI_NumTracks = 0;
|
|
_MIDI_TrackMemSize = 0;
|
|
|
|
_MIDI_TotalTime = 0;
|
|
_MIDI_TotalTicks = 0;
|
|
_MIDI_TotalBeats = 0;
|
|
_MIDI_TotalMeasures = 0;
|
|
|
|
MPU_Reset();
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_PlaySong
|
|
|
|
Begins playback of a MIDI song.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_PlaySong
|
|
(
|
|
unsigned char *song,
|
|
int loopflag
|
|
)
|
|
|
|
{
|
|
int numtracks;
|
|
int format;
|
|
long headersize;
|
|
long tracklength;
|
|
track *CurrentTrack;
|
|
unsigned char *ptr;
|
|
int status;
|
|
DWORD i;
|
|
|
|
if ( _MIDI_SongLoaded )
|
|
{
|
|
MIDI_StopSong();
|
|
}
|
|
|
|
MPU_Init(MUSIC_SoundDevice);
|
|
|
|
_MIDI_Loop = loopflag;
|
|
|
|
if ( _MIDI_Funcs == NULL )
|
|
{
|
|
return( MIDI_NullMidiModule );
|
|
}
|
|
|
|
if ( *( unsigned long * )song != MIDI_HEADER_SIGNATURE )
|
|
{
|
|
return( MIDI_InvalidMidiFile );
|
|
}
|
|
|
|
song += 4;
|
|
|
|
headersize = _MIDI_ReadNumber( song, 4 );
|
|
song += 4;
|
|
format = _MIDI_ReadNumber( song, 2 );
|
|
_MIDI_NumTracks = _MIDI_ReadNumber( song + 2, 2 );
|
|
_MIDI_Division = _MIDI_ReadNumber( song + 4, 2 );
|
|
if ( _MIDI_Division < 0 )
|
|
{
|
|
// If a SMPTE time division is given, just set to 96 so no errors occur
|
|
_MIDI_Division = 96;
|
|
}
|
|
|
|
if ( format > MAX_FORMAT )
|
|
{
|
|
return( MIDI_UnknownMidiFormat );
|
|
}
|
|
|
|
ptr = song + headersize;
|
|
|
|
if ( _MIDI_NumTracks == 0 )
|
|
{
|
|
return( MIDI_NoTracks );
|
|
}
|
|
|
|
_MIDI_TrackMemSize = _MIDI_NumTracks * sizeof( track );
|
|
status = USRHOOKS_GetMem( (void**)&_MIDI_TrackPtr, _MIDI_TrackMemSize );
|
|
if ( status != USRHOOKS_Ok )
|
|
{
|
|
return( MIDI_NoMemory );
|
|
}
|
|
|
|
CurrentTrack = _MIDI_TrackPtr;
|
|
numtracks = _MIDI_NumTracks;
|
|
while( numtracks-- )
|
|
{
|
|
if ( *( unsigned long * )ptr != MIDI_TRACK_SIGNATURE )
|
|
{
|
|
USRHOOKS_FreeMem( _MIDI_TrackPtr );
|
|
|
|
_MIDI_TrackPtr = NULL;
|
|
_MIDI_TrackMemSize = 0;
|
|
|
|
return( MIDI_InvalidTrack );
|
|
}
|
|
|
|
tracklength = _MIDI_ReadNumber( ptr + 4, 4 );
|
|
ptr += 8;
|
|
CurrentTrack->start = ptr;
|
|
ptr += tracklength;
|
|
CurrentTrack++;
|
|
}
|
|
|
|
if ( _MIDI_Funcs->GetVolume != NULL )
|
|
{
|
|
_MIDI_TotalVolume = _MIDI_Funcs->GetVolume();
|
|
}
|
|
|
|
_MIDI_InitEMIDI();
|
|
|
|
if ( _MIDI_Funcs->LoadPatch )
|
|
{
|
|
MIDI_LoadTimbres();
|
|
}
|
|
|
|
_MIDI_ResetTracks();
|
|
|
|
if ( !Reset )
|
|
{
|
|
MIDI_Reset();
|
|
}
|
|
|
|
Reset = FALSE;
|
|
|
|
MIDI_SetDivision( _MIDI_Division );
|
|
//MIDI_SetTempo( 120 );
|
|
|
|
_MIDI_SongLoaded = TRUE;
|
|
_MIDI_SongActive = TRUE;
|
|
|
|
while (_MPU_BuffersWaiting < 4) _MIDI_ServiceRoutine();
|
|
MPU_BeginPlayback();
|
|
|
|
return( MIDI_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetTempo
|
|
|
|
Sets the song tempo.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetTempo
|
|
(
|
|
int tempo
|
|
)
|
|
|
|
{
|
|
long tickspersecond;
|
|
|
|
MIDI_Tempo = tempo;
|
|
tickspersecond = ( ( tempo ) * _MIDI_Division ) / 60;
|
|
_MIDI_FPSecondsPerTick = ( 1 << TIME_PRECISION ) / tickspersecond;
|
|
MPU_SetTempo(tempo);
|
|
}
|
|
|
|
void MIDI_SetDivision(int division)
|
|
{
|
|
MPU_SetDivision(division);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_GetTempo
|
|
|
|
Returns the song tempo.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MIDI_GetTempo
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return( MIDI_Tempo );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: _MIDI_ProcessNextTick
|
|
|
|
Sets the position of the song pointer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static int _MIDI_ProcessNextTick
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int event;
|
|
int channel;
|
|
int command;
|
|
track *Track;
|
|
int tracknum;
|
|
int status;
|
|
int c1;
|
|
int c2;
|
|
int TimeSet = FALSE;
|
|
|
|
Track = _MIDI_TrackPtr;
|
|
tracknum = 0;
|
|
while( ( tracknum < _MIDI_NumTracks ) && ( Track != NULL ) )
|
|
{
|
|
while ( ( Track->active ) && ( Track->delay == 0 ) )
|
|
{
|
|
GET_NEXT_EVENT( Track, event );
|
|
|
|
if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
|
|
{
|
|
switch( event )
|
|
{
|
|
case MIDI_SYSEX :
|
|
case MIDI_SYSEX_CONTINUE :
|
|
_MIDI_SysEx( Track );
|
|
break;
|
|
|
|
case MIDI_META_EVENT :
|
|
_MIDI_MetaEvent( Track );
|
|
break;
|
|
}
|
|
|
|
if ( Track->active )
|
|
{
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( event & MIDI_RUNNING_STATUS )
|
|
{
|
|
Track->RunningStatus = event;
|
|
}
|
|
else
|
|
{
|
|
event = Track->RunningStatus;
|
|
Track->pos--;
|
|
}
|
|
|
|
channel = GET_MIDI_CHANNEL( event );
|
|
command = GET_MIDI_COMMAND( event );
|
|
|
|
if ( _MIDI_CommandLengths[ command ] > 0 )
|
|
{
|
|
GET_NEXT_EVENT( Track, c1 );
|
|
if ( _MIDI_CommandLengths[ command ] > 1 )
|
|
{
|
|
GET_NEXT_EVENT( Track, c2 );
|
|
}
|
|
}
|
|
|
|
if ( _MIDI_RerouteFunctions[ channel ] != NULL )
|
|
{
|
|
status = _MIDI_RerouteFunctions[ channel ]( event, c1, c2 );
|
|
|
|
if ( status == MIDI_DONT_PLAY )
|
|
{
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch ( command )
|
|
{
|
|
case MIDI_NOTE_OFF :
|
|
break;
|
|
|
|
case MIDI_NOTE_ON :
|
|
break;
|
|
|
|
case MIDI_POLY_AFTER_TCH :
|
|
if ( _MIDI_Funcs->PolyAftertouch )
|
|
{
|
|
_MIDI_Funcs->PolyAftertouch( channel, c1, c2 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_CONTROL_CHANGE :
|
|
TimeSet = _MIDI_InterpretControllerInfo( Track, TimeSet,
|
|
channel, c1, c2 );
|
|
break;
|
|
|
|
case MIDI_PROGRAM_CHANGE :
|
|
if ( ( _MIDI_Funcs->ProgramChange ) &&
|
|
( !Track->EMIDI_ProgramChange ) )
|
|
{
|
|
_MIDI_Funcs->ProgramChange( channel, c1 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_AFTER_TOUCH :
|
|
if ( _MIDI_Funcs->ChannelAftertouch )
|
|
{
|
|
_MIDI_Funcs->ChannelAftertouch( channel, c1 );
|
|
}
|
|
break;
|
|
|
|
case MIDI_PITCH_BEND :
|
|
if ( _MIDI_Funcs->PitchBend )
|
|
{
|
|
_MIDI_Funcs->PitchBend( channel, c1, c2 );
|
|
}
|
|
break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
}
|
|
|
|
Track->delay--;
|
|
Track++;
|
|
tracknum++;
|
|
|
|
if ( _MIDI_ActiveTracks == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
_MIDI_AdvanceTick();
|
|
|
|
return( TimeSet );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetSongTick
|
|
|
|
Sets the position of the song pointer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetSongTick
|
|
(
|
|
unsigned long PositionInTicks
|
|
)
|
|
|
|
{
|
|
if ( !_MIDI_SongLoaded )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MIDI_PauseSong();
|
|
|
|
if ( PositionInTicks < _MIDI_PositionInTicks )
|
|
{
|
|
_MIDI_ResetTracks();
|
|
MIDI_Reset();
|
|
}
|
|
|
|
while( _MIDI_PositionInTicks < PositionInTicks )
|
|
{
|
|
if ( _MIDI_ProcessNextTick() )
|
|
{
|
|
break;
|
|
}
|
|
if ( _MIDI_ActiveTracks == 0 )
|
|
{
|
|
_MIDI_ResetTracks();
|
|
if ( !_MIDI_Loop )
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
MIDI_SetVolume( _MIDI_TotalVolume );
|
|
MIDI_ContinueSong();
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetSongTime
|
|
|
|
Sets the position of the song pointer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetSongTime
|
|
(
|
|
unsigned long milliseconds
|
|
)
|
|
|
|
{
|
|
unsigned long mil;
|
|
unsigned long sec;
|
|
unsigned long newtime;
|
|
|
|
if ( !_MIDI_SongLoaded )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MIDI_PauseSong();
|
|
|
|
mil = ( ( milliseconds % 1000 ) << TIME_PRECISION ) / 1000;
|
|
sec = ( milliseconds / 1000 ) << TIME_PRECISION;
|
|
newtime = sec + mil;
|
|
|
|
if ( newtime < _MIDI_Time )
|
|
{
|
|
_MIDI_ResetTracks();
|
|
MIDI_Reset();
|
|
}
|
|
|
|
while( _MIDI_Time < newtime )
|
|
{
|
|
if ( _MIDI_ProcessNextTick() )
|
|
{
|
|
break;
|
|
}
|
|
if ( _MIDI_ActiveTracks == 0 )
|
|
{
|
|
_MIDI_ResetTracks();
|
|
if ( !_MIDI_Loop )
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
MIDI_SetVolume( _MIDI_TotalVolume );
|
|
MIDI_ContinueSong();
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_SetSongPosition
|
|
|
|
Sets the position of the song pointer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_SetSongPosition
|
|
(
|
|
int measure,
|
|
int beat,
|
|
int tick
|
|
)
|
|
|
|
{
|
|
unsigned long pos;
|
|
|
|
if ( !_MIDI_SongLoaded )
|
|
{
|
|
return;
|
|
}
|
|
|
|
MIDI_PauseSong();
|
|
|
|
pos = RELATIVE_BEAT( measure, beat, tick );
|
|
|
|
if ( pos < (unsigned long)RELATIVE_BEAT( _MIDI_Measure, _MIDI_Beat, _MIDI_Tick ) )
|
|
{
|
|
_MIDI_ResetTracks();
|
|
MIDI_Reset();
|
|
}
|
|
|
|
while( (unsigned long)RELATIVE_BEAT( _MIDI_Measure, _MIDI_Beat, _MIDI_Tick ) < pos )
|
|
{
|
|
if ( _MIDI_ProcessNextTick() )
|
|
{
|
|
break;
|
|
}
|
|
if ( _MIDI_ActiveTracks == 0 )
|
|
{
|
|
_MIDI_ResetTracks();
|
|
if ( !_MIDI_Loop )
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
MIDI_SetVolume( _MIDI_TotalVolume );
|
|
MIDI_ContinueSong();
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_GetSongPosition
|
|
|
|
Returns the position of the song pointer in Measures, beats, ticks.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_GetSongPosition
|
|
(
|
|
songposition *pos
|
|
)
|
|
|
|
{
|
|
unsigned long mil;
|
|
unsigned long sec;
|
|
|
|
mil = ( _MIDI_Time & ( ( 1 << TIME_PRECISION ) - 1 ) ) * 1000;
|
|
sec = _MIDI_Time >> TIME_PRECISION;
|
|
pos->milliseconds = ( mil >> TIME_PRECISION ) + ( sec * 1000 );
|
|
pos->tickposition = _MIDI_PositionInTicks;
|
|
pos->measure = _MIDI_Measure;
|
|
pos->beat = _MIDI_Beat;
|
|
pos->tick = _MIDI_Tick;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_GetSongLength
|
|
|
|
Returns the length of the song.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_GetSongLength
|
|
(
|
|
songposition *pos
|
|
)
|
|
|
|
{
|
|
unsigned long mil;
|
|
unsigned long sec;
|
|
|
|
mil = ( _MIDI_TotalTime & ( ( 1 << TIME_PRECISION ) - 1 ) ) * 1000;
|
|
sec = _MIDI_TotalTime >> TIME_PRECISION;
|
|
|
|
pos->milliseconds = ( mil >> TIME_PRECISION ) + ( sec * 1000 );
|
|
pos->measure = _MIDI_TotalMeasures;
|
|
pos->beat = _MIDI_TotalBeats;
|
|
pos->tick = _MIDI_TotalTicks;
|
|
pos->tickposition = 0;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_InitEMIDI
|
|
|
|
Sets up the EMIDI
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void _MIDI_InitEMIDI
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int event;
|
|
int command;
|
|
int channel;
|
|
int length;
|
|
int IncludeFound;
|
|
track *Track;
|
|
int tracknum;
|
|
int type;
|
|
int c1;
|
|
int c2;
|
|
|
|
type = EMIDI_GeneralMIDI;
|
|
|
|
_MIDI_ResetTracks();
|
|
|
|
_MIDI_TotalTime = 0;
|
|
_MIDI_TotalTicks = 0;
|
|
_MIDI_TotalBeats = 0;
|
|
_MIDI_TotalMeasures = 0;
|
|
|
|
Track = _MIDI_TrackPtr;
|
|
tracknum = 0;
|
|
while( ( tracknum < _MIDI_NumTracks ) && ( Track != NULL ) )
|
|
{
|
|
_MIDI_Tick = 0;
|
|
_MIDI_Beat = 1;
|
|
_MIDI_Measure = 1;
|
|
_MIDI_Time = 0;
|
|
_MIDI_BeatsPerMeasure = 4;
|
|
_MIDI_TicksPerBeat = _MIDI_Division;
|
|
_MIDI_TimeBase = 4;
|
|
|
|
_MIDI_PositionInTicks = 0;
|
|
_MIDI_ActiveTracks = 0;
|
|
_MIDI_Context = -1;
|
|
|
|
Track->RunningStatus = 0;
|
|
Track->active = TRUE;
|
|
|
|
Track->EMIDI_ProgramChange = FALSE;
|
|
Track->EMIDI_VolumeChange = FALSE;
|
|
Track->EMIDI_IncludeTrack = TRUE;
|
|
|
|
memset( Track->context, 0, sizeof( Track->context ) );
|
|
|
|
while( Track->delay > 0 )
|
|
{
|
|
_MIDI_AdvanceTick();
|
|
Track->delay--;
|
|
}
|
|
|
|
IncludeFound = FALSE;
|
|
while ( Track->active )
|
|
{
|
|
GET_NEXT_EVENT( Track, event );
|
|
|
|
if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
|
|
{
|
|
switch( event )
|
|
{
|
|
case MIDI_SYSEX :
|
|
case MIDI_SYSEX_CONTINUE :
|
|
_MIDI_SysEx( Track );
|
|
break;
|
|
|
|
case MIDI_META_EVENT :
|
|
_MIDI_MetaEvent( Track );
|
|
break;
|
|
}
|
|
|
|
if ( Track->active )
|
|
{
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
while( Track->delay > 0 )
|
|
{
|
|
_MIDI_AdvanceTick();
|
|
Track->delay--;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( event & MIDI_RUNNING_STATUS )
|
|
{
|
|
Track->RunningStatus = event;
|
|
}
|
|
else
|
|
{
|
|
event = Track->RunningStatus;
|
|
Track->pos--;
|
|
}
|
|
|
|
channel = GET_MIDI_CHANNEL( event );
|
|
command = GET_MIDI_COMMAND( event );
|
|
length = _MIDI_CommandLengths[ command ];
|
|
|
|
if ( command == MIDI_CONTROL_CHANGE )
|
|
{
|
|
if ( *Track->pos == MIDI_MONO_MODE_ON )
|
|
{
|
|
length++;
|
|
}
|
|
GET_NEXT_EVENT( Track, c1 );
|
|
GET_NEXT_EVENT( Track, c2 );
|
|
length -= 2;
|
|
|
|
switch( c1 )
|
|
{
|
|
case EMIDI_LOOP_START :
|
|
case EMIDI_SONG_LOOP_START :
|
|
if ( c2 == 0 )
|
|
{
|
|
Track->context[ 0 ].loopcount = EMIDI_INFINITE;
|
|
}
|
|
else
|
|
{
|
|
Track->context[ 0 ].loopcount = c2;
|
|
}
|
|
|
|
Track->context[ 0 ].pos = Track->pos;
|
|
Track->context[ 0 ].loopstart = Track->pos;
|
|
Track->context[ 0 ].RunningStatus = Track->RunningStatus;
|
|
Track->context[ 0 ].time = _MIDI_Time;
|
|
Track->context[ 0 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
|
|
Track->context[ 0 ].tick = _MIDI_Tick;
|
|
Track->context[ 0 ].beat = _MIDI_Beat;
|
|
Track->context[ 0 ].measure = _MIDI_Measure;
|
|
Track->context[ 0 ].BeatsPerMeasure = _MIDI_BeatsPerMeasure;
|
|
Track->context[ 0 ].TicksPerBeat = _MIDI_TicksPerBeat;
|
|
Track->context[ 0 ].TimeBase = _MIDI_TimeBase;
|
|
break;
|
|
|
|
case EMIDI_LOOP_END :
|
|
case EMIDI_SONG_LOOP_END :
|
|
if ( c2 == EMIDI_END_LOOP_VALUE )
|
|
{
|
|
Track->context[ 0 ].loopstart = NULL;
|
|
Track->context[ 0 ].loopcount = 0;
|
|
}
|
|
break;
|
|
|
|
case EMIDI_INCLUDE_TRACK :
|
|
if ( EMIDI_AffectsCurrentCard( c2, type ) )
|
|
{
|
|
//printf( "Include track %d on card %d\n", tracknum, c2 );
|
|
IncludeFound = TRUE;
|
|
Track->EMIDI_IncludeTrack = TRUE;
|
|
}
|
|
else if ( !IncludeFound )
|
|
{
|
|
//printf( "Track excluded %d on card %d\n", tracknum, c2 );
|
|
IncludeFound = TRUE;
|
|
Track->EMIDI_IncludeTrack = FALSE;
|
|
}
|
|
break;
|
|
|
|
case EMIDI_EXCLUDE_TRACK :
|
|
if ( EMIDI_AffectsCurrentCard( c2, type ) )
|
|
{
|
|
//printf( "Exclude track %d on card %d\n", tracknum, c2 );
|
|
Track->EMIDI_IncludeTrack = FALSE;
|
|
}
|
|
break;
|
|
|
|
case EMIDI_PROGRAM_CHANGE :
|
|
if ( !Track->EMIDI_ProgramChange )
|
|
//printf( "Program change on track %d\n", tracknum );
|
|
Track->EMIDI_ProgramChange = TRUE;
|
|
break;
|
|
|
|
case EMIDI_VOLUME_CHANGE :
|
|
if ( !Track->EMIDI_VolumeChange )
|
|
//printf( "Volume change on track %d\n", tracknum );
|
|
Track->EMIDI_VolumeChange = TRUE;
|
|
break;
|
|
|
|
case EMIDI_CONTEXT_START :
|
|
if ( ( c2 > 0 ) && ( c2 < EMIDI_NUM_CONTEXTS ) )
|
|
{
|
|
Track->context[ c2 ].pos = Track->pos;
|
|
Track->context[ c2 ].loopstart = Track->context[ 0 ].loopstart;
|
|
Track->context[ c2 ].loopcount = Track->context[ 0 ].loopcount;
|
|
Track->context[ c2 ].RunningStatus = Track->RunningStatus;
|
|
Track->context[ c2 ].time = _MIDI_Time;
|
|
Track->context[ c2 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
|
|
Track->context[ c2 ].tick = _MIDI_Tick;
|
|
Track->context[ c2 ].beat = _MIDI_Beat;
|
|
Track->context[ c2 ].measure = _MIDI_Measure;
|
|
Track->context[ c2 ].BeatsPerMeasure = _MIDI_BeatsPerMeasure;
|
|
Track->context[ c2 ].TicksPerBeat = _MIDI_TicksPerBeat;
|
|
Track->context[ c2 ].TimeBase = _MIDI_TimeBase;
|
|
}
|
|
break;
|
|
|
|
case EMIDI_CONTEXT_END :
|
|
break;
|
|
}
|
|
}
|
|
|
|
Track->pos += length;
|
|
Track->delay = _MIDI_ReadDelta( Track );
|
|
|
|
while( Track->delay > 0 )
|
|
{
|
|
_MIDI_AdvanceTick();
|
|
Track->delay--;
|
|
}
|
|
}
|
|
|
|
_MIDI_TotalTime = max( _MIDI_TotalTime, _MIDI_Time );
|
|
if ( RELATIVE_BEAT( _MIDI_Measure, _MIDI_Beat, _MIDI_Tick ) >
|
|
RELATIVE_BEAT( _MIDI_TotalMeasures, _MIDI_TotalBeats,
|
|
_MIDI_TotalTicks ) )
|
|
{
|
|
_MIDI_TotalTicks = _MIDI_Tick;
|
|
_MIDI_TotalBeats = _MIDI_Beat;
|
|
_MIDI_TotalMeasures = _MIDI_Measure;
|
|
}
|
|
|
|
Track++;
|
|
tracknum++;
|
|
}
|
|
|
|
_MIDI_ResetTracks();
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MIDI_LoadTimbres
|
|
|
|
Preloads the timbres on cards that use patch-caching.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MIDI_LoadTimbres
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int event;
|
|
int command;
|
|
int channel;
|
|
int length;
|
|
int Finished;
|
|
track *Track;
|
|
int tracknum;
|
|
|
|
Track = _MIDI_TrackPtr;
|
|
tracknum = 0;
|
|
while( ( tracknum < _MIDI_NumTracks ) && ( Track != NULL ) )
|
|
{
|
|
Finished = FALSE;
|
|
while ( !Finished )
|
|
{
|
|
GET_NEXT_EVENT( Track, event );
|
|
|
|
if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
|
|
{
|
|
switch( event )
|
|
{
|
|
case MIDI_SYSEX :
|
|
case MIDI_SYSEX_CONTINUE :
|
|
length = _MIDI_ReadDelta( Track );
|
|
Track->pos += length;
|
|
break;
|
|
|
|
case MIDI_META_EVENT :
|
|
GET_NEXT_EVENT( Track, command );
|
|
GET_NEXT_EVENT( Track, length );
|
|
|
|
if ( command == MIDI_END_OF_TRACK )
|
|
{
|
|
Finished = TRUE;
|
|
}
|
|
|
|
Track->pos += length;
|
|
break;
|
|
}
|
|
|
|
if ( !Finished )
|
|
{
|
|
_MIDI_ReadDelta( Track );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( event & MIDI_RUNNING_STATUS )
|
|
{
|
|
Track->RunningStatus = event;
|
|
}
|
|
else
|
|
{
|
|
event = Track->RunningStatus;
|
|
Track->pos--;
|
|
}
|
|
|
|
channel = GET_MIDI_CHANNEL( event );
|
|
command = GET_MIDI_COMMAND( event );
|
|
length = _MIDI_CommandLengths[ command ];
|
|
|
|
if ( command == MIDI_CONTROL_CHANGE )
|
|
{
|
|
if ( *Track->pos == MIDI_MONO_MODE_ON )
|
|
{
|
|
length++;
|
|
}
|
|
|
|
if ( *Track->pos == EMIDI_PROGRAM_CHANGE )
|
|
{
|
|
_MIDI_Funcs->LoadPatch( *( Track->pos + 1 ) );
|
|
}
|
|
}
|
|
|
|
if ( channel == MIDI_RHYTHM_CHANNEL )
|
|
{
|
|
if ( command == MIDI_NOTE_ON )
|
|
{
|
|
_MIDI_Funcs->LoadPatch( 128 + *Track->pos );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( command == MIDI_PROGRAM_CHANGE )
|
|
{
|
|
_MIDI_Funcs->LoadPatch( *Track->pos );
|
|
}
|
|
}
|
|
Track->pos += length;
|
|
_MIDI_ReadDelta( Track );
|
|
}
|
|
Track++;
|
|
tracknum++;
|
|
}
|
|
|
|
_MIDI_ResetTracks();
|
|
}
|
|
|
|
|
|
void MIDI_UpdateMusic(void)
|
|
{
|
|
if (!_MIDI_SongLoaded || !_MIDI_SongActive) return;
|
|
while (_MPU_BuffersWaiting < 4) _MIDI_ServiceRoutine();
|
|
}
|
|
|