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

📄 midi.h

📁 这个版本修正了已知的Bug,同时添加了部分函数
💻 H
📖 第 1 页 / 共 4 页
字号:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <limits.h>

#ifndef _MIDI_H_INCLUDED
#define _MIDI_H_INCLUDED
#if !defined(_ENABLE_AUTODEPEND)
  #pragma read_only_file;
#endif
#endif
//////// Sound Blaster ///////////
static short lk_fm_port=-1, lk_fm_delay1=6, lk_fm_delay2=35;
static  MIDI *midifile = NULL;                   /* the file that is playing */
static  short midi_loop = 0;                     /* repeat at eof? */
static  long midi_loop_start = -1;                      /* where to loop back to */
static  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 */
static  short  _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 */
static volatile long midi_pos = -1;                    /* position in MIDI file */
static long midi_pos_counter;                   /* delta for midi_pos */
static volatile long _midi_tick = 0;                   /* counter for killing notes */
static MIDI_DRIVER *midi_driver;
static char MidiFlag=0;
static char adlib_desc[80] = "not initialised";

char  WritePortC(unsigned char value);
unsigned char ReadPortC();
short GetDspVersion();
void  MixerWrite(short reg,short data);
short MixerRead(short reg);
short SetMixer(int digi_volume, int midi_volume);


void  FMWrite(unsigned short reg,unsigned char data);
unsigned char FMRead();
void  FMInit();
void  FMSound(short frequency,short block);
void  FMSoundOff(short frequency,short block);
void  FM(short frequency,short block);


/* external interface to the Adlib driver */
 int fm_detect();
 int fm_init(int voices);
 void fm_exit();
 int fm_mixer_volume(int volume);
 int fm_load_patches(char *patches, char *drums);
 void fm_key_on(int inst, int note, int bend, int vol, int pan);
 void fm_key_off(int voice);
 void fm_set_volume(int voice, int vol);
 void fm_set_pitch(int voice, int note, int bend);

void _dummy_adjust_patches(char *patches, char *drums) {}
void _dummy_noop2(int p1, int p2) {}

// Midi Player /////////////////////////////////////////////////////////////

 void midi_player();                      /* core MIDI player routine */
#pragma intrinsic(midi_player);
 void prepare_to_play(MIDI *midi);

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;


////////////////////////////////////////////////////////////////////////////
char  MidiInit(char driver);
char  MidiRest();
void  FM_load_map(int choice,int drums);
int   FM_load_ibk(char *filename, int drums);
MIDI *LoadMidi(char *fname);
void  DestroyMidi(MIDI*);
int   MidiPlay(MIDI *midi, int loop);
void  StopMidi();
void  MidiPause();
int   MidiSeek(int target);
void  MidiResume();
////////////////////////////////////////////////////////////////////////////
 int  midi_init(char driver);
 void midi_exit();


MIDI_DRIVER Midi_adlib =
{
   "Adlib", 
   adlib_desc,
   0, 0, 0, 0, -1, -1,
   fm_detect,
   fm_init,
   fm_exit,
   NULL, /*fm_mixer_volume,*/
   NULL,
   fm_load_patches,
   _dummy_adjust_patches,
   fm_key_on,
   fm_key_off,
   fm_set_volume,
   fm_set_pitch,
   _dummy_noop2,
   _dummy_noop2
};

char MidiInit(char driver)
{ if (MidiFlag) return 1;
  if (!lk_sb_port) { sprintf(grp_err,"Sound card init failed\n");return FALSE; }
  if (!midi_init(driver)) return FALSE;
  MidiFlag=1; return 1;
}

char MidiRest()
{ if (!MidiFlag) return 0;
  midi_exit(); 
  MidiFlag=0;
  return 1;
}

 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];
}

 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 StopMidi()
{ MidiPlay(NULL, FALSE); }

MIDI *LoadMidi(char *filename)
{
   int c;
   char buf[256];
   long data,retsiz;
   FILE *fp;
   MIDI *midi;
   int num_tracks;

   fp = fopen(filename, "rb");        /* open the file */
   if (!fp) {
   sprintf(grp_err,"Can Open Midi File %s",filename);

   return FALSE; }

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

   for (c=0; c<MIDI_TRACKS; c++) {
      midi->track[c].data = NULL;
      midi->track[c].len = 0;
   }
  
   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);
   DestroyMidi(midi);
   return FALSE;
}

void DestroyMidi(MIDI *midi)
{
   int c;

   if (midi == midifile)
      StopMidi();

   if (midi) {
      for (c=0; c<MIDI_TRACKS; c++) {
	 if (midi->track[c].data) {
	    free(midi->track[c].data);
	 }
      }
      free(midi);
   }
}

 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.
 */

 inline int global_volume_fix(int vol)
{
   if (_midi_volume >= 0)
      return (vol * _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.
 */

 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!
 */

 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.
 */

 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.
 */

 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 _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.
 */

 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;
   }
   else {
      inst = midi_channel[channel].patch;
      corrected_note = note;
      bend = midi_channel[channel].pitch_bend;
      sort_out_pitch_bend(&bend, &corrected_note);
   }

   /* play the note */
   midi_alloc_channel = channel;
   midi_alloc_note = note;
   midi_alloc_vol = vol;

   midi_driver->key_on(inst, corrected_note, bend, 
		       sort_out_volume(channel, vol), 
		       midi_channel[channel].pan);
}

/* all_notes_off:
 *  Turns off all active notes.
 */

 void all_notes_off(int channel)
{
   if (midi_driver->raw_midi) {
      midi_driver->raw_midi(0xB0+channel);
      midi_driver->raw_midi(123);

⌨️ 快捷键说明

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