From 787e4fc21c68348b4e001dd93cb9bc4b9eefc348 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 13 Feb 2002 19:07:39 +0000 Subject: [PATCH] Peter Kutak - Wed Feb 13 10:26:57 PST 2002 * Added native midi support on Linux, using GPL code --enable-music-native-midi-gpl --- CHANGES | 3 + Makefile.am | 6 +- configure.in | 30 +- native_midi_gpl/Makefile.am | 16 + native_midi_gpl/README | 26 ++ native_midi_gpl/awe_voice.h | 490 ++++++++++++++++++++++++++++++ native_midi_gpl/emumidi.c | 390 ++++++++++++++++++++++++ native_midi_gpl/emumidi.h | 63 ++++ native_midi_gpl/gmvoices.h | 106 +++++++ native_midi_gpl/gsvoices.h | 39 +++ native_midi_gpl/native_midi_gpl.c | 488 +++++++++++++++++++++++++++++ native_midi_gpl/patchload.c | 412 +++++++++++++++++++++++++ native_midi_gpl/playevents.c | 217 +++++++++++++ native_midi_gpl/playmidi.h | 107 +++++++ native_midi_gpl/readmidi.c | 188 ++++++++++++ 15 files changed, 2579 insertions(+), 2 deletions(-) create mode 100644 native_midi_gpl/Makefile.am create mode 100644 native_midi_gpl/README create mode 100644 native_midi_gpl/awe_voice.h create mode 100644 native_midi_gpl/emumidi.c create mode 100644 native_midi_gpl/emumidi.h create mode 100644 native_midi_gpl/gmvoices.h create mode 100644 native_midi_gpl/gsvoices.h create mode 100644 native_midi_gpl/native_midi_gpl.c create mode 100644 native_midi_gpl/patchload.c create mode 100644 native_midi_gpl/playevents.c create mode 100644 native_midi_gpl/playmidi.h create mode 100644 native_midi_gpl/readmidi.c diff --git a/CHANGES b/CHANGES index 54a1c1ec..affddf6c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ 1.2.3: +Peter Kutak - Wed Feb 13 10:26:57 PST 2002 + * Added native midi support on Linux, using GPL code + --enable-music-native-midi-gpl Pete Shinners - Mon Jan 14 11:31:26 PST 2002 * Added seek support for MP3 files Ryan Gordon - Mon Jan 14 11:30:44 PST 2002 diff --git a/Makefile.am b/Makefile.am index 978078be..9db39b27 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ lib_LTLIBRARIES = libSDL_mixer.la SUBDIRS = @MUSIC_SUBDIRS@ -DIST_SUBDIRS = mikmod timidity native_midi +DIST_SUBDIRS = mikmod timidity native_midi native_midi_gpl libSDL_mixerincludedir = $(includedir)/SDL libSDL_mixerinclude_HEADERS = \ @@ -38,7 +38,11 @@ else TIMIDITY_LIB = endif if USE_NATIVE_MIDI +if USE_NATIVE_MIDI_GPL +NATIVE_MIDI_LIB = native_midi_gpl/libnativemidi_gpl.la +else NATIVE_MIDI_LIB = native_midi/libnativemidi.la +endif else NATIVE_MIDI_LIB = endif diff --git a/configure.in b/configure.in index 664a4448..06caaa57 100644 --- a/configure.in +++ b/configure.in @@ -135,12 +135,38 @@ if test x$enable_music_midi = xyes; then use_music_native_midi=yes LIBS="$LIBS -framework QuickTime" ;; + *-*-linux* | *-*-freebsd*) + if test x$enable_music_native_midi_gpl != xyes; then + AC_ERROR([the native midi code is GPL code, use --enable-music-native-midi-gpl]) + fi + ;; + *) + AC_ERROR([native MIDI not supported on this platform]) + ;; esac if test x$use_music_native_midi = xyes; then CFLAGS="$CFLAGS -DUSE_NATIVE_MIDI -I\$(top_srcdir)/native_midi" MUSIC_SUBDIRS="$MUSIC_SUBDIRS native_midi" fi fi + AC_ARG_ENABLE(music-native-midi-gpl, +[ --enable-music-native-midi-gpl enable native MIDI on UNIX using GPL code [default=no]], + , enable_music_native_midi_gpl=no) + if test x$enable_music_native_midi_gpl = xyes; then + use_music_native_midi_gpl=no + case "$target" in + *-*-linux* | *-*-freebsd*) + use_music_native_midi_gpl=yes + ;; + *) + AC_ERROR([native MIDI not supported on this platform]) + ;; + esac + if test x$use_music_native_midi_gpl = xyes; then + CFLAGS="$CFLAGS -DUSE_NATIVE_MIDI -I\$(top_srcdir)/native_midi" + MUSIC_SUBDIRS="$MUSIC_SUBDIRS native_midi_gpl" + fi + fi fi AC_ARG_ENABLE(music-ogg, [ --enable-music-ogg enable Ogg Vorbis music [default=yes]], @@ -176,7 +202,8 @@ dnl Add Makefile conditionals AC_SUBST(MUSIC_SUBDIRS) AM_CONDITIONAL(USE_MIKMOD, test x$enable_music_mod = xyes) AM_CONDITIONAL(USE_TIMIDITY, test x$enable_music_timidity_midi = xyes) -AM_CONDITIONAL(USE_NATIVE_MIDI, test x$use_music_native_midi = xyes) +AM_CONDITIONAL(USE_NATIVE_MIDI, test x$use_music_native_midi = xyes || test x$use_music_native_midi_gpl = xyes) +AM_CONDITIONAL(USE_NATIVE_MIDI_GPL, test x$use_music_native_midi_gpl = xyes) # Finally create all the generated files AC_OUTPUT([ @@ -184,5 +211,6 @@ Makefile mikmod/Makefile timidity/Makefile native_midi/Makefile +native_midi_gpl/Makefile SDL_mixer.spec ]) diff --git a/native_midi_gpl/Makefile.am b/native_midi_gpl/Makefile.am new file mode 100644 index 00000000..8bba6bf6 --- /dev/null +++ b/native_midi_gpl/Makefile.am @@ -0,0 +1,16 @@ + +noinst_LTLIBRARIES = libnativemidi_gpl.la + +libnativemidi_gpl_la_SOURCES = \ + awe_voice.h \ + emumidi.c \ + emumidi.h \ + gmvoices.h \ + gsvoices.h \ + native_midi_gpl.c \ + patchload.c \ + playevents.c \ + playmidi.h \ + readmidi.c + +INCLUDES = -I$(top_srcdir) diff --git a/native_midi_gpl/README b/native_midi_gpl/README new file mode 100644 index 00000000..3e34aae4 --- /dev/null +++ b/native_midi_gpl/README @@ -0,0 +1,26 @@ + +Unfortunately, the code for the native MIDI support on Linux is derived +from source under the GNU General Public License, and not the GNU LGPL. +Therefore, it's not built by default. + +Native Midi support for Linux should work on FreeBSD too ( I can not test it ). + +Midi device can be selected with enviroment variable SDL_NATIVE_MUSIC, there can have one of flowing vlues: +GUS - GravisUltrasound +AWE - SoundBlaster AWE32 +FM - FreqentionModulation SoundBlaster Compatible +OPL3 - Yamaha OPL/3 +EXT - External MIDI port + +External MIDI port can be set with env. SDL_NATIVE_MUSIC_EXT (SDL_NATIVE_MUSIC_EXT="1"). + +Autoselect priority: +1. GUS +2. AWE +3. use Timidity +4. FM +5. EXT + +Also if you want use FM befor Timidity set variable SDL_NATIVE_MUSIC="FM" or edit source code native_midi_lnx.c( return value in function synth_setup, by autoselect FM should return 1). +For using FM: files drums.sb drums.o3 std.sb std.o3 should be moved to /etc/ dir ( or symlink created ). + diff --git a/native_midi_gpl/awe_voice.h b/native_midi_gpl/awe_voice.h new file mode 100644 index 00000000..aa131313 --- /dev/null +++ b/native_midi_gpl/awe_voice.h @@ -0,0 +1,490 @@ +/* + * sound/awe_voice.h + * + * Voice information definitions for the low level driver for the + * AWE32/Sound Blaster 32 wave table synth. + * version 0.4.2c; Oct. 7, 1997 + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AWE_VOICE_H +#define AWE_VOICE_H + +#ifndef SAMPLE_TYPE_AWE32 +#define SAMPLE_TYPE_AWE32 0x20 +#endif + +#ifndef _PATCHKEY +#define _PATCHKEY(id) ((id<<8)|0xfd) +#endif + +/*---------------------------------------------------------------- + * patch information record + *----------------------------------------------------------------*/ + +/* patch interface header: 16 bytes */ +typedef struct awe_patch_info { + short key; /* use AWE_PATCH here */ +#define AWE_PATCH _PATCHKEY(0x07) + + short device_no; /* synthesizer number */ + unsigned short sf_id; /* file id (should be zero) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ + + short type; /* patch operation type */ +#define AWE_LOAD_INFO 0 /* awe_voice_rec */ +#define AWE_LOAD_DATA 1 /* awe_sample_info */ +#define AWE_OPEN_PATCH 2 /* awe_open_parm */ +#define AWE_CLOSE_PATCH 3 /* none */ +#define AWE_UNLOAD_PATCH 4 /* none */ +#define AWE_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define AWE_MAP_PRESET 6 /* awe_voice_map */ +#define AWE_LOAD_CHORUS_FX 0x10 /* awe_chorus_fx_rec (optarg=mode) */ +#define AWE_LOAD_REVERB_FX 0x11 /* awe_reverb_fx_rec (optarg=mode) */ + + short reserved; /* word alignment data */ + + /* the actual patch data begins after this */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 + char data[0]; +#endif +} awe_patch_info; + +/*#define AWE_PATCH_INFO_SIZE 16*/ +#define AWE_PATCH_INFO_SIZE sizeof(awe_patch_info) + + +/*---------------------------------------------------------------- + * open patch + *----------------------------------------------------------------*/ + +#define AWE_PATCH_NAME_LEN 32 + +typedef struct _awe_open_parm { + unsigned short type; /* sample type */ +#define AWE_PAT_TYPE_MISC 0 +#define AWE_PAT_TYPE_GM 1 +#define AWE_PAT_TYPE_GS 2 +#define AWE_PAT_TYPE_MT32 3 +#define AWE_PAT_TYPE_XG 4 +#define AWE_PAT_TYPE_SFX 5 +#define AWE_PAT_TYPE_GUS 6 +#define AWE_PAT_TYPE_MAP 7 + +#define AWE_PAT_LOCKED 0x100 /* lock the samples */ + + short reserved; + char name[AWE_PATCH_NAME_LEN]; +} awe_open_parm; + +/*#define AWE_OPEN_PARM_SIZE 28*/ +#define AWE_OPEN_PARM_SIZE sizeof(awe_open_parm) + + +/*---------------------------------------------------------------- + * raw voice information record + *----------------------------------------------------------------*/ + +/* wave table envelope & effect parameters to control EMU8000 */ +typedef struct _awe_voice_parm { + unsigned short moddelay; /* modulation delay (0x8000) */ + unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */ + unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */ + unsigned short modrelease; /* modulation release time (0x807f) */ + short modkeyhold, modkeydecay; /* envelope change per key (not used) */ + unsigned short voldelay; /* volume delay (0x8000) */ + unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */ + unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */ + unsigned short volrelease; /* volume release time (0x807f) */ + short volkeyhold, volkeydecay; /* envelope change per key (not used) */ + unsigned short lfo1delay; /* LFO1 delay (0x8000) */ + unsigned short lfo2delay; /* LFO2 delay (0x8000) */ + unsigned short pefe; /* modulation pitch & cutoff (0x0000) */ + unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */ + unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */ + unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */ + unsigned char cutoff; /* initial cutoff (0xff) */ + unsigned char filterQ; /* initial filter Q [0-15] (0x0) */ + unsigned char chorus; /* chorus send (0x00) */ + unsigned char reverb; /* reverb send (0x00) */ + unsigned short reserved[4]; /* not used */ +} awe_voice_parm; + +#define AWE_VOICE_PARM_SIZE 48 + + +/* wave table parameters: 92 bytes */ +typedef struct _awe_voice_info { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ + short rate_offset; /* sample rate pitch offset */ + unsigned short mode; /* sample mode */ +#define AWE_MODE_ROMSOUND 0x8000 +#define AWE_MODE_STEREO 1 +#define AWE_MODE_LOOPING 2 +#define AWE_MODE_NORELEASE 4 /* obsolete */ +#define AWE_MODE_INIT_PARM 8 + + short root; /* midi root key */ + short tune; /* pitch tuning (in cents) */ + char low, high; /* key note range */ + char vellow, velhigh; /* velocity range */ + char fixkey, fixvel; /* fixed key, velocity */ + char pan, fixpan; /* panning, fixed panning */ + short exclusiveClass; /* exclusive class (0 = none) */ + unsigned char amplitude; /* sample volume (127 max) */ + unsigned char attenuation; /* attenuation (0.375dB) */ + short scaleTuning; /* pitch scale tuning(%), normally 100 */ + awe_voice_parm parm; /* voice envelope parameters */ + short index; /* internal index (set by driver) */ +} awe_voice_info; + +/*#define AWE_VOICE_INFO_SIZE 92*/ +#define AWE_VOICE_INFO_SIZE sizeof(awe_voice_info) + +/*----------------------------------------------------------------*/ + +/* The info entry of awe_voice_rec is changed from 0 to 1 + * for some compilers refusing zero size array. + * Due to this change, sizeof(awe_voice_rec) becomes different + * from older versions. + * Use AWE_VOICE_REC_SIZE instead. + */ + +/* instrument info header: 4 bytes */ +typedef struct _awe_voice_rec_hdr { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define AWE_WR_APPEND 0 /* append anyway */ +#define AWE_WR_EXCLUSIVE 1 /* skip if already exists */ +#define AWE_WR_REPLACE 2 /* replace if already exists */ +} awe_voice_rec_hdr; + +/*#define AWE_VOICE_REC_SIZE 4*/ +#define AWE_VOICE_REC_SIZE sizeof(awe_voice_rec_hdr) + +/* the standard patch structure for one sample */ +typedef struct _awe_voice_rec_patch { + awe_patch_info patch; + awe_voice_rec_hdr hdr; + awe_voice_info info; +} awe_voice_rec_patch; + + +/* obsolete data type */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 +#define AWE_INFOARRAY_SIZE 0 +#else +#define AWE_INFOARRAY_SIZE 1 +#endif + +typedef struct _awe_voice_rec { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + short nvoices; /* number of voices */ + /* voice information follows here */ + awe_voice_info info[AWE_INFOARRAY_SIZE]; +} awe_voice_rec; + + +/*---------------------------------------------------------------- + * sample wave information + *----------------------------------------------------------------*/ + +/* wave table sample header: 32 bytes */ +typedef struct awe_sample_info { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ + short checksum_flag; /* use check sum = 1 */ + unsigned short mode_flags; /* mode flags */ +#define AWE_SAMPLE_8BITS 1 /* wave data is 8bits */ +#define AWE_SAMPLE_UNSIGNED 2 /* wave data is unsigned */ +#define AWE_SAMPLE_NO_BLANK 4 /* no blank loop is attached */ +#define AWE_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */ +#define AWE_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */ +#define AWE_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ +#define AWE_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ +#define AWE_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ + unsigned int checksum; /* check sum */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 + unsigned short data[0]; /* sample data follows here */ +#endif +} awe_sample_info; + +/*#define AWE_SAMPLE_INFO_SIZE 32*/ +#define AWE_SAMPLE_INFO_SIZE sizeof(awe_sample_info) + + +/*---------------------------------------------------------------- + * voice preset mapping + *----------------------------------------------------------------*/ + +typedef struct awe_voice_map { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} awe_voice_map; + +#define AWE_VOICE_MAP_SIZE sizeof(awe_voice_map) + + +/*---------------------------------------------------------------- + * awe hardware controls + *----------------------------------------------------------------*/ + +#define _AWE_DEBUG_MODE 0x00 +#define _AWE_REVERB_MODE 0x01 +#define _AWE_CHORUS_MODE 0x02 +#define _AWE_REMOVE_LAST_SAMPLES 0x03 +#define _AWE_INITIALIZE_CHIP 0x04 +#define _AWE_SEND_EFFECT 0x05 +#define _AWE_TERMINATE_CHANNEL 0x06 +#define _AWE_TERMINATE_ALL 0x07 +#define _AWE_INITIAL_VOLUME 0x08 +#define _AWE_INITIAL_ATTEN _AWE_INITIAL_VOLUME +#define _AWE_RESET_CHANNEL 0x09 +#define _AWE_CHANNEL_MODE 0x0a +#define _AWE_DRUM_CHANNELS 0x0b +#define _AWE_MISC_MODE 0x0c +#define _AWE_RELEASE_ALL 0x0d +#define _AWE_NOTEOFF_ALL 0x0e +#define _AWE_CHN_PRESSURE 0x0f +/*#define _AWE_GET_CURRENT_MODE 0x10*/ +#define _AWE_EQUALIZER 0x11 +/*#define _AWE_GET_MISC_MODE 0x12*/ +/*#define _AWE_GET_FONTINFO 0x13*/ + +#define _AWE_MODE_FLAG 0x80 +#define _AWE_COOKED_FLAG 0x40 /* not supported */ +#define _AWE_MODE_VALUE_MASK 0x3F + +/*----------------------------------------------------------------*/ + +#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \ +{((char*)(p))[0] = SEQ_PRIVATE;\ + ((char*)(p))[1] = dev;\ + ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\ + ((char*)(p))[3] = voice;\ + ((unsigned short*)(p))[2] = p1;\ + ((unsigned short*)(p))[3] = p2;} + +/* buffered access */ +#define _AWE_CMD(dev, voice, cmd, p1, p2) \ +{_SEQ_NEEDBUF(8);\ + _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\ + _SEQ_ADVBUF(8);} + +/* direct access */ +#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \ +{struct seq_event_rec tmp;\ + _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\ + ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);} + +/*----------------------------------------------------------------*/ + +/* set debugging mode */ +#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0) +/* set reverb mode; from 0 to 7 */ +#define AWE_REVERB_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0) +/* set chorus mode; from 0 to 7 */ +#define AWE_CHORUS_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0) + +/* reset channel */ +#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0) +#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0) + +/* send an effect to all layers */ +#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value) +#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value) +#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0) +/* send an effect to a layer */ +#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value) +#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value) +#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0) + +/* terminate sound on the channel/voice */ +#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0) +/* terminate all sounds */ +#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0) +/* release all sounds (w/o sustain effect) */ +#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0) +/* note off all sounds (w sustain effect) */ +#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0) + +/* set initial attenuation */ +#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0) +#define AWE_INITIAL_ATTEN AWE_INITIAL_VOLUME +/* relative attenuation */ +#define AWE_SET_ATTEN(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1) + +/* set channel playing mode; mode=0/1/2 */ +#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0) +#define AWE_PLAY_INDIRECT 0 /* indirect voice mode (default) */ +#define AWE_PLAY_MULTI 1 /* multi note voice mode */ +#define AWE_PLAY_DIRECT 2 /* direct single voice mode */ +#define AWE_PLAY_MULTI2 3 /* sequencer2 mode; used internally */ + +/* set drum channel mask; channels is 32bit long value */ +#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16)) + +/* set bass and treble control; values are from 0 to 11 */ +#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble) + +/* remove last loaded samples */ +#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0) +/* initialize emu8000 chip */ +#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0) + +/* set miscellaneous modes; meta command */ +#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value) +/* exclusive sound off; 1=off */ +#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode) +/* default GUS bank number */ +#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank) +/* change panning position in realtime; 0=don't 1=do */ +#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode) + +/* extended pressure controls; not portable with other sound drivers */ +#define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel) +#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0) + +/*----------------------------------------------------------------*/ + +/* reverb mode parameters */ +#define AWE_REVERB_ROOM1 0 +#define AWE_REVERB_ROOM2 1 +#define AWE_REVERB_ROOM3 2 +#define AWE_REVERB_HALL1 3 +#define AWE_REVERB_HALL2 4 +#define AWE_REVERB_PLATE 5 +#define AWE_REVERB_DELAY 6 +#define AWE_REVERB_PANNINGDELAY 7 +#define AWE_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define AWE_REVERB_NUMBERS 32 + +typedef struct awe_reverb_fx_rec { + unsigned short parms[28]; +} awe_reverb_fx_rec; + +/*----------------------------------------------------------------*/ + +/* chorus mode parameters */ +#define AWE_CHORUS_1 0 +#define AWE_CHORUS_2 1 +#define AWE_CHORUS_3 2 +#define AWE_CHORUS_4 3 +#define AWE_CHORUS_FEEDBACK 4 +#define AWE_CHORUS_FLANGER 5 +#define AWE_CHORUS_SHORTDELAY 6 +#define AWE_CHORUS_SHORTDELAY2 7 +#define AWE_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define AWE_CHORUS_NUMBERS 32 + +typedef struct awe_chorus_fx_rec { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} awe_chorus_fx_rec; + +/*----------------------------------------------------------------*/ + +/* misc mode types */ +enum { +/* 0*/ AWE_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ AWE_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ AWE_MD_VERSION, /* read only */ +/* 3*/ AWE_MD_EXCLUSIVE_SOUND, /* ignored */ +/* 4*/ AWE_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ AWE_MD_GUS_BANK, /* bank number for GUS patches (default=0) */ +/* 6*/ AWE_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ AWE_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ AWE_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ AWE_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */ +/*10*/ AWE_MD_DEF_PRESET, /* integer: default preset number (def=0) */ +/*11*/ AWE_MD_DEF_BANK, /* integer: default bank number (def=0) */ +/*12*/ AWE_MD_DEF_DRUM, /* integer: default drumset number (def=0) */ +/*13*/ AWE_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */ + AWE_MD_END, +}; + +/*----------------------------------------------------------------*/ + +/* effect parameters */ +enum { + +/* modulation envelope parameters */ +/* 0*/ AWE_FX_ENV1_DELAY, /* WORD: ENVVAL */ +/* 1*/ AWE_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */ +/* 2*/ AWE_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */ +/* 3*/ AWE_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */ +/* 4*/ AWE_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */ +/* 5*/ AWE_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */ +/* 6*/ AWE_FX_ENV1_PITCH, /* BYTE: up PEFE */ +/* 7*/ AWE_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */ + +/* volume envelope parameters */ +/* 8*/ AWE_FX_ENV2_DELAY, /* WORD: ENVVOL */ +/* 9*/ AWE_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */ +/*10*/ AWE_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */ +/*11*/ AWE_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */ +/*12*/ AWE_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */ +/*13*/ AWE_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */ + +/* LFO1 (tremolo & vibrato) parameters */ +/*14*/ AWE_FX_LFO1_DELAY, /* WORD: LFO1VAL */ +/*15*/ AWE_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */ +/*16*/ AWE_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */ +/*17*/ AWE_FX_LFO1_PITCH, /* BYTE: up FMMOD */ +/*18*/ AWE_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */ + +/* LFO2 (vibrato) parameters */ +/*19*/ AWE_FX_LFO2_DELAY, /* WORD: LFO2VAL */ +/*20*/ AWE_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */ +/*21*/ AWE_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */ + +/* Other overall effect parameters */ +/*22*/ AWE_FX_INIT_PITCH, /* SHORT: pitch offset */ +/*23*/ AWE_FX_CHORUS, /* BYTE: chorus effects send (0-255) */ +/*24*/ AWE_FX_REVERB, /* BYTE: reverb effects send (0-255) */ +/*25*/ AWE_FX_CUTOFF, /* BYTE: up IFATN */ +/*26*/ AWE_FX_FILTERQ, /* BYTE: up CCCA */ + +/* Sample / loop offset changes */ +/*27*/ AWE_FX_SAMPLE_START, /* SHORT: offset */ +/*28*/ AWE_FX_LOOP_START, /* SHORT: offset */ +/*29*/ AWE_FX_LOOP_END, /* SHORT: offset */ +/*30*/ AWE_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ +/*31*/ AWE_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ +/*32*/ AWE_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ AWE_FX_ATTEN, /* BYTE: lo IFATN */ + + AWE_FX_END, +}; + +#endif /* AWE_VOICE_H */ diff --git a/native_midi_gpl/emumidi.c b/native_midi_gpl/emumidi.c new file mode 100644 index 00000000..c52ce581 --- /dev/null +++ b/native_midi_gpl/emumidi.c @@ -0,0 +1,390 @@ +/************************************************************************ + 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 */ diff --git a/native_midi_gpl/emumidi.h b/native_midi_gpl/emumidi.h new file mode 100644 index 00000000..46596d0a --- /dev/null +++ b/native_midi_gpl/emumidi.h @@ -0,0 +1,63 @@ +/************************************************************************ + emumidi.h -- tables and includes required by emumidi.c + + 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 2139, 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. + *************************************************************************/ +#include "playmidi.h" +#ifdef linux +#include +#else +#include +#endif + +/* + * TABLE OF NEARLY EXACT FREQUENCIES FOR ALL MIDI NOTES (A=440Hz) + * the whole table is really not necessary, but it prevents some + * rounding errors by having it complete, and the cost of 128 + * integers is cheaper than the cpu cost of multiple right shifts + * of a table of twelve frequencies, and definately cheaper than + * calculating freq = 13.75 * 2^((n + 4)/12) for each note value, + * which is how this table was created. + */ + +unsigned int n_freq[128] = +{ +/* C C# D D# E F F# G G# A A# B */ + 16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, 31, + 33, 34, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, + 65, 69, 73, 78, 82, 87, 92, 98, 103, 110, 117, 123, + 131, 139, 147, 156, 165, 175, 185, 195, 207, 220, 233, 247, + 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, + 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, + 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, + 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, + 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902, + 8372, 8870, 9397, 9956,10548,11175,11840,12544,13290,14080,14917,15804, +16744,17740,18795,19912,21096,22351,23680,25088 +}; + +/* MT-32 emulation translate table */ +int mt32pgm[128] = +{ + 0, 1, 2, 4, 4, 5, 5, 3, 16, 16, 16, 16, 19, + 19, 19, 21, 6, 6, 6, 7, 7, 7, 8, 8, 62, 57, + 63, 58, 38, 38, 39, 39, 88, 33, 52, 35, 97, 100, 38, + 39, 14, 102, 68, 103, 44, 92, 46, 80, 48, 49, 51, 45, + 40, 40, 42, 42, 43, 46, 46, 24, 25, 28, 27, 104, 32, + 32, 34, 33, 36, 37, 39, 35, 79, 73, 76, 72, 74, 75, + 64, 65, 66, 67, 71, 71, 69, 70, 60, 22, 56, 59, 57, + 63, 60, 60, 58, 61, 61, 11, 11, 99, 100, 9, 14, 13, + 12, 107, 106, 77, 78, 78, 76, 111, 47, 117, 127, 115, 118, + 116, 118, 126, 121, 121, 55, 124, 120, 125, 126, 127 +}; + diff --git a/native_midi_gpl/gmvoices.h b/native_midi_gpl/gmvoices.h new file mode 100644 index 00000000..78f6439f --- /dev/null +++ b/native_midi_gpl/gmvoices.h @@ -0,0 +1,106 @@ +/******************************************************************** + gmvoices.h -- List of GM voice filenames for 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. + + ********************************************************************/ +char *gmvoice[256] = { +/* [Melodic Patches] */ + /* 000 */ "acpiano", /* 001 */ "britepno", /* 002 */ "synpiano", + /* 003 */ "honky", /* 004 */ "epiano1", /* 005 */ "epiano2", + /* 006 */ "hrpschrd", /* 007 */ "clavinet", /* 008 */ "celeste", + /* 009 */ "glocken", /* 010 */ "musicbox", /* 011 */ "vibes", + /* 012 */ "marimba", /* 013 */ "xylophon", /* 014 */ "tubebell", + /* 015 */ "santur", /* 016 */ "homeorg", /* 017 */ "percorg", + /* 018 */ "rockorg", /* 019 */ "church", /* 020 */ "reedorg", + /* 021 */ "accordn", /* 022 */ "harmonca", /* 023 */ "concrtna", + /* 024 */ "nyguitar", /* 025 */ "acguitar", /* 026 */ "jazzgtr", + /* 027 */ "cleangtr", /* 028 */ "mutegtr", /* 029 */ "odguitar", + /* 030 */ "distgtr", /* 031 */ "gtrharm", /* 032 */ "acbass", + /* 033 */ "fngrbass", /* 034 */ "pickbass", /* 035 */ "fretless", + /* 036 */ "slapbas1", /* 037 */ "slapbas2", /* 038 */ "synbass1", + /* 039 */ "synbass2", /* 040 */ "violin", /* 041 */ "viola", + /* 042 */ "cello", /* 043 */ "contraba", /* 044 */ "marcato", + /* 045 */ "pizzcato", /* 046 */ "harp", /* 047 */ "timpani", + /* 048 */ "marcato", /* 049 */ "slowstr", /* 050 */ "synstr1", + /* 051 */ "synstr2", /* 052 */ "choir", /* 053 */ "doo", + /* 054 */ "voices", /* 055 */ "orchhit", /* 056 */ "trumpet", + /* 057 */ "trombone", /* 058 */ "tuba", /* 059 */ "mutetrum", + /* 060 */ "frenchrn", /* 061 */ "hitbrass", /* 062 */ "synbras1", + /* 063 */ "synbras2", /* 064 */ "sprnosax", /* 065 */ "altosax", + /* 066 */ "tenorsax", /* 067 */ "barisax", /* 068 */ "oboe", + /* 069 */ "englhorn", /* 070 */ "bassoon", /* 071 */ "clarinet", + /* 072 */ "piccolo", /* 073 */ "flute", /* 074 */ "recorder", + /* 075 */ "woodflut", /* 076 */ "bottle", /* 077 */ "shakazul", + /* 078 */ "whistle", /* 079 */ "ocarina", /* 080 */ "sqrwave", + /* 081 */ "sawwave", /* 082 */ "calliope", /* 083 */ "chiflead", + /* 084 */ "charang", /* 085 */ "voxlead", /* 086 */ "lead5th", + /* 087 */ "basslead", /* 088 */ "fantasia", /* 089 */ "warmpad", + /* 090 */ "polysyn", /* 091 */ "ghostie", /* 092 */ "bowglass", + /* 093 */ "metalpad", /* 094 */ "halopad", /* 095 */ "sweeper", + /* 096 */ "aurora", /* 097 */ "soundtrk", /* 098 */ "crystal", + /* 099 */ "atmosphr", /* 100 */ "freshair", /* 101 */ "unicorn", + /* 102 */ "sweeper", /* 103 */ "startrak", /* 104 */ "sitar", + /* 105 */ "banjo", /* 106 */ "shamisen", /* 107 */ "koto", + /* 108 */ "kalimba", /* 109 */ "bagpipes", /* 110 */ "fiddle", + /* 111 */ "shannai", /* 112 */ "carillon", /* 113 */ "agogo", + /* 114 */ "steeldrm", /* 115 */ "woodblk", /* 116 */ "taiko", + /* 117 */ "toms", /* 118 */ "syntom", /* 119 */ "revcym", + /* 120 */ "fx-fret", /* 121 */ "fx-blow", /* 122 */ "seashore", + /* 123 */ "jungle", /* 124 */ "telephon", /* 125 */ "helicptr", + /* 126 */ "applause", /* 127 */ "ringwhsl", +/* [Drum Patches] */ + /* C 0 */ NULL, /* C#0 */ NULL, /* D 0 */ NULL, + /* D#0 */ NULL, /* E 0 */ NULL, /* F 0 */ NULL, + /* F#0 */ NULL, /* G 0 */ NULL, /* G#0 */ NULL, + /* A 0 */ NULL, /* A#0 */ NULL, /* B 0 */ NULL, + /* C 1 */ NULL, /* C#1 */ NULL, /* D 1 */ NULL, + /* D#1 */ NULL, /* E 1 */ NULL, /* F 1 */ NULL, + /* F#1 */ NULL, /* G 1 */ NULL, /* G#1 */ NULL, + /* A 1 */ NULL, /* A#1 */ NULL, /* B 1 */ NULL, + /* C 2 */ NULL, /* C#2 */ NULL, /* D 2 */ NULL, + /* D#2 */ "highq", /* E 2 */ "slap", /* F 2 */ "scratch1", + /* F#2 */ "scratch2", /* G 2 */ "sticks", /* G#2 */ "sqrclick", + /* A 2 */ "metclick", /* A#2 */ "metbell", /* B 2 */ "kick1", + /* C 3 */ "kick2", /* C#3 */ "stickrim", /* D 3 */ "snare1", + /* D#3 */ "claps", /* E 3 */ "snare2", /* F 3 */ "tomlo2", + /* F#3 */ "hihatcl", /* G 3 */ "tomlo1", /* G#3 */ "hihatpd", + /* A 3 */ "tommid2", /* A#3 */ "hihatop", /* B 3 */ "tommid1", + /* C 4 */ "tomhi2", /* C#4 */ "cymcrsh1", /* D 4 */ "tomhi1", + /* D#4 */ "cymride1", /* E 4 */ "cymchina", /* F 4 */ "cymbell", + /* F#4 */ "tamborin", /* G 4 */ "cymsplsh", /* G#4 */ "cowbell", + /* A 4 */ "cymcrsh2", /* A#4 */ "vibslap", /* B 4 */ "cymride2", + /* C 5 */ "bongohi", /* C#5 */ "bongolo", /* D 5 */ "congahi1", + /* D#5 */ "congahi2", /* E 5 */ "congalo", /* F 5 */ "timbaleh", + /* F#5 */ "timbalel", /* G 5 */ "agogohi", /* G#5 */ "agogolo", + /* A 5 */ "cabasa", /* A#5 */ "maracas", /* B 5 */ "whistle1", + /* C 6 */ "whistle2", /* C#6 */ "guiro1", /* D 6 */ "guiro2", + /* D#6 */ "clave", /* E 6 */ "woodblk1", /* F 6 */ "woodblk2", + /* F#6 */ "cuica1", /* G 6 */ "cuica2", /* G#6 */ "triangl1", + /* A 6 */ "triangl2", /* A#6 */ "shaker", /* B 6 */ "jingles", + /* C 7 */ "belltree", /* C#7 */ "castinet", /* D 7 */ "surdo1", + /* D#7 */ "surdo2", /* E 7 */ NULL, /* F 7 */ NULL, + /* F#7 */ NULL, /* G 7 */ NULL, /* G#7 */ NULL, + /* A 7 */ NULL, /* A#7 */ NULL, /* B 7 */ NULL, + /* C 8 */ NULL, /* C#8 */ NULL, /* D 8 */ NULL, + /* D#8 */ NULL, /* E 8 */ NULL, /* F 8 */ NULL, + /* F#8 */ NULL, /* G 8 */ NULL, /* G#8 */ NULL, + /* A 8 */ NULL, /* A#8 */ NULL, /* B 8 */ NULL, + /* C 9 */ NULL, /* C#9 */ NULL, /* D 9 */ NULL, + /* D#9 */ NULL, /* E 9 */ NULL, /* F 9 */ NULL, + /* F#9 */ NULL, /* G 9 */ NULL, /* G#9 */ NULL, + /* A 9 */ NULL, /* A#9 */ NULL, /* B 9 */ NULL, + /* C 10*/ NULL, /* C#10*/ NULL, /* D 10*/ NULL, + /* D#10*/ NULL, /* E 10*/ NULL, /* F 10*/ NULL, + /* F#10*/ NULL, /* G 10*/ NULL +}; diff --git a/native_midi_gpl/gsvoices.h b/native_midi_gpl/gsvoices.h new file mode 100644 index 00000000..d714a690 --- /dev/null +++ b/native_midi_gpl/gsvoices.h @@ -0,0 +1,39 @@ +/************************************************************** + * gsvoices.h - list of gs voice names - all 654 of SC88 * + **************************************************************/ +#ifndef GSVOICES_H +#define GSVOICES_H +char *gsvoice[128] = { +"Piano 1 ", "Piano 2 ", "Piano 3 ", "Honky-tonk ", +"E.Piano 1 ", "E.Piano 2 ", "Harpsichord ", "Clav. ", +"Celesta ", "Glockenspl ", "Music Box ", "Vibraphone ", +"Marimba ", "Xylophone ", "Tubularbell ", "Santur ", +"Organ 1 ", "Organ 2 ", "Organ 3 ", "Church Org1 ", +"Reed Organ ", "Accordion F ", "Harmonica ", "Bandoneon ", +"Nylon Gt. ", "Steel Gt. ", "Jazz Gt. ", "Clean Gt. ", +"Muted Gt. ", "OverdriveGt ", "Dist.Gt. ", "Gt.Harmonix ", +"Acoustic Bs ", "Fingered Bs ", "Picked Bass ", "Fretless Bs ", +"Slap Bass 1 ", "Slap Bass 2 ", "Syn.Bass 1 ", "Syn.Bass 2 ", +"Violin ", "Viola ", "Cello ", "Contrabass ", +"Tremolo Str ", "Pizzicato ", "Harp ", "Timpani ", +"Strings ", "SlowStrings ", "SynStrings1 ", "SynStrings2 ", +"Choir Aahs ", "Voice Oohs ", "SynVox ", "Orchest.Hit ", +"Trumpet ", "Trombone ", "Tuba ", "MuteTrumpet ", +"French Horn ", "Brass 1 ", "Syn.Brass 1 ", "Syn.Brass 2 ", +"Soprano Sax ", "Alto Sax ", "Tenor Sax ", "BaritoneSax ", +"Oboe ", "EnglishHorn ", "Bassoon ", "Clarinet ", +"Piccolo ", "Flute ", "Recorder ", "Pan Flute ", +"Bottle Blow ", "Shakuhachi ", "Whistle ", "Ocarina ", +"Square Wave ", "Saw Wave ", "SynCalliope ", "ChifferLead ", +"Charang ", "Solo Vox ", "5th Saw ", "Bass & Lead ", +"Fantasia ", "Warm Pad ", "Polysynth ", "Space Voice ", +"Bowed Glass ", "Metal Pad ", "Halo Pad ", "Sweep Pad ", +"Ice Rain ", "Soundtrack ", "Crystal ", "Atmosphere ", +"Brightness ", "Goblin ", "Echo Drops ", "Star Theme ", +"Sitar ", "Banjo ", "Shamisen ", "Koto ", +"Kalimba ", "Bagpipe ", "Fiddle ", "Shanai ", +"Tinkle Bell ", "Agogo ", "Steel Drums ", "Woodblock ", +"Taiko ", "Melo. Tom 1 ", "Synth Drum ", "Reverse Cym ", +"Gt.FretNoiz ", "BreathNoise ", "Seashore ", "Bird ", +"Telephone 1 ", "Helicopter ", "Applause ", "Gun Shot " }; +#endif diff --git a/native_midi_gpl/native_midi_gpl.c b/native_midi_gpl/native_midi_gpl.c new file mode 100644 index 00000000..5c3f6779 --- /dev/null +++ b/native_midi_gpl/native_midi_gpl.c @@ -0,0 +1,488 @@ +/*************************************************************************** + native_midi_lnx.c + ----------------- + + copyright : (C) 2002 by Peter Kuȇk + email : kutak@stonline.sk + ***************************************************************************/ + +/* in this file is used code from PlayMidi Copyright (C) 1994-1996 Nathan I. Laredo */ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#if defined(linux) || defined(__FreeBSD__) + +#ifndef __FreeBSD__ +#include +#endif +#include +#include +#include +#include +#include +#include "SDL_thread.h" +#include "native_midi.h" +#include "playmidi.h" + +SEQ_DEFINEBUF(SEQUENCERBLOCKSIZE); + +int play_fm = 0, play_gus = 0, play_ext = 0, play_awe = 0; +int opl3_patch_aviable = 0, fm_patch_aviable = 0; + +struct miditrack seq[MAXTRKS]; +struct synth_info card_info[MAX_CARDS]; + +int FORCE_EXT_DEV = -1; +int chanmask = 0xffff, perc = PERCUSSION; +int dochan = 1, force8bit = 0, wantopl3 = FM_DEFAULT_MODE; +int patchloaded[256], fmloaded[256], useprog[16], usevol[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +int reverb = 0, chorus = 0, nrsynths, nrmidis; +int sb_dev = -1, gus_dev = -1, ext_dev = -1, awe_dev = -1, p_remap = 0; +int seqfd, MT32 = 0; +FILE *mfd; +unsigned long int default_tempo; +float skew = 1.0; +char ImPlaying = 0; +SDL_Thread *playevents_thread=NULL; + +extern int ntrks; +extern char *gmvoice[256]; +extern int mt32pgm[128]; +extern int note_vel[16][128]; +extern int playevents(); +extern int gus_load(int); +extern int readmidi(unsigned char *, off_t); +extern void loadfm(); + +void seqbuf_dump(); +int synth_setup(); + +struct _NativeMidiSong +{ + char *filebuf; + unsigned long int file_size; +}; + +int native_midi_detect() +{ + + int sbfd; + int ret=0; + + /* Open sequencer device */ + if ((seqfd = open(SEQUENCER_DEV, O_WRONLY, 0)) < 0) + { + perror("open " SEQUENCER_DEV); + return 0; + } + + gus_dev = -1; + sb_dev = -1; + ext_dev = -1; + awe_dev = -1; + play_fm = 0; + play_gus = 0; + play_ext = 0; + play_awe = 0; + + opl3_patch_aviable = 0; + fm_patch_aviable = 0; + + sbfd = open(SBMELODIC, O_RDONLY, 0); + if (sbfd != -1) + { + close(sbfd); + sbfd = open(SBDRUMS, O_RDONLY, 0); + if (sbfd != -1) + { + close(sbfd); + fm_patch_aviable = 1; + } + } + + sbfd = open(O3MELODIC, O_RDONLY, 0); + if (sbfd != -1) + { + close(sbfd); + sbfd = open(O3DRUMS, O_RDONLY, 0); + if (sbfd != -1) + { + close(sbfd); + opl3_patch_aviable = 1; + } + } + + ret=synth_setup(); + + /* Close sequencer device */ + close(seqfd); + + return ret; +} + +NativeMidiSong *native_midi_loadsong(char *midifile) +{ + NativeMidiSong *song = NULL; + char *extra; + int piped = 0; + struct stat info; + + song = malloc(sizeof(NativeMidiSong)); + if (!song) + { + return NULL; + }; + if (stat(midifile, &info) == -1) + { + if ((extra = malloc(strlen(midifile) + 4)) == NULL) + { + goto end; + } + sprintf(extra, "%s.mid", midifile); + if (stat(extra, &info) == -1) + { + free(extra); + goto end; + } + if ((mfd = fopen(extra, "r")) == NULL) + { + free(extra); + goto end; + } + free(extra); + } else + { + char *ext = strrchr(midifile, '.'); + if (ext && strcmp(ext, ".gz") == 0) + { + char temp[1024]; + piped = 1; + sprintf(temp, "gzip -l %s", midifile); + if ((mfd = popen(temp, "r")) == NULL) + { + goto end; + } + fgets(temp, sizeof(temp), mfd); /* skip 1st line */ + fgets(temp, sizeof(temp), mfd); + strtok(temp, " "); /* compressed size */ + info.st_size = atoi(strtok(NULL, " ")); /* original size */ + pclose(mfd); + sprintf(temp, "gzip -d -c %s",midifile); + if ((mfd = popen(temp, "r")) == NULL) + { + goto end; + } + }else if ((mfd = fopen(midifile, "r")) == NULL) + { + goto end; + } + } + if ((song->filebuf = malloc(info.st_size)) == NULL) + { + if (piped) + { + pclose(mfd); + }else + { + fclose(mfd); + } + goto end; + } + song->file_size=info.st_size; + fread(song->filebuf, 1, info.st_size, mfd); + if (piped) + { + pclose(mfd); + } else + { + fclose(mfd); + } + + return song; + +end: + free(song); + return NULL; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + free(song->filebuf); + free(song); +} + +void native_midi_start(NativeMidiSong *song) +{ + + int i, error = 0, j; + + + for (i = 0; i < 16; i++) + { + useprog[i] = 0; /* reset options */ + } + + /* Open sequencer device */ + if ((seqfd = open(SEQUENCER_DEV, O_WRONLY, 0)) < 0) + { + perror("open " SEQUENCER_DEV); + goto eend; + } + if(!synth_setup()) { goto end;}; + + + if (play_gus) + { + gus_load(-1); + } + default_tempo = 500000; + /* error holds number of tracks read */ + error = readmidi(song->filebuf, song->file_size); + if (play_gus && error > 0) + { + int i; /* need to keep other i safe */ +#define CMD (seq[i].data[j] & 0xf0) +#define CHN (seq[i].data[j] & 0x0f) +#define PGM (seq[i].data[j + 1]) + /* REALLY STUPID way to preload GUS, but it works */ + for (i = 0; i < ntrks; i++) + for (j = 0; j < seq[i].length - 5; j++) + if (ISGUS(CHN) && !(PGM & 0x80) && + ((CMD == MIDI_PGM_CHANGE && !ISPERC(CHN)) + || (CMD == MIDI_NOTEON && ISPERC(CHN)))) + gus_load(ISPERC(CHN) ? PGM + 128 : + useprog[CHN] ? useprog[CHN] - 1 : + MT32 ? mt32pgm[PGM] : PGM); + /* make sure that some program was loaded to use */ + for (j = 0; patchloaded[j] != 1 && j < 128; j++); + if (j > 127) + gus_load(0); + } + /* if there's an error skip to next file */ + if (error > 0) /* error holds number of tracks read */ + { + ImPlaying=1; + playevents_thread=SDL_CreateThread(playevents,NULL); + } + +end: +eend: +} + +void native_midi_stop() +{ + /* Close sequencer device */ + close(seqfd); +} + +int native_midi_active() +{ + return ImPlaying; +} + +void native_midi_setvolume(int volume) +{ +} + +char *native_midi_error() +{ + return "stala sa chyba"; +} + +void seqbuf_dump() +{ + if (_seqbufptr) + if (write(seqfd, _seqbuf, _seqbufptr) == -1) { + perror("write " SEQUENCER_DEV); + return; + } + _seqbufptr = 0; +} + +int synth_setup() +{ + int i; + char *nativemusicenv = getenv("SDL_NATIVE_MUSIC"); + char *extmidi=getenv("SDL_NATIVE_MUSIC_EXT"); + + if(extmidi) + { + + FORCE_EXT_DEV = atoi(extmidi); + printf("EXT midi %s , %d \n",extmidi,FORCE_EXT_DEV); + } + + if (ioctl(seqfd, SNDCTL_SEQ_NRSYNTHS, &nrsynths) == -1) + { + fprintf(stderr, "there is no soundcard\n"); + return 0; + } + for (i = 0; i < nrsynths; i++) + { + card_info[i].device = i; + if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &card_info[i]) == -1) + { + fprintf(stderr, "cannot get info on soundcard\n"); + perror(SEQUENCER_DEV); + return 0; + } + card_info[i].device = i; + if (card_info[i].synth_type == SYNTH_TYPE_SAMPLE + && card_info[i].synth_subtype == SAMPLE_TYPE_GUS) + { + gus_dev = i; + }else if (card_info[i].synth_type == SYNTH_TYPE_SAMPLE + && card_info[i].synth_subtype == SAMPLE_TYPE_AWE32) + { + awe_dev = i; + }else if (card_info[i].synth_type == SYNTH_TYPE_FM) + { + sb_dev = i; + if (play_fm) + loadfm(); + if (wantopl3) + { + card_info[i].nr_voices = 12; /* we have 12 with 4-op */ + } + } + } + + if (gus_dev >= 0) { + if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + { + perror("Sample reset"); + return 0; + } + } + if (ioctl(seqfd, SNDCTL_SEQ_NRMIDIS, &nrmidis) == -1) + { + fprintf(stderr, "can't get info about midi ports\n"); + return 0; + } + if (nrmidis > 0) { + if (FORCE_EXT_DEV >= 0) + ext_dev = FORCE_EXT_DEV; + else + ext_dev = nrmidis - 1; + } + + if( nativemusicenv ) /* select device by SDL_NATIVE_MUSIC */ + { + if(strcasecmp(nativemusicenv,"GUS") == 0) + { + if( gus_dev >= 0 ) + { + play_gus = 1; + awe_dev = -1; + sb_dev = -1; + ext_dev = -1; + return 1; + }else + { + play_gus = 0; + return 0; + } + }else if(strcasecmp(nativemusicenv,"AWE") == 0) + { + if( awe_dev >= 0 ) + { + play_awe = 1; + gus_dev = -1; + sb_dev = -1; + ext_dev = -1; + return 1; + }else + { + play_awe = 0; + return 0; + } + }else if(strcasecmp(nativemusicenv,"FM") == 0) + { + if( sb_dev >= 0 && fm_patch_aviable ) + { + play_fm = 1; + gus_dev = -1; + awe_dev = -1; + ext_dev = -1; + wantopl3 = 0; + return 1; + }else + { + play_fm = 0; + return 0; + } + }else if(strcasecmp(nativemusicenv,"OPL3") == 0) + { + if( sb_dev >= 0 && opl3_patch_aviable ) + { + play_fm = 1; + gus_dev = -1; + awe_dev = -1; + ext_dev = -1; + wantopl3 = 1; + return 1; + }else + { + play_fm = 0; + return 0; + } + }else if(strcasecmp(nativemusicenv,"EXT") == 0) + { + if( ext_dev >= 0 ) + { + play_ext = 1; + gus_dev = -1; + awe_dev = -1; + sb_dev = -1; + return 1; + }else + { + play_ext = 0; + return 0; + } + } + } + /* autoselect best device */ + if( gus_dev >= 0 ) + { + play_gus = 1; + awe_dev = -1; + sb_dev = -1; + ext_dev = -1; + return 1; + } + if( awe_dev >= 0 ) + { + play_awe = 1; + gus_dev = -1; + sb_dev = -1; + ext_dev = -1; + return 1; + } + if( sb_dev >= 0 && fm_patch_aviable ) + { + play_fm = 1; + gus_dev = -1; + awe_dev = -1; + ext_dev = -1; + return 2; /* return 1 if use FM befor Timidity */ + } + if( ext_dev >= 0 ) + { + play_ext = 1; + gus_dev = -1; + awe_dev = -1; + sb_dev = -1; + return 3; + } + return 0; +} + +#endif /* linux || FreeBSD */ diff --git a/native_midi_gpl/patchload.c b/native_midi_gpl/patchload.c new file mode 100644 index 00000000..ee6518d1 --- /dev/null +++ b/native_midi_gpl/patchload.c @@ -0,0 +1,412 @@ +/************************************************************************ + patchload.c -- loads patches for playmidi package + Some of this code was adapted from code written by Hannu Solovainen + + 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 "playmidi.h" +#ifdef linux +#include +#else +#include +#endif +#include +#include +#include +#include "gmvoices.h" + +SEQ_USE_EXTBUF(); +extern int play_gus, play_sb, play_ext, playing, verbose, force8bit; +extern int reverb, fmloaded[256], patchloaded[256]; +extern int gus_dev, sb_dev, ext_dev, seqfd, wantopl3; +extern struct synth_info card_info[MAX_CARDS]; + +static int use8bit = 0; + +struct pat_header { + char magic[12]; + char version[10]; + char description[60]; + unsigned char instruments; + char voices; + char channels; + unsigned short nr_waveforms; + unsigned short master_volume; + unsigned int data_size; +}; + +struct sample_header { + char name[7]; + unsigned char fractions; + int len; + int loop_start; + int loop_end; + unsigned short base_freq; + int low_note; + int high_note; + int base_note; + short detune; + unsigned char panning; + + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + + char modes; + + short scale_frequency; + unsigned short scale_factor; +}; + +struct patch_info *patch; +int spaceleft, totalspace; + +void gus_reload_8_bit(); + +void gus_load(pgm) +int pgm; +{ + int i, j, patfd, offset; + struct pat_header header; + struct sample_header sample; + char buf[256], name[256]; + struct stat info; + + if (pgm < 0) { + use8bit = force8bit; + GUS_NUMVOICES(gus_dev, (card_info[gus_dev].nr_voices = 32)); + SEQ_DUMPBUF(); + for (i = 0; i < 256; i++) + patchloaded[i] = 0; + if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + { + /* error: should quit */ + } + spaceleft = gus_dev; + ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &spaceleft); + totalspace = spaceleft; + } + + if (patchloaded[pgm] < 0) + return; + + if (patchloaded[pgm] == 1) + return; + + if (gmvoice[pgm] == NULL) { + patchloaded[pgm] = -1; + return; + } + sprintf(name, PATCH_PATH1 "/%s.pat", gmvoice[pgm]); + + if (stat(name, &info) == -1) { + sprintf(name, PATCH_PATH2 "/%s.pat", gmvoice[pgm]); + if (stat(name, &info) == -1) + return; + } + if ((patfd = open(name, O_RDONLY, 0)) == -1) + return; + if (spaceleft < info.st_size) { + if (!use8bit) + gus_reload_8_bit(); + if (use8bit) + if (spaceleft < info.st_size / 2) { + close(patfd); + patchloaded[pgm] = -1; /* no space for patch */ + return; + } + } + if (read(patfd, buf, 0xef) != 0xef) { + close(patfd); + return; + } + memcpy((char *) &header, buf, sizeof(header)); + + if (strncmp(header.magic, "GF1PATCH110", 12)) { + close(patfd); + return; + } + if (strncmp(header.version, "ID#000002", 10)) { + close(patfd); + return; + } + header.nr_waveforms = *(unsigned short *) &buf[85]; + header.master_volume = *(unsigned short *) &buf[87]; + + offset = 0xef; + + for (i = 0; i < header.nr_waveforms; i++) { + + if (lseek(patfd, offset, 0) == -1) { + close(patfd); + return; + } + if (read(patfd, &buf, sizeof(sample)) != sizeof(sample)) { + close(patfd); + return; + } + memcpy((char *) &sample, buf, sizeof(sample)); + + /* + * Since some fields of the patch record are not 32bit aligned, we must + * handle them specially. + */ + sample.low_note = *(int *) &buf[22]; + sample.high_note = *(int *) &buf[26]; + sample.base_note = *(int *) &buf[30]; + sample.detune = *(short *) &buf[34]; + sample.panning = (unsigned char) buf[36]; + + memcpy(sample.envelope_rate, &buf[37], 6); + memcpy(sample.envelope_offset, &buf[43], 6); + + sample.tremolo_sweep = (unsigned char) buf[49]; + sample.tremolo_rate = (unsigned char) buf[50]; + sample.tremolo_depth = (unsigned char) buf[51]; + + sample.vibrato_sweep = (unsigned char) buf[52]; + sample.vibrato_rate = (unsigned char) buf[53]; + sample.vibrato_depth = (unsigned char) buf[54]; + sample.modes = (unsigned char) buf[55]; + sample.scale_frequency = *(short *) &buf[56]; + sample.scale_factor = *(unsigned short *) &buf[58]; + + offset = offset + 96; + patch = (struct patch_info *) malloc(sizeof(*patch) + sample.len); + + if (patch == NULL) { + close(patfd); + return; + } + patch->key = GUS_PATCH; + patch->device_no = gus_dev; + patch->instr_no = pgm; + patch->mode = sample.modes | WAVE_TREMOLO | + WAVE_VIBRATO | WAVE_SCALE; + patch->len = (use8bit ? sample.len / 2 : sample.len); + patch->loop_start = + (use8bit ? sample.loop_start / 2 : sample.loop_start); + patch->loop_end = (use8bit ? sample.loop_end / 2 : sample.loop_end); + patch->base_note = sample.base_note; + patch->high_note = sample.high_note; + patch->low_note = sample.low_note; + patch->base_freq = sample.base_freq; + patch->detuning = sample.detune; + patch->panning = (sample.panning - 7) * 16; + + memcpy(patch->env_rate, sample.envelope_rate, 6); + for (j = 0; j < 6; j++) /* tone things down slightly */ + patch->env_offset[j] = + (736 * sample.envelope_offset[j] + 384) / 768; + + if (reverb) + if (pgm < 120) + patch->env_rate[3] = (2 << 6) | (12 - (reverb >> 4)); + else if (pgm > 127) + patch->env_rate[1] = (3 << 6) | (63 - (reverb >> 1)); + + patch->tremolo_sweep = sample.tremolo_sweep; + patch->tremolo_rate = sample.tremolo_rate; + patch->tremolo_depth = sample.tremolo_depth; + + patch->vibrato_sweep = sample.vibrato_sweep; + patch->vibrato_rate = sample.vibrato_rate; + patch->vibrato_depth = sample.vibrato_depth; + + patch->scale_frequency = sample.scale_frequency; + patch->scale_factor = sample.scale_factor; + + patch->volume = header.master_volume; + + if (lseek(patfd, offset, 0) == -1) { + close(patfd); + return; + } + if (read(patfd, patch->data, sample.len) != sample.len) { + close(patfd); + return; + } + if (patch->mode & WAVE_16_BITS && use8bit) { + patch->mode &= ~WAVE_16_BITS; + /* cut out every other byte to make 8-bit data from 16-bit */ + for (j = 0; j < patch->len; j++) + patch->data[j] = patch->data[1 + j * 2]; + } + SEQ_WRPATCH(patch, sizeof(*patch) + patch->len); + free(patch); + offset = offset + sample.len; + } + close(patfd); + spaceleft = gus_dev; + ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &spaceleft); + patchloaded[pgm] = 1; + return; +} + +void gus_reload_8_bit() +{ + int i; + + if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) + { + /* error: should quit */ + } + spaceleft = gus_dev; + ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &spaceleft); + totalspace = spaceleft; + use8bit = 1; + for (i = 0; i < 256; i++) + if (patchloaded[i] > 0) { + patchloaded[i] = 0; + gus_load(i); + } +} + +void adjustfm(buf, key) +char *buf; +int key; +{ + unsigned char pan = ((rand() % 3) + 1) << 4; + + if (key == FM_PATCH) { + buf[39] &= 0xc0; + if (buf[46] & 1) + buf[38] &= 0xc0; + buf[46] = (buf[46] & 0xcf) | pan; + if (reverb) { + unsigned val; + val = buf[43] & 0x0f; + if (val > 0) + val--; + buf[43] = (buf[43] & 0xf0) | val; + } + } else { + int mode; + if (buf[46] & 1) + mode = 2; + else + mode = 0; + if (buf[57] & 1) + mode++; + buf[50] &= 0xc0; + if (mode == 3) + buf[49] &= 0xc0; + if (mode == 1) + buf[39] &= 0xc0; + if (mode == 2 || mode == 3) + buf[38] &= 0xc0; + buf[46] = (buf[46] & 0xcf) | pan; + buf[57] = (buf[57] & 0xcf) | pan; + if (mode == 1 && reverb) { + unsigned val; + val = buf[43] & 0x0f; + if (val > 0) + val--; + buf[43] = (buf[43] & 0xf0) | val; + val = buf[54] & 0x0f; + if (val > 0) + val--; + buf[54] = (buf[54] & 0xf0) | val; + } + } +} + +void loadfm() +{ + int sbfd, i, n, voice_size, data_size; + char buf[60]; + struct sbi_instrument instr; + + for (i = 0; i < 256; i++) + fmloaded[i] = 0; + srand(getpid()); + if (wantopl3) { + voice_size = 60; + sbfd = open(O3MELODIC, O_RDONLY, 0); + } else { + voice_size = 52; + sbfd = open(SBMELODIC, O_RDONLY, 0); + } + if (sbfd == -1) + { + /* error: should quit */ + } + instr.device = sb_dev; + + for (i = 0; i < 128; i++) { + if (read(sbfd, buf, voice_size) != voice_size) + { + /* error: should quit */ + } + instr.channel = i; + + if (strncmp(buf, "4OP", 3) == 0) { + instr.key = OPL3_PATCH; + data_size = 22; + } else { + instr.key = FM_PATCH; + data_size = 11; + } + + fmloaded[i] = instr.key; + + adjustfm(buf, instr.key); + for (n = 0; n < 32; n++) + instr.operators[n] = (n < data_size) ? buf[36 + n] : 0; + + SEQ_WRPATCH(&instr, sizeof(instr)); + } + close(sbfd); + + if (wantopl3) + sbfd = open(O3DRUMS, O_RDONLY, 0); + else + sbfd = open(SBDRUMS, O_RDONLY, 0); + + for (i = 128; i < 175; i++) { + if (read(sbfd, buf, voice_size) != voice_size) + { + /* error: should quit */ + } + instr.channel = i; + + if (strncmp(buf, "4OP", 3) == 0) { + instr.key = OPL3_PATCH; + data_size = 22; + } else { + instr.key = FM_PATCH; + data_size = 11; + } + fmloaded[i] = instr.key; + + adjustfm(buf, instr.key); + for (n = 0; n < 32; n++) + instr.operators[n] = (n < data_size) ? buf[n + 36] : 0; + + SEQ_WRPATCH(&instr, sizeof(instr)); + } + close(sbfd); +} +#endif /* linux || FreeBSD */ diff --git a/native_midi_gpl/playevents.c b/native_midi_gpl/playevents.c new file mode 100644 index 00000000..9f9ec8ef --- /dev/null +++ b/native_midi_gpl/playevents.c @@ -0,0 +1,217 @@ +/************************************************************************ + playevents.c -- actually sends sorted list of events to device + + 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 "playmidi.h" +#include + +extern int seq_set_patch(int, int); +extern void seq_key_pressure(int, int, int, int); +extern void seq_start_note(int, int, int, int); +extern void seq_stop_note(int, int, int, int); +extern void seq_control(int, int, int, int); +extern void seq_chn_pressure(int, int, int); +extern void seq_bender(int, int, int, int); +extern void seq_reset(); + +SEQ_USE_EXTBUF(); +extern int division, ntrks, format; +extern int gus_dev, ext_dev, sb_dev, awe_dev, perc, seqfd, p_remap; +extern int play_gus, play_fm, play_ext, play_awe, reverb, chorus, chanmask; +extern int usevol[16]; +extern struct miditrack seq[MAXTRKS]; +extern float skew; +extern unsigned long int default_tempo; +extern char ImPlaying,StopPlease; +extern void load_sysex(int, unsigned char *, int); + +unsigned long int ticks, tempo; +struct timeval start_time; + +unsigned long int rvl(s) +struct miditrack *s; +{ + register unsigned long int value = 0; + register unsigned char c; + + if (s->index < s->length && ((value = s->data[(s->index)++]) & 0x80)) { + value &= 0x7f; + do { + if (s->index >= s->length) + c = 0; + else + value = (value << 7) + + ((c = s->data[(s->index)++]) & 0x7f); + } while (c & 0x80); + } + return (value); +} + +/* indexed by high nibble of command */ +int cmdlen[16] = +{0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0}; + +#define CMD seq[track].running_st +#define TIME seq[track].ticks +#define CHN (CMD & 0xf) +#define NOTE data[0] +#define VEL data[1] + +int playevents() +{ + unsigned long int tempo = default_tempo, lasttime = 0; + unsigned int lowtime, track, best, length, loaded; + unsigned char *data; + double current = 0.0, dtime = 0.0; + int use_dev, play_status, playing = 1; + + seq_reset(); + gettimeofday(&start_time, NULL); /* for synchronization */ + for (track = 0; track < ntrks; track++) { + seq[track].index = seq[track].running_st = 0; + seq[track].ticks = rvl(&seq[track]); + } + for (best = 0; best < 16; best++) { + if (ISAWE(best)) + use_dev = awe_dev; + else if (ISGUS(best)) + use_dev = gus_dev; + else if (ISFM(best)) + use_dev = sb_dev; + else + use_dev = ext_dev; + seq_control(use_dev, best, CTL_BANK_SELECT, 0); + seq_control(use_dev, best, CTL_EXT_EFF_DEPTH, reverb); + seq_control(use_dev, best, CTL_CHORUS_DEPTH, chorus); + seq_control(use_dev, best, CTL_MAIN_VOLUME, 127); + seq_chn_pressure(use_dev, best, 127); + seq_control(use_dev, best, 0x4a, 127); + } + SEQ_START_TIMER(); + SEQ_DUMPBUF(); + while (playing) { + lowtime = ~0; + for (best = track = 0; track < ntrks; track++) + if (seq[track].ticks < lowtime) { + best = track; + lowtime = TIME; + } + if (lowtime == ~0) + break; /* no more data to read */ + track = best; + if (ISMIDI(CHN)) + use_dev = ext_dev; + else if (ISAWE(CHN)) + use_dev = awe_dev; + else if (ISGUS(CHN)) + use_dev = gus_dev; + else + use_dev = sb_dev; + + /* this section parses data in midi file buffer */ + if ((seq[track].data[seq[track].index] & 0x80) && + (seq[track].index < seq[track].length)) + CMD = seq[track].data[seq[track].index++]; + if (CMD == 0xff && seq[track].index < seq[track].length) + CMD = seq[track].data[seq[track].index++]; + if (CMD > 0xf7) /* midi real-time message (ignored) */ + length = 0; + else if (!(length = cmdlen[(CMD & 0xf0) >> 4])) + length = rvl(&seq[track]); + + if (seq[track].index + length < seq[track].length) { + /* use the parsed midi data */ + data = &(seq[track].data[seq[track].index]); + if (CMD == set_tempo) + tempo = ((*(data) << 16) | (data[1] << 8) | data[2]); + if (TIME > lasttime) { + if (division > 0) { + dtime = ((double) ((TIME - lasttime) * (tempo / 10000)) / + (double) (division)) * skew; + current += dtime; + lasttime = TIME; + } else if (division < 0) + current = ((double) TIME / + ((double) ((division & 0xff00 >> 8) * + (division & 0xff)) * 10000.0)) * skew; + /* stop if there's more than 40 seconds of nothing */ + if (dtime > 4096.0) + playing = 0; + else if ((int) current > ticks) { + SEQ_WAIT_TIME((ticks = (int) current)); + SEQ_DUMPBUF(); + } + } + if (CMD > 0x7f && CMD < 0xf0 && ISPERC(CHN) && p_remap) { + CMD &= 0xf0; + CMD |= (p_remap - 1); + } + loaded = 0; /* for patch setting failures */ + if (playing && CMD > 0x7f && ISPLAYING(CHN)) + switch (CMD & 0xf0) { + case MIDI_KEY_PRESSURE: + if (ISPERC(CHN) && VEL && (!ISMIDI(CHN)&&!ISAWE(CHN))) + loaded = seq_set_patch(CHN, NOTE + 128); + if (loaded != -1) + seq_key_pressure(use_dev, CHN, NOTE, VEL); + break; + case MIDI_NOTEON: + if (ISPERC(CHN) && VEL && (!ISMIDI(CHN)&&!ISAWE(CHN))) + loaded = seq_set_patch(CHN, NOTE + 128); + if (VEL && usevol[CHN]) + VEL = usevol[CHN]; + if (loaded != -1) + seq_start_note(use_dev, CHN, NOTE, VEL); + break; + case MIDI_NOTEOFF: + seq_stop_note(use_dev, CHN, NOTE, VEL); + break; + case MIDI_CTL_CHANGE: + seq_control(use_dev, CHN, NOTE, VEL); + break; + case MIDI_CHN_PRESSURE: + seq_chn_pressure(use_dev, CHN, NOTE); + break; + case MIDI_PITCH_BEND: + seq_bender(use_dev, CHN, NOTE, VEL); + break; + case MIDI_PGM_CHANGE: + if (ISMIDI(CHN) || ISAWE(CHN) || !ISPERC(CHN)) + NOTE = seq_set_patch(CHN, NOTE); + break; + case MIDI_SYSTEM_PREFIX: + if (length > 1) + load_sysex(length, data, CMD); + break; + default: + break; + } + } + /* this last little part queues up the next event time */ + seq[track].index += length; + if (seq[track].index >= seq[track].length) + seq[track].ticks = ~0; /* mark track complete */ + else + seq[track].ticks += rvl(&seq[track]); + } + SEQ_DUMPBUF(); + ImPlaying = 0; + return 1; +} +#endif /* linux || FreeBSD */ diff --git a/native_midi_gpl/playmidi.h b/native_midi_gpl/playmidi.h new file mode 100644 index 00000000..6f219412 --- /dev/null +++ b/native_midi_gpl/playmidi.h @@ -0,0 +1,107 @@ +#define RELEASE "Playmidi 2.4" +/************************************************************************ + playmidi.h -- defines and structures for use by playmidi package + + 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 */ + + +/* Default mask for percussion instruments. Channels 16 and 10 = 0x8200 */ +#define PERCUSSION 0x0200 +/* change the following if you have lots of synth devices */ +#define MAX_CARDS 5 +/* the following definition is set by Configure */ +#define FM_DEFAULT_MODE 0 +/* the following definition is set by Configure */ +#define PATCH_PATH1 "/dos/ultrasnd/midi" +/* the following definition is set by Configure */ +#define PATCH_PATH2 "/usr/local/lib/Plib" +/* change this if you notice performance problems, 128 bytes by default */ +#define SEQUENCERBLOCKSIZE 128 +/* change this if you have really outrageous midi files > 128 tracks */ +/* 128 tracks is approximately a 4K structure */ +#define MAXTRKS 128 +/* where to find fm patch libraries */ +#define SEQUENCER_DEV "/dev/sequencer" +#define O3MELODIC "/etc/std.o3" +#define O3DRUMS "/etc/drums.o3" +#define SBMELODIC "/etc/std.sb" +#define SBDRUMS "/etc/drums.sb" +#define ISPERC(x) (perc & (1 << x)) +#define ISGUS(x) (play_gus & (1 << x)) +#define ISFM(x) (play_fm & (1 << x)) +#define ISMIDI(x) (play_ext & (1 << x)) +#define ISAWE(x) (play_awe & (1 << x)) +#define ISPLAYING(x) (chanmask & (1 << x)) +#define NO_EXIT 100 + +#include +#include +#include +#include +#include +#ifdef linux +#include +#else +#include +#endif + +struct chanstate { + int program; + int bender; + int oldbend; /* used for graphics */ + int bender_range; + int oldrange; /* used for graphics */ + int controller[255]; + int pressure; +}; + +struct voicestate { + int note; + int channel; + int timestamp; + int dead; +}; +/* Non-standard MIDI file formats */ +#define RIFF 0x52494646 +#define CTMF 0x43544d46 +/* Standard MIDI file format definitions */ +#define MThd 0x4d546864 +#define MTrk 0x4d54726b +#define meta_event 0xff +#define sequence_number 0x00 +#define text_event 0x01 +#define copyright_notice 0x02 +#define sequence_name 0x03 +#define instrument_name 0x04 +#define lyric 0x05 +#define marker 0x06 +#define cue_point 0x07 +#define channel_prefix 0x20 +#define end_of_track 0x2f +#define set_tempo 0x51 +#define smpte_offset 0x54 +#define time_signature 0x58 +#define key_signature 0x59 +#define sequencer_specific 0x74 + +struct miditrack { + unsigned char *data; /* data of midi track */ + unsigned long int length; /* length of track data */ + unsigned long int index; /* current byte in track */ + unsigned long int ticks; /* current midi tick count */ + unsigned char running_st; /* running status byte */ +}; + diff --git a/native_midi_gpl/readmidi.c b/native_midi_gpl/readmidi.c new file mode 100644 index 00000000..5505e4d5 --- /dev/null +++ b/native_midi_gpl/readmidi.c @@ -0,0 +1,188 @@ +/************************************************************************ + readmidi.c -- last change: 1 Jan 96 + + Creates a linked list of each chunk in a midi file. + ENTIRE MIDI FILE IS RETAINED IN MEMORY so that no additional malloc + calls need be made to store the data of the events in the midi file. + + Copyright (C) 1995-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 "playmidi.h" +#include + +int format, ntrks, division; +unsigned char *midifilebuf; + +/* the following few lines are needed for dealing with CMF files */ +int reloadfm = 0; +extern void loadfm(); +extern int seqfd, sb_dev, wantopl3, play_fm, fmloaded[256]; +SEQ_USE_EXTBUF(); + +extern struct miditrack seq[MAXTRKS]; +extern unsigned long int default_tempo; + +unsigned short Read16() +{ + register unsigned short x; + + x = (*(midifilebuf) << 8) | midifilebuf[1]; + midifilebuf += 2; + return x; +} + +unsigned long Read32() +{ + register unsigned long x; + + x = (*(midifilebuf) << 24) | (midifilebuf[1] << 16) | + (midifilebuf[2] << 8) | midifilebuf[3]; + midifilebuf += 4; + return x; +} + +int readmidi(filebuf, filelength) +unsigned char *filebuf; +off_t filelength; +{ + unsigned long int i = 0, track, tracklen; + + midifilebuf = filebuf; + /* allow user to specify header number in from large archive */ +#if 0 + while (i != find_header && midifilebuf < (filebuf + filelength - 32)) { + if (strncmp(midifilebuf, "MThd", 4) == 0) { + i++; + midifilebuf += 4; + } else + midifilebuf++; + } + if (i != find_header) { /* specified header was not found */ + midifilebuf = filebuf; + return find_header = 0; + } +#endif + if (midifilebuf != filebuf) + midifilebuf -= 4; + i = Read32(); + if (i == RIFF) { + midifilebuf += 16; + i = Read32(); + } + if (i == MThd) { + tracklen = Read32(); + format = Read16(); + ntrks = Read16(); + division = Read16(); + } else if (i == CTMF) { + /* load a creative labs CMF file, with instruments for fm */ + tracklen = midifilebuf[4] | (midifilebuf[5] << 8); + format = 0; + ntrks = 1; + division = midifilebuf[6] | (midifilebuf[7] << 8); + default_tempo = 1000000 * division / + (midifilebuf[8] | (midifilebuf[9] << 8)); + seq[0].data = filebuf + tracklen; + seq[0].length = filelength - tracklen; + i = (unsigned long int) (*(short *) &midifilebuf[2]) - 4; + /* if fm playback is enabled, load all fm patches from file */ + if (play_fm) { + struct sbi_instrument instr; + int j, k; + reloadfm = midifilebuf[32]; /* number of custom patches */ + instr.device = sb_dev; + for (j = 0; j < 32; j++) + instr.operators[j] = 0x3f; + instr.key = FM_PATCH; + for (j = 0; j < reloadfm && j < 255; j++) { + instr.channel = j; + fmloaded[j] = instr.key; + for (k = 0; k < 16; k++) + instr.operators[k] = midifilebuf[i + (16 * j) + k]; + SEQ_WRPATCH(&instr, sizeof(instr)); + } + } + return ntrks; + } else { + int found = 0; + while (!found && midifilebuf < (filebuf + filelength - 8)) + if (strncmp(midifilebuf, "MThd", 4) == 0) + found++; + else + midifilebuf++; + if (found) { + midifilebuf += 4; + tracklen = Read32(); + format = Read16(); + ntrks = Read16(); + division = Read16(); + } else { +#ifndef DISABLE_RAW_MIDI_FILES + /* this allows playing ANY file, so watch out */ + midifilebuf -= 4; + format = 0; /* assume it's .mus file ? */ + ntrks = 1; + division = 40; +#else + return -1; +#endif + } + } + if (ntrks > MAXTRKS) { + fprintf(stderr, "\nWARNING: %d TRACKS IGNORED!\n", ntrks - MAXTRKS); + ntrks = MAXTRKS; + } + if (play_fm && reloadfm) { + loadfm(); /* if custom CMF patches loaded, replace */ + reloadfm = 0; + } + for (track = 0; track < ntrks; track++) { + if (Read32() != MTrk) { + /* MTrk isn't where it's supposed to be, search rest of file */ + int fuzz, found = 0; + midifilebuf -= 4; + if (strncmp(midifilebuf, "MThd", 4) == 0) + continue; + else { + if (!track) { + seq[0].length = filebuf + filelength - midifilebuf; + seq[0].data = midifilebuf; + continue; /* assume raw midi data file */ + } + midifilebuf -= seq[track - 1].length; + for (fuzz = 0; (fuzz + midifilebuf) < + (filebuf + filelength - 8) && !found; fuzz++) + if (strncmp(&midifilebuf[fuzz], "MTrk", 4) == 0) + found++; + seq[track - 1].length = fuzz; + midifilebuf += fuzz; + if (!found) + continue; + } + } + tracklen = Read32(); + if (midifilebuf + tracklen > filebuf + filelength) + tracklen = filebuf + filelength - midifilebuf; + seq[track].length = tracklen; + seq[track].data = midifilebuf; + midifilebuf += tracklen; + } + ntrks = track; + return ntrks; +} +#endif /* linux || FreeBSD */