/************************************************************************ emumidi.c -- emulation of midi device for FM/OPL3/GUS Copyright (C) 1994-1996 Nathan I. Laredo This program is modifiable/redistributable under the terms of the GNU General Public Licence. 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., 675 Mass Ave, Cambridge, MA 02139, USA. Send your comments and all your spare pocket change to laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401 Kelly Drive, Lackland AFB, TX 78236-5128, USA. *************************************************************************/ /* edited by Peter Kutak */ /* email : kutak@stonline.sk */ #if defined(linux) || defined(__FreeBSD__) #include "emumidi.h" SEQ_USE_EXTBUF(); extern int seqfd, play_ext, play_gus, play_fm, play_awe; extern int gus_dev, sb_dev, ext_dev, awe_dev; extern struct synth_info card_info[MAX_CARDS]; extern int chanmask, perc, ticks, dochan, wantopl3, MT32; extern int patchloaded[256], fmloaded[256], useprog[16]; int note_vel[16][128]; struct voicestate voice[2][36]; struct chanstate channel[16]; #define C_GUS 0 #define C_FM 1 #define CN (ISGUS(chn) ? C_GUS : C_FM) #define CHANNEL (dochan ? chn : 0) void load_sysex(length, data, type) int length; unsigned char *data; int type; { unsigned long int i, j; /* * If the system exclusive is for roland, evaluate it. More than * roland could be evaluated here if i had documentation. Please * submit patches for any other hardware to laredo@gnu.ai.mit.edu * Complete emulation of all GS sysex messages in the works.... */ if (length > 7 && data[0] == 0x41 && data[2] == 0x42 && data[3] == 0x12) { /* GS DATA SET MESSAGES */ if (data[4] == 0x40 && (data[5] & 0xf0) == 0x10 && data[6] == 0x15) { /* USE RHYTHM PART */ if (!(i = (data[5] & 0xf))) i = 0x09; else if (i < 10) i--; i = 1< 127 || r - note < 0) return n_freq[note]; r = n_freq[note + r] - n_freq[note - r]; d = b * r; d /= 8192; return n_freq[note] + d; } */ extern int _seqbufptr; void seq_stop_note(dev, chn, note, vel) int dev, chn, note, vel; { int i, card = CN; note_vel[chn][note] = 0; if (ISMIDI(chn)) { SEQ_MIDIOUT(dev, MIDI_NOTEOFF + CHANNEL); SEQ_MIDIOUT(dev, note); SEQ_MIDIOUT(dev, vel); } else if (ISAWE(chn)) { SEQ_STOP_NOTE(dev, chn, note, vel); } else for (i = 0; i < card_info[dev].nr_voices; i++) if (voice[card][i].channel == chn && voice[card][i].note == note) { voice[card][i].dead = 1; voice[card][i].timestamp /= 2; if (!channel[chn].controller[CTL_SUSTAIN] && !ISPERC(chn)) SEQ_STOP_NOTE(dev, i, note, vel); } } void seq_key_pressure(dev, chn, note, vel) int dev, chn, note, vel; { int i, card = CN; if (ISMIDI(chn)) { SEQ_MIDIOUT(dev, MIDI_KEY_PRESSURE + CHANNEL); SEQ_MIDIOUT(dev, note); SEQ_MIDIOUT(dev, vel); } else if (ISAWE(chn)) { AWE_KEY_PRESSURE(dev, chn, note, vel); } else for (i = 0; i < card_info[dev].nr_voices; i++) if (voice[card][i].channel == chn && voice[card][i].note == note) SEQ_KEY_PRESSURE(dev, i, note, vel); } int new_voice(dev, chn) int dev, chn; { int i, oldest, last, card = CN; if (ISFM(chn) && fmloaded[channel[chn].program] == OPL3_PATCH) last = 6; /* 4-op voice can only use first six voices */ else last = card_info[dev].nr_voices; for (i = oldest = 0; i < last; i++) if (voice[card][i].timestamp < voice[card][oldest].timestamp) oldest = i; return oldest; } void seq_start_note(dev, chn, note, vel) int dev, chn, note, vel; { int v, c, card = CN; note_vel[chn][note] = vel; if (ISMIDI(chn)) { SEQ_MIDIOUT(dev, MIDI_NOTEON + CHANNEL); SEQ_MIDIOUT(dev, note); SEQ_MIDIOUT(dev, vel); } else if (vel == 0) seq_stop_note(dev, chn, note, 64); else if (ISAWE(chn)) { SEQ_START_NOTE(dev, chn, note, vel); } else { v = new_voice(dev, chn); SEQ_SET_PATCH(dev, v, channel[chn].program); SEQ_BENDER_RANGE(dev, v, (channel[chn].bender_range * 100)); SEQ_BENDER(dev, v, channel[chn].bender); SEQ_CONTROL(dev, v, CTL_PAN, channel[chn].controller[CTL_PAN]); SEQ_START_NOTE(dev, v, note, vel); voice[card][v].note = note; voice[card][v].channel = chn; voice[card][v].timestamp = ticks; voice[card][v].dead = 0; if ((c = channel[chn].controller[CTL_CHORUS_DEPTH] * 8)) { if (channel[chn].bender_range) c /= channel[chn].bender_range; v = new_voice(dev, chn); SEQ_SET_PATCH(dev, v, channel[chn].program); SEQ_BENDER_RANGE(dev, v, (channel[chn].bender_range * 100)); if (channel[chn].bender + c < 0x4000) { SEQ_BENDER(dev, v, channel[chn].bender + c); } else { SEQ_BENDER(dev, v, channel[chn].bender - c); } /* put chorus note on the "extreme" side */ c = channel[chn].controller[CTL_PAN]; if (c < 64) c = 0; else if (c > 64) c = 127; SEQ_CONTROL(dev, v, CTL_PAN, c); SEQ_START_NOTE(dev, v, note, vel); voice[card][v].note = note; voice[card][v].channel = chn; /* allow chorus note to be stolen very quickly */ voice[card][v].timestamp = ticks / 2; voice[card][v].dead = 0; } } } static int rpn1[16] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}; static int rpn2[16] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}; void seq_control(dev, chn, p1, p2) int dev, chn, p1, p2; { int i, card = CN; channel[chn].controller[p1] = p2; if (ISMIDI(chn)) { SEQ_MIDIOUT(dev, MIDI_CTL_CHANGE + CHANNEL); SEQ_MIDIOUT(dev, p1); SEQ_MIDIOUT(dev, p2); } if (p1 == 7 || p1 == 39) return; switch (p1) { case CTL_SUSTAIN: if (ISAWE(chn)) { SEQ_CONTROL(dev, chn, p1, p2); } else if (!ISMIDI(chn)) if (p1 == CTL_SUSTAIN && !p2) { for (i = 0; i < card_info[card].nr_voices; i++) if (voice[card][i].channel == chn && voice[card][i].dead) { SEQ_STOP_NOTE(dev, i, voice[card][i].note, 64); voice[card][i].dead = 0; } } break; case CTL_REGIST_PARM_NUM_MSB: rpn1[chn] = p2; break; case CTL_REGIST_PARM_NUM_LSB: rpn2[chn] = p2; break; case CTL_DATA_ENTRY: if (rpn1[chn] == 0 && rpn2[chn] == 0) { channel[chn].oldrange = channel[chn].bender_range; channel[chn].bender_range = p2; rpn1[chn] = rpn2[chn] = 127; if (ISAWE(chn)) { SEQ_BENDER_RANGE(dev, chn, p2 * 100); } else if (!ISMIDI(chn)) for (i = 0; i < card_info[card].nr_voices; i++) SEQ_BENDER_RANGE(dev, i, p2 * 100); } break; default: /* sent on the off chance the sound driver is enhanced */ if (ISAWE(chn)) { SEQ_CONTROL(dev, chn, p1, p2); } else if (!ISMIDI(chn) && (p1 < 0x10 || (p1 & 0xf0) == 0x50)) for (i = 0; i < card_info[card].nr_voices; i++) if (voice[card][i].channel == chn) SEQ_CONTROL(dev, i, p1, p2); break; } } void seq_chn_pressure(dev, chn, vel) int dev, chn, vel; { int card = CN, i; channel[chn].pressure = vel; if (ISMIDI(chn)) { SEQ_MIDIOUT(dev, MIDI_CHN_PRESSURE + CHANNEL); SEQ_MIDIOUT(dev, vel); } else if (ISAWE(chn)) { AWE_CHN_PRESSURE(dev, chn, vel); } else for (i = 0; i < card_info[dev].nr_voices; i++) if (voice[card][i].channel == chn) SEQ_KEY_PRESSURE(dev, i, voice[card][i].note, vel); } void seq_bender(dev, chn, p1, p2) int dev, chn, p1, p2; { int card = CN, i, val; val = (p2 << 7) + p1; channel[chn].oldbend = channel[chn].bender; channel[chn].bender = val; if (ISMIDI(chn)) { SEQ_MIDIOUT(dev, MIDI_PITCH_BEND + CHANNEL); SEQ_MIDIOUT(dev, p1); SEQ_MIDIOUT(dev, p2); } else if (ISAWE(chn)) { SEQ_BENDER(dev, chn, val); } else for (i = 0; i < card_info[dev].nr_voices; i++) if (voice[card][i].channel == chn) SEQ_BENDER(dev, i, val); } void seq_reset() { int i, j; _seqbufptr = ticks = 0; ioctl(seqfd, SNDCTL_SEQ_RESET); for (i = 0; i < 16; i++) { if (ISMIDI(i)) { seq_control(ext_dev,i,0,0); seq_control(ext_dev,i,32,0); } seq_set_patch(i, 0); for (j = 0; j < 128; j++) note_vel[i][j] = 0; channel[i].bender = channel[i].oldbend = 8192; channel[i].bender_range = channel[i].oldrange = 2; channel[i].controller[CTL_PAN] = 64; channel[i].controller[CTL_SUSTAIN] = 0; } if (play_gus) for (i = 0; i < card_info[gus_dev].nr_voices; i++) { SEQ_CONTROL(gus_dev, i, SEQ_VOLMODE, VOL_METHOD_LINEAR); if (voice[0][i].note) SEQ_STOP_NOTE(gus_dev, i, voice[0][i].note, 64); voice[0][i].dead = voice[0][i].timestamp = -1; } if (play_fm) { if (wantopl3) ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &sb_dev); for (i = 0; i < card_info[sb_dev].nr_voices; i++) { SEQ_CONTROL(sb_dev, i, SEQ_VOLMODE, VOL_METHOD_LINEAR); if (voice[1][i].note) SEQ_STOP_NOTE(sb_dev, i, voice[1][i].note, 64); voice[1][i].dead = voice[1][i].timestamp = -1; } } if (play_awe) { AWE_SET_CHANNEL_MODE(awe_dev, 1); AWE_DRUM_CHANNELS(awe_dev, perc); AWE_TERMINATE_ALL(awe_dev); for (i = 0; i < card_info[awe_dev].nr_voices; i++) { voice[0][i].dead = voice[0][i].timestamp = -1; } } SEQ_DUMPBUF(); } #endif /* linux || FreeBSD */