📄 midi.h
字号:
#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 + -