[AD] Non-FM OSS MIDI driver

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Here are some patches modifying Peter's FM-only OSS MIDI driver
to also support wavetable synths.  It could support generic
external MIDI devices too (I think the same code I used should
work for them) but, as stated in a comment in the code, I think
that should probably be not autodetected, only enabled according
to config settings, since everyone has a MIDI port but almost
no one has anything plugged into it.

I've only tested it with my AWE32.  If you like it, providing
the FM synth still works (I don't have the kernel modules for
it) and other wavetables work too, I think it could be merged
into the 4.0 branch, since it is merely a driver upgrade.

However I'm not totally happy with the way it's implemented, it
feels very messy.  I've always disliked Allegro's treatment of
MIDI files, it seems to do far too much fiddling; this driver
might work better (at least for wavetable and external MIDI, I
don't know about FM) as a raw MIDI device, since that's the way
OSS seems to work itself.  As far as wavetable and external MIDI
are concerned, Allegro's voice allocation really gets in the
way.

George

Index: aclocal.m4
===================================================================
RCS file: /cvsroot/alleg/allegro/aclocal.m4,v
retrieving revision 1.29
diff -u -u -r1.29 aclocal.m4
--- aclocal.m4	18 May 2002 02:44:03 -0000	1.29
+++ aclocal.m4	19 May 2002 22:25:08 -0000
@@ -308,6 +308,7 @@
   AC_CHECK_HEADERS(sys/soundcard.h, allegro_support_ossmidi=yes)
   AC_CHECK_HEADERS(machine/soundcard.h, allegro_support_ossmidi=yes)
   AC_CHECK_HEADERS(linux/soundcard.h, allegro_support_ossmidi=yes)
+  AC_CHECK_HEADERS(linux/awe_voice.h)
 fi
 ])
 
Index: uossmidi.c
===================================================================
RCS file: /cvsroot/alleg/allegro/src/unix/uossmidi.c,v
retrieving revision 1.7
diff -u -u -r1.7 uossmidi.c
--- uossmidi.c	9 Nov 2001 10:09:45 -0000	1.7
+++ uossmidi.c	19 May 2002 22:13:37 -0000
@@ -15,7 +15,6 @@
  *      See readme.txt for copyright information.
  */
 
-
 #include "allegro.h"
 
 #ifdef MIDI_OSS
@@ -38,6 +37,10 @@
 #endif
 #include <sys/ioctl.h>
 
+#if defined(HAVE_LINUX_AWE_VOICE_H)
+#include <linux/awe_voice.h>
+#define HAVE_AWE32
+#endif
 
 
 /* our patch data */
@@ -57,10 +60,13 @@
 
 
 
+#define MAX_VOICES 256
+
 static int seq_fd = -1;
 static int seq_device;
-static int seq_synth_type; 
-static int seq_patch[18];
+static int seq_synth_type, seq_synth_subtype; 
+static int seq_patch[MAX_VOICES];
+static int seq_note[MAX_VOICES];
 static int seq_drum_start;
 static char seq_desc[256] = EMPTY_STRING;
 
@@ -129,44 +135,112 @@
 static int seq_find_synth(int fd)
 {
    struct synth_info info;
-   int num_synths, i, ret = 0;
+   int num_synths, i;
    char *s;
    char tmp1[64], tmp2[256];
+   int score = 0, best_score, best_device;
 
    if (ioctl(fd, SNDCTL_SEQ_NRSYNTHS, &num_synths) == -1)
       return 0;
 
+   best_device = -1;
+   best_score = 0;
+   
+   /* Detect the best device */
    for (i = 0; i < num_synths; i++) {
       info.device = i;
       if (ioctl(fd, SNDCTL_SYNTH_INFO, &info) == -1)
 	 return 0;
 
-      /* only FM synthesis supported, for now */
-      if (info.synth_type == SYNTH_TYPE_FM) {
-	 seq_device = i;
-	 seq_synth_type = SYNTH_TYPE_FM;
-	 midi_oss.voices = info.nr_voices;
-	 ret = 1;
-	 break;
+      switch (info.synth_type) {
+
+	 case SYNTH_TYPE_FM:
+	    /* FM synthesis is kind of ok */
+	    score = 2;
+	    break;
+
+	 case SYNTH_TYPE_SAMPLE:
+	    /* Wavetable MIDI is cool! */
+	    score = 3;
+	    break;
+
+	 case SYNTH_TYPE_MIDI:
+	    /* Only weird people want to use the MIDI out port... */
+	    /* ... so we don't accept it yet, this can be fixed when
+	     * we've got a config file option to select the MIDI device
+	     * so then we'll only select this if people specifically
+	     * ask for it */
+	    score = 0;
+	    break;
+      }
+
+      if (score > best_score) {
+	 best_score = score;
+	 best_device = i;
       }
    }
 
-   switch (info.synth_subtype) {
-      case FM_TYPE_ADLIB:
-	 s = uconvert_ascii("Adlib", tmp1);
+   if (best_score == 0) {
+      return 0;
+   }
+
+   /* Cool, we got a decent synth type */
+   seq_device = best_device;
+   
+   /* Now get more information */
+   info.device = seq_device;
+   if (ioctl(fd, SNDCTL_SYNTH_INFO, &info) == -1)
+      return 0;
+
+   seq_synth_type = info.synth_type;
+   seq_synth_subtype = info.synth_subtype;
+
+   midi_oss.voices = info.nr_voices;
+   if (midi_oss.voices > MAX_VOICES) midi_oss.voices = MAX_VOICES;
+
+   
+   switch (seq_synth_type) {
+   
+      case SYNTH_TYPE_FM:
+	 switch (seq_synth_subtype) {
+	    case FM_TYPE_ADLIB:
+	       s = uconvert_ascii("Adlib", tmp1);
+	       break;
+	    case FM_TYPE_OPL3:
+	       s = uconvert_ascii("OPL3", tmp1);
+	       break;
+	    default:
+	       s = uconvert_ascii("FM (unknown)", tmp1);
+	       break;
+	 }
 	 break;
-      case FM_TYPE_OPL3:
-	 s = uconvert_ascii("OPL3", tmp1);
+
+      case SYNTH_TYPE_SAMPLE:
+	 switch (seq_synth_subtype) {
+#ifdef HAVE_AWE32
+	    case SAMPLE_TYPE_AWE32:
+	       s = uconvert_ascii("AWE32", tmp1);
+	       break;
+#endif
+	    default:
+	       s = uconvert_ascii("sample (unknown)", tmp1);
+	       break;
+	 }
 	 break;
+
+      case SYNTH_TYPE_MIDI:
+	 s = uconvert_ascii("MIDI out", tmp1);
+	 break;
+
       default:
-	 s = uconvert_ascii("Error!", tmp1);
+	 s = uconvert_ascii("Unknown synth", tmp1);
 	 break;
    }
 
    uszprintf(seq_desc, sizeof(seq_desc), uconvert_ascii("Open Sound System (%s)", tmp2), s);
    midi_driver->desc = seq_desc;
 
-   return ret;
+   return 1;
 }
 
 
@@ -199,6 +273,57 @@
 
 
 
+/* FM synth setup */
+static void seq_setup_fm (void)
+{
+   seq_set_fm_patches(seq_fd);
+   seq_drum_start = midi_oss.voices - 5;
+}
+
+
+
+#ifdef HAVE_AWE32
+/* AWE32 synth setup */
+static void seq_setup_awe32 (void)
+{
+   int bits = 0, drums;
+
+   seq_drum_start = midi_oss.voices;
+   if (seq_drum_start > 32) seq_drum_start = 32;
+   
+   /* These non-32 cases probably never happen, since the AWE32 has 
+    * 32 voices and anything higher needs a new interface... */
+   if (midi_oss.voices <= 1) {
+      drums = 0;
+   } else if (midi_oss.voices <= 4) {
+      drums = 1;
+   } else if (midi_oss.voices <= 32) {
+      drums = midi_oss.voices / 8;
+   } else {
+      drums = 4;
+   }
+
+#if 0 // I think I got this wrong
+   /* Set the top 'drums' bits of bitfield and decrement seq_drum_start */
+   while (drums--) bits |= (1 << --seq_drum_start);
+#else
+   /* The AWE driver seems to like receiving all percussion on MIDI channel
+    * 10 (OSS channel 9) */
+   bits = (1<<9);
+   seq_drum_start -= drums;
+#endif
+   
+   /* Tell the AWE which channels are drum channels.  No, I don't know 
+    * what 'multi' mode is or how to play drums in the other modes.  
+    * This is just what playmidi does (except I'm using AWE_PLAY_MULTI 
+    * instead of its value, 1). */
+   AWE_SET_CHANNEL_MODE(seq_device, AWE_PLAY_MULTI);
+   AWE_DRUM_CHANNELS(seq_device, bits);
+}
+#endif
+
+
+
 /* oss_midi_detect:
  *  Sequencer detection routine.
  */
@@ -238,19 +363,34 @@
 
    if (!seq_find_synth(seq_fd)) {
       close(seq_fd);
-      ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("No support synth type found"));
+      ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("No supported synth type found"));
       return -1;
    }
 
    ioctl(seq_fd, SNDCTL_SEQ_RESET);
 
+   /* Driver-specific setup */
    if (seq_synth_type == SYNTH_TYPE_FM) {
-      seq_set_fm_patches(seq_fd);
-      seq_drum_start = midi_oss.voices - 5;
+   
+      seq_setup_fm();
+      
+   } else if (seq_synth_type == SYNTH_TYPE_SAMPLE) {
+
+#ifdef HAVE_AWE32
+      if (seq_synth_subtype == SAMPLE_TYPE_AWE32) {
+
+	 seq_setup_awe32();
+	 
+      }
+#endif
+      
    }
 
-   for (i = 0; i < (sizeof(seq_patch) / sizeof(int)); i++) 
+	       
+   for (i = 0; i < (sizeof(seq_patch) / sizeof(int)); i++) {
       seq_patch[i] = -1;
+      seq_note[i] = -1;
+   }
 
    /* for the mixer routine */
    ustrzcpy(mixer_driver, sizeof(mixer_driver), get_config_string(uconvert_ascii("sound", tmp1),
@@ -284,17 +424,62 @@
 
 
 
+/* get_hardware_voice:
+ *  Get the hardware voice corresponding to this virtual voice.  The 
+ *  hardware may support more than one note per voice, in which case
+ *  you don't want to terminate old notes on reusing a voice, but when
+ *  Allegro reuses a voice it forgets the old note and won't ever send
+ *  a keyoff.  So we should use more virtual (Allegro) voices than the
+ *  channels the hardware provides, and map them down to actual 
+ *  hardware channels here.
+ *
+ *  We also swap voices 9 and 15 (MIDI 10 and 16).  This is necessary 
+ *  because _midi_allocate_voice can only allocate in continuous ranges 
+ *  -- so we use voices [0,seq_drum_start) for melody and for the
+ *  percussion [seq_drum_start,max_voices), as far as Allegro is 
+ *  concerned.  But hardware likes percussion on MIDI 10, so we need to
+ *  remap it.
+ */
+static int get_hardware_voice (int voice)
+{
+   int hwvoice = voice;
+   
+   /* FIXME: is this OK/useful for other things than AWE32? */
+   if (seq_synth_type != SYNTH_TYPE_FM) {
+
+      /* map drums >= 15, everything else < 15 */
+      hwvoice = hwvoice * 15 / seq_drum_start;
+
+      /* fix up so drums are on MIDI channel 10 */
+      if (hwvoice >= 15)
+         hwvoice = 9;
+      else if (hwvoice == 9)
+         hwvoice = 15;
+
+   }
+
+   return hwvoice;
+}
+
+
+
 /* oss_midi_key_on:
  *  Triggers the specified voice. 
  */
 static void oss_midi_key_on(int inst, int note, int bend, int vol, int pan)
 {
-   int voice;
+   int voice, hwvoice;
+   int is_percussion = 0;
 
    /* percussion? */
    if (inst > 127) {
       voice = _midi_allocate_voice(seq_drum_start, midi_driver->voices-1);
+      /* TODO: Peter's code decrements inst by 35; but the AWE driver 
+       * ignores inst completely, using note instead, as God (well, GM) 
+       * intended.  Does the FM driver ignore note?  If so then we're OK. */
+      note = inst-128;
       inst -= 35;
+      is_percussion = 1;
    }
    else
       voice = _midi_allocate_voice(0, seq_drum_start-1);
@@ -302,15 +487,27 @@
    if (voice < 0)
       return;
 
-   /* make sure the voice is set up with the right sound */
-   if (inst != seq_patch[voice]) {
-      SEQ_SET_PATCH(seq_device, voice, inst);
-      seq_patch[voice] = inst;
+   /* Get the hardware voice corresponding to this virtual voice. */
+   hwvoice = get_hardware_voice (voice);
+      
+   /* FIXME: should we do this or not for FM? */
+   if (seq_synth_type != SYNTH_TYPE_FM) {
+      /* Stop any previous note on this voice -- but not if it's percussion */
+      if (!is_percussion && seq_note[voice] != -1) {
+         SEQ_STOP_NOTE(seq_device, hwvoice, seq_note[voice], 64);
+      }
+   }
+   seq_note[voice] = note;
+
+   /* make sure the (hardware) voice is set up with the right sound */
+   if (inst != seq_patch[hwvoice]) {
+      SEQ_SET_PATCH(seq_device, hwvoice, inst);
+      seq_patch[hwvoice] = inst;
    }
 
-   SEQ_CONTROL(seq_device, voice, CTL_PAN, pan);
-   SEQ_BENDER(seq_device, voice, 8192 + bend);
-   SEQ_START_NOTE(seq_device, voice, note, vol);
+   SEQ_CONTROL(seq_device, hwvoice, CTL_PAN, pan);
+   SEQ_BENDER(seq_device, hwvoice, 8192 + bend);
+   SEQ_START_NOTE(seq_device, hwvoice, note, vol);
    SEQ_DUMPBUF();
 }
 
@@ -321,8 +518,13 @@
  */
 static void oss_midi_key_off(int voice)
 {
-   SEQ_STOP_NOTE(seq_device, voice, 0, 64);
+   /* Get the hardware voice corresponding to this virtual voice. */
+   int hwvoice = get_hardware_voice (voice);
+      
+   SEQ_STOP_NOTE(seq_device, hwvoice, seq_note[voice], 64);
    SEQ_DUMPBUF();
+
+   seq_note[voice] = -1;
 }
 
 


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/