⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lkeymidi.cpp

📁 ldraw_DOS游戏开发包
💻 CPP
📖 第 1 页 / 共 4 页
字号:
/**************************************************************************
 *  LinDevelop v1.2
 *  The core MIDI file player.
 *  written by Lin Wei, 2000
 **************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <limits.h>
#include <lkey.h>
#include <lsys.h>

#define TRUE  1
#define FALSE 0
#define MIDI_LAYERS   4
#define MIDI_TRACKS  32
////////////////////////////////////////////////////////////////////////////
char   *lmMidiMapFile[2]={ "","" };
#define TIMERS_PER_SECOND     1193181L
#define SECS_TO_TIMER(x)      ((long)(x) * TIMERS_PER_SECOND)
#define MSEC_TO_TIMER(x)      ((long)(x) * (TIMERS_PER_SECOND / 1000))
#define BPS_TO_TIMER(x)       (TIMERS_PER_SECOND / (long)(x))
#define BPM_TO_TIMER(x)       ((60 * TIMERS_PER_SECOND) / (long)(x))

typedef struct MIDI_DRIVER             /* driver for playing midi music */
{
   char *name;                         /* driver name */
   char *desc;                         /* description string */
   int  voices;                        /* available voices */
   int  basevoice;                     /* voice number offset */
   int  max_voices;                    /* maximum voices we can support */
   int  def_voices;                    /* default number of voices to use */
   int  xmin, xmax;                    /* reserved voice range */

   /* setup routines */
   int  (*detect)();
   int  (*init)(int voices);
   void (*exit)();
   int  (*mixer_volume)(int volume);

   /* raw MIDI output to MPU-401, etc. */
   void (*raw_midi)(unsigned char data);

   /* dynamic patch loading routines */
   int  (*load_patches)(char *patches, char *drums);
   void (*adjust_patches)(char *patches, char *drums);

   /* note control functions */
   void (*key_on)(int inst, int note, int bend, int vol, int pan);
   void (*key_off)(int voice);
   void (*set_volume)(int voice, int vol);
   void (*set_pitch)(int voice, int note, int bend);
   void (*set_pan)(int voice, int pan);
   void (*set_vibrato)(int voice, int amount);
} MIDI_DRIVER;
extern volatile long _midi_tick;
extern MIDI_DRIVER *midi_driver;
#define MIDI_NONE    0
#define MIDI_OPL2    1
#define MIDI_OPL3    2
#define MIDI_2XOPL2  3
////////////////////////////////////////////////////////////////////////////
typedef struct MIDI_TRACK                       /* a track in the MIDI file */
{
   unsigned char *pos;                          /* position in track data */
   long timer;                                  /* time until next event */
   unsigned char running_status;                /* last MIDI event */
} MIDI_TRACK;
typedef struct MIDI_CHANNEL                     /* a MIDI channel */
{
   int patch;                                   /* current sound */
   int volume;                                  /* volume controller */
   int pan;                                     /* pan position */
   int pitch_bend;                              /* pitch bend position */
   int new_volume;                              /* cached volume change */
   int new_pitch_bend;                          /* cached pitch bend */
   int note[128][MIDI_LAYERS];                  /* status of each note */
} MIDI_CHANNEL;
typedef struct MIDI_VOICE                         /* a voice on the soundcard */
{
   int channel;                                 /* MIDI channel */
   int note;                                    /* note (-1 = off) */
   int volume;                                  /* note velocity */
   int time;                                   /* when note was triggered */
} MIDI_VOICE;
typedef struct WAITING_NOTE                     /* a stored note-on request */
{
   int channel;
   int note;
   int volume;
} WAITING_NOTE;
typedef struct PATCH_TABLE                      /* GM -> external synth */
{
   int bank1;                                   /* controller #0 */
   int bank2;                                   /* controller #32 */
   int prog;                                    /* program change */
   int pitch;                                   /* pitch shift */
} PATCH_TABLE;
volatile long midi_pos = -1;                    /* position in MIDI file */
static long midi_pos_counter;                   /* delta for midi_pos */
volatile long _midi_tick = 0;                   /* counter for killing notes */

static void midi_player();                      /* core MIDI player routine */
static void prepare_to_play(MIDI *midi);
static void midi_lock_mem();

static MIDI *midifile = NULL;                   /* the file that is playing */
static short midi_loop = 0;                     /* repeat at eof? */
long midi_loop_start = -1;                      /* where to loop back to */
long midi_loop_end = -1;                        /* loop at this position */
static int midi_semaphore = 0;                  /* reentrancy flag */
static int midi_loaded_patches = FALSE;         /* loaded entire patch set? */
static long  midi_timer_speed;                    /* midi_player's timer speed */
static int midi_pos_speed;                      /* MIDI delta -> midi_pos */
static int midi_speed;                          /* MIDI delta -> timer */
static int midi_new_speed;                      /* for tempo change events */
static int old_midi_volume = -1;                /* stored global volume */
short  lm_midi_volume=255;
static int midi_alloc_channel;                  /* so _midi_allocate_voice */
static int midi_alloc_note;                     /* knows which note the */
static int midi_alloc_vol;                      /* sound is associated with */

static MIDI_TRACK midi_track[MIDI_TRACKS];      /* the active tracks */
static MIDI_VOICE midi_voice[MIDI_VOICES];      /* synth voice status */
static MIDI_CHANNEL midi_channel[16];           /* MIDI channel info */
static WAITING_NOTE midi_waiting[MIDI_VOICES];  /* notes still to be played */
static PATCH_TABLE patch_table[128];            /* GM -> external synth */

static short midi_seeking;                        /* set during seeks */
static short midi_looping;                        /* set during loops */
void (*midi_msg_callback)(int msg, int byte1, int byte2) = NULL;
void (*midi_meta_callback)(int type, unsigned char *data, int length) = NULL;
void (*midi_sysex_callback)(unsigned char *data, int length) = NULL;
MIDI_DRIVER *midi_driver;
////////////////////////////////////////////////////////////////////////////
static char lmMidiFlag=0;
char  lm_error_msg[150];
char  lmMidiInit(char driver);
char  lmMidiRest();
void  lmFM_load_map(int choice,int drums);
int   lmFM_load_ibk(char *filename, int drums);
MIDI *lmLoadMidi(char *fname,long file_offset);
void  lmDestroyMidi(MIDI*);
int   lmMidiPlay(MIDI *midi, int loop);
void  lmStopMidi();
void  lmMidiPause();
int   lmMidiSeek(int target);
void  lmMidiResume();
////////////////////////////////////////////////////////////////////////////
static int  midi_init(char driver);
static void midi_exit();

char lmMidiInit(char driver)
{ if (lmMidiFlag) return 1;
  if (!lk_sb_port) { sprintf(lsys_message,"Sound card init failed\n"); return 0; }
  if (!midi_init(driver)) return FALSE;
  lmMidiFlag=1; return 1;
}
char lmMidiRest()
{ if (!lmMidiFlag) return 0;
  midi_exit(); 
  lmMidiFlag=0;
  return 1;
}
static int   mfgetw(FILE *f)
{ unsigned short w[2],i;
  for (i=0;i<2;i++) w[i]=fgetc(f); 
  return (unsigned)w[0]*256+w[1];
}
static long  mfgetl(FILE *f)
{ unsigned w[4],i; 
  for (i=0;i<4;i++) w[i]=fgetc(f); 
  return (w[0]*256+w[1])*65536L+w[2]*256+w[3];
}
void lmDestroyMidi(MIDI *);
void lmStopMidi() { lmMidiPlay(NULL, FALSE); }
MIDI *lmLoadMidi(char *filename,long file_offset)
{
   int c;
   char buf[256];
   long data,retsiz;
   FILE *fp;
   MIDI *midi;
   int num_tracks;

   fp = fopen(filename, "rb");        /* open the file */
   if (!fp) return NULL; 

   midi = (MIDI*)malloc(sizeof(MIDI));              /* get some memory */
   if (!midi) {
      fclose(fp); return NULL;
   }

   for (c=0; c<MIDI_TRACKS; c++) {
      midi->track[c].data = NULL;
      midi->track[c].len = 0;
   }
  
   fseek(fp, file_offset, SEEK_SET);
   fread(buf, 4, 1, fp);                   /* read midi header */
   if (memcmp(buf, "MThd", 4)) goto err; 
   mfgetl(fp);                            /* skip header chunk length */

   data = mfgetw(fp);                    /* MIDI file type */
   if ((data != 0) && (data != 1)) goto err; 
   num_tracks = mfgetw(fp);              /* number of tracks */
   if ((num_tracks < 1) || (num_tracks > MIDI_TRACKS)) goto err; 

   data = mfgetw(fp);                    /* beat divisions */
   midi->divisions = abs(data);

   for (c=0; c<num_tracks; c++) {            /* read each track */
      fread(buf, 4, 1, fp);                  /* read track header */
      if (memcmp(buf, "MTrk", 4)) goto err; 
      data = mfgetl(fp);                      /* length of track chunk */
      midi->track[c].len = data;
      midi->track[c].data = (unsigned char*)malloc(data);    /* allocate memory */
      if (!midi->track[c].data) goto err; 
					     /* finally, read track data */
      retsiz=fread(midi->track[c].data, 1, data, fp);
      if ( retsiz != data) goto err; 
   }

   fclose(fp);
   return midi;

   /* oh dear... */
   err:
   fclose(fp);
   lmDestroyMidi(midi);
   return NULL;
}
void lmDestroyMidi(MIDI *midi)
{
   int c;

   if (midi == midifile)
      lmStopMidi();

   if (midi) {
      for (c=0; c<MIDI_TRACKS; c++) {
	 if (midi->track[c].data) {
	    free(midi->track[c].data);
	 }
      }
      free(midi);
   }
}
static unsigned long parse_var_len(unsigned char **data)
{
   unsigned long val = **data & 0x7F;

   while (**data & 0x80) {
      (*data)++;
      val <<= 7;
      val += (**data & 0x7F);
   }

   (*data)++;
   return val;
}
/* global_volume_fix:
 *  Converts a note volume, adjusting it according to the global 
 *  _midi_volume variable.
 */
static inline int global_volume_fix(int vol)
{
   if (lm_midi_volume >= 0)
      return (vol * lm_midi_volume) / 256;

   return vol;
}
/* sort_out_volume:
 *  Converts a note volume, adjusting it according to the channel volume
 *  and the global _midi_volume variable.
 */
static inline int sort_out_volume(int c, int vol)
{
   return global_volume_fix((vol * midi_channel[c].volume) / 128);
}
/* raw_program_change:
 *  Sends a program change message to a device capable of handling raw
 *  MIDI data, using patch mapping tables. Assumes that midi_driver->raw_midi
 *  isn't NULL, so check before calling it!
 */
static void raw_program_change(int channel, int patch)
{
   if (channel != 9) {
      /* bank change #1 */
      if (patch_table[patch].bank1 >= 0) {
	 midi_driver->raw_midi(0xB0+channel);
	 midi_driver->raw_midi(0);
	 midi_driver->raw_midi(patch_table[patch].bank1);
      }

      /* bank change #2 */
      if (patch_table[patch].bank2 >= 0) {
	 midi_driver->raw_midi(0xB0+channel);
	 midi_driver->raw_midi(32);
	 midi_driver->raw_midi(patch_table[patch].bank2);
      }

      /* program change */
      midi_driver->raw_midi(0xC0+channel);
      midi_driver->raw_midi(patch_table[patch].prog);

      /* update volume */
      midi_driver->raw_midi(0xB0+channel);
      midi_driver->raw_midi(7);
      midi_driver->raw_midi(global_volume_fix(midi_channel[channel].volume-1));
   }
}
/* midi_note_off:
 *  Processes a MIDI note-off event.
 */
static void midi_note_off(int channel, int note)
{
   int done = FALSE;
   int voice, layer;
   int c;

   /* can we send raw MIDI data? */
   if (midi_driver->raw_midi) {
      if (channel != 9)
	 note += patch_table[midi_channel[channel].patch].pitch;

      midi_driver->raw_midi(0x80+channel);
      midi_driver->raw_midi(note);
      midi_driver->raw_midi(0);
      return;
   }

   /* oh well, have to do it the long way... */
   for (layer=0; layer<MIDI_LAYERS; layer++) {
      voice = midi_channel[channel].note[note][layer];
      if (voice >= 0) {
	 midi_driver->key_off(voice + midi_driver->basevoice);
	 midi_voice[voice].note = -1;
	 midi_voice[voice].time = _midi_tick;
	 midi_channel[channel].note[note][layer] = -1; 
	 done = TRUE;
      }
   }

   /* if the note isn't playing, it must still be in the waiting room */
   if (!done) {
      for (c=0; c<MIDI_VOICES; c++) {
	 if ((midi_waiting[c].channel == channel) && 
	     (midi_waiting[c].note == note)) {
	    midi_waiting[c].note = -1;
	    break;
	 }
      }
   }
}
/* sort_out_pitch_bend:
 *  MIDI pitch bend range is + or - two semitones. The low-level soundcard
 *  drivers can only handle bends up to +1 semitone. This routine converts
 *  pitch bends from MIDI format to our own format.
 */
static inline void sort_out_pitch_bend(int *bend, int *note)
{
   if (*bend == 0x2000) {
      *bend = 0;
      return;
   }

   (*bend) -= 0x2000;

   while (*bend < 0) {
      (*note)--;
      (*bend) += 0x1000;
   }

   while (*bend >= 0x1000) {
      (*note)++;
      (*bend) -= 0x1000;
   }
}



/* _midi_allocate_voice:
 *  Allocates a MIDI voice in the range min-max (inclusive). This is 
 *  intended to be called by the key_on() handlers in the MIDI driver, 
 *  and shouldn't be used by any other code.
 */
int lm_midi_allocate_voice(int min, int max)
{
   int c;
   int layer;
   int voice = -1;
   int best_time = LONG_MAX;

   if (min < 0)
      min = 0;

   if (max < 0)
      max = midi_driver->voices-1;

   /* which layer can we use? */
   for (layer=0; layer<MIDI_LAYERS; layer++)
      if (midi_channel[midi_alloc_channel].note[midi_alloc_note][layer] < 0)
	 break; 

   if (layer >= MIDI_LAYERS)
      return -1;

   /* find a free voice */
   for (c=min; c<=max; c++) {
      if ((midi_voice[c].note < 0) && 
	  (midi_voice[c].time < best_time) && 
	  ((c < midi_driver->xmin) || (c > midi_driver->xmax))) {
	 voice = c;
	 best_time = midi_voice[c].time;
      }
   }

   /* if there are no free voices, kill a note to make room */
   if (voice < 0) {
      voice = -1;
      best_time = LONG_MAX;
      for (c=min; c<=max; c++) {
	 if ((midi_voice[c].time < best_time) && 
	     ((c < midi_driver->xmin) || (c > midi_driver->xmax))) {
	    voice = c;
	    best_time = midi_voice[c].time;
	 }
      }
      if (voice >= 0)
	 midi_note_off(midi_voice[voice].channel, midi_voice[voice].note);
      else
	 return -1;
   }

   /* ok, we got it... */
   midi_voice[voice].channel = midi_alloc_channel;
   midi_voice[voice].note = midi_alloc_note;
   midi_voice[voice].volume = midi_alloc_vol;
   midi_voice[voice].time = _midi_tick;
   midi_channel[midi_alloc_channel].note[midi_alloc_note][layer] = voice; 

   return voice + midi_driver->basevoice;
}
/* midi_note_on:
 *  Processes a MIDI note-on event. Tries to find a free soundcard voice,
 *  and if it can't either cuts off an existing note, or if 'polite' is
 *  set, just stores the channel, note and volume in the waiting list.
 */
static void midi_note_on(int channel, int note, int vol, int polite)
{
   int c, layer, inst, bend, corrected_note;

   /* it's easy if the driver can handle raw MIDI data */
   if (midi_driver->raw_midi) {
      if (channel != 9)
	 note += patch_table[midi_channel[channel].patch].pitch;

      midi_driver->raw_midi(0x90+channel);
      midi_driver->raw_midi(note);
      midi_driver->raw_midi(vol);
      return;
   }

   /* if the note is already on, turn it off */
   for (layer=0; layer<MIDI_LAYERS; layer++) {
      if (midi_channel[channel].note[note][layer] >= 0) {
	 midi_note_off(channel, note);
	 return;
      }
   }

   /* if zero volume and the note isn't playing, we can just ignore it */
   if (vol == 0)
      return;

   if (channel != 9) {
      /* are there any free voices? */
      for (c=0; c<midi_driver->voices; c++)
	 if ((midi_voice[c].note < 0) && 
	     ((c < midi_driver->xmin) || (c > midi_driver->xmax)))
	    break;

      /* if there are no free voices, remember the note for later */
      if ((c >= midi_driver->voices) && (polite)) {
	 for (c=0; c<MIDI_VOICES; c++) {
	    if (midi_waiting[c].note < 0) {
	       midi_waiting[c].channel = channel;
	       midi_waiting[c].note = note;
	       midi_waiting[c].volume = vol;
	       break;
	    }
	 }
	 return;
      }
   }

   /* drum sound? */
   if (channel == 9) {
      inst = 128+note;
      corrected_note = 60;
      bend = 0;
   }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -