diff -upNr tintin++/README.msp tintin++-msp/README.msp
--- tintin++/README.msp	Thu Jan  1 01:00:00 1970
+++ tintin++-msp/README.msp	Fri Apr 10 18:02:50 1998
@@ -0,0 +1,115 @@
+Release Notes  (April 10th, 1998)
+=-=-=-=-=-=-=
+
+***                                                                       ***
+* NOTE! This software is provided with ABSOLUTELY NO warranty. If something *
+* breaks, you keep both parts. Usage implies acceptance of these terms.     *
+***                                                                       ***
+
+  -- Read this, please.
+
+
+NEW COMMANDS:
+
+#msp
+This command toggles on and off msp support. Enabled by default, but you
+can add it to your startup file if you want to disable it.
+
+
+RELEASE NOTES:
+
+Sounds are expected to be found at /usr/local/share/sounds/ , and inside
+that directory, two more called samples (where the .wav files go) and
+modules (where the .uni files go). To successfully compile this patch,
+you need the MikMod 3.0.3 library available at sunsite.unc.edu (but you
+have to rebuild it, the normal build wont do). Or if you dont want to
+compile, why dont you just download the binary version? Midi files are
+NOT supported and prolly will never be: who would want to hear those ugly
+and cpu expensive files when they can have nice multichannel modules?
+This means the default extension for music files isnt .mid but .uni . As
+well, the maximum filename length is 32 bytes. Please put null.uni in its
+right directory when installing and dont delete it.. optionally, you
+might rename some other module as null.uni, and it will be played as
+background when no other music has been selected.
+
+.uni is the native module format for the mikmod library functions. I
+suggest you to use mikcvt to convert your old modules to .uni modules.
+This way, mikmod wont have to convert the modules over and over to its
+internal format when it loads a new one. mikcvt 3.0 is supplied with this
+patch, since for some weird reason it was left out of the 3.0.x UNIX / Linux
+libraries.
+
+!!SOUND Formats supported: wav
+!!MUSIC Formats supported: it xm s3m mod mtm stm dsm med far ult 669 uni
+
+The patch leaves lots of room for improvement.. it just works, doesn't
+aspire for efficience or elegance. I would had liked to add mixing of
+multiple sound effects, but msp only supports one at a time *mutter*
+If you have any patches for the patch or problems running with it, let me
+know.. I don't make promises, but I'll try to give you a hand.
+
+
+COMPILING:
+
+To compile, apply the patch and compile tintin as normal, and then
+
+gcc -O2 -s -DMSP_FINAL sndserver.c -o sndserver -Lmikmod/lib -lmikmod -lmmio
+
+Copy this file to the same directory where the new tintin is, and
+remember to copy null.uni to /usr/local/share/sounds/modules/
+
+You can compile mikcvt too:
+gcc -O2 -s mikcvt.c -o mikcvf -Lmikmod/lib -lmikmod -lmmio
+
+
+KNOWN PROBLEMS:
+
+Note that in some development kernels sound support is broken (2.1.53 to
+2.1.94, current last one), and for others you must manually insert the
+sound module (as in 2.1.43) before starting tintin. If tintin freezes
+just when it starts up at the point that you cant see what you are
+typing, that means it's still waiting for sndserver.. make sure its in
+the correct place.  Btw this isn't tested: it compiles, it runs, you can
+turn it off in case of problems.. what else do you want for what you are
+paying? :)
+
+
+OTHER CHANGES:
+
+The other of the changes that come with this patch is that color is
+ignored when matchiing text and triggers. That is,
+
+Isildur's backstab ANNIHILATES you!
+                   ^^^^^^^^^^^
+
+If the underlined word was, say, in red, an action trigger with the text
+{devastates you} wouldnt work before. Not too good :)
+
+Thats all, beta tester ;)
+
+
+PLUG:
+
+Btw, if you'd like to try a very unique MUD with non stock high quality
+areas, with all the good features and none of the silly ones, and a good
+role playing atmosphere with non abusing hard working underpaid imms
+and nice players, try: 
+
+   ARCADIA 2 (Beta)
+   telnet://arcadia.net:6000
+
+   http://www.arcadia.net
+
+
+CONTACT INFO:
+
+Ragnar Hojland Espinosa        |  Since it's untested, I'd appreciate some
+                               |  feedback: Does it work for you? Or maybe
+http://www.lightside.ddns.org  |  It doesn't? Why not? How did you fix it?
+ragnar@lightside.ddns.org      |  .. Don't be like Nezmor, write something
+tech.support@redestb.es        |  that does make sense ;)
+
+____/|  Ragnar Hojland                                  Fingerprint  94C4B
+\ o.O|                                                  2F0D27DE025BE2302C
+ =(_)=  "Thou shalt not follow the NULL pointer for     104B78C56 B72F0822
+   U     chaos and madness await thee at its end."      hkp://keys.pgp.com
diff -upNr tintin++/src/Makefile tintin++-msp/src/Makefile
--- tintin++/src/Makefile	Mon Apr 25 13:32:04 1994
+++ tintin++-msp/src/Makefile	Fri Apr 10 17:54:48 1998
@@ -5,8 +5,8 @@
 # I'm using GNU's gcc compiler. The most important is that the 
 # compiler is ANSI-compatible. If gcc isn't installed on your
 # system, then try change the 'gcc' below to 'cc' If 
-#CC = cc -O  
-CC = gcc -O
+#CC = cc -O
+CC = gcc 
 
 # Some people's ANSI-compiler somehow don't have the full
 # ANSI-defined standard-library. Uncomment the lines below
@@ -23,7 +23,7 @@ CC = gcc -O
 # If you are using a SYS V varient unix (IRIX <sgi>, HP/UX <hp>, Linux, etc.)
 # define F3 so that term echoing will function properly.  If you are using a
 # BSD varient (SunOS, 386BSD, BSD/I, FreeBSD, etc..) leave F3 commented out.
-#F3 = -DSYSV
+F3 = -DSYSV
 
 # If you plan on doing debugging (with gdb), it is very helpful to turn all
 # the alarms off so that you can step through the code without having to 
@@ -35,8 +35,12 @@ CC = gcc -O
 
 #F5 = -DSOCK_FIX
 
-CFLAGS= $(F1) $(F2) $(F3) $(F4) $(F5)
-LFLAGS= -s
+FMSP = -DDEFAULT_SOUND_PATH=\"/usr/local/share/sounds/\" \
+       -Imikmod/include -DMSP_FINAL -O2 -g0
+
+CFLAGS= $(F1) $(F2) $(F3) $(F4) $(F5) $(FMSP)
+LFLAGS=  -Lmikmod/lib -g0 -s
+MSPLIB=-lmikmod -lmmio
 
 # BINDIR is the directory you wish tt++ to be placed if you wish to use
 # make install.  
@@ -57,8 +61,8 @@ OFILES = main.o parse.o action.o alias.o
 files.o history.o ticks.o misc.o path.o net.o llist.o utils.o echo.o \
 variables.o highlight.o antisub.o ivars.o help.o text.o glob.o
 
-tintin++: $(OFILES) 
-	$(CC) $(CFLAGS) $(LFLAGS)  -o ../tt++ $(OFILES) $(LIB)
+tintin++: $(OFILES)
+	$(CC) $(CFLAGS) $(LFLAGS)  -o tt++ $(OFILES) $(LIB)
 
 main.o: tintin.h
 parse.o: tintin.h
diff -upNr tintin++/src/action.c tintin++-msp/src/action.c
--- tintin++/src/action.c	Sun Apr 24 21:00:37 1994
+++ tintin++-msp/src/action.c	Fri Apr 10 17:54:48 1998
@@ -270,6 +270,36 @@ int check_a_action(line, action, ses)
   char result[BUFFER_SIZE];
   char *temp2, *tptr, *lptr, *lptr2;
   int  i,flag_anchor, count, len, flag;
+  
+#if !defined NO_COLORSTRIP
+        char color_stripped[BUFFER_SIZE];
+        if (1)
+          {  
+	    char *cs = &color_stripped[0], *cslp = line;
+	    
+	    while (*cslp)
+	      {
+		if (*cslp == 27)
+		  {
+		    while (*(++cslp) && *cslp != 'm' && *cslp != ' ') 
+		      {
+			/* empty body */
+		      }
+		  }
+		else
+		  {
+		    *(cs++) = *(cslp);
+		  }
+		
+		cslp++;
+	      }
+
+	    *cs = '\0';
+	    line = &color_stripped[0];
+	  }
+#endif
+
+  
   for (i=0; i<10; i++) var_len[i]=-1;
   flag_anchor=FALSE;
   lptr=line;
diff -upNr tintin++/src/b.c tintin++-msp/src/b.c
--- tintin++/src/b.c	Thu Jan  1 01:00:00 1970
+++ tintin++-msp/src/b.c	Fri Apr 10 17:54:48 1998
@@ -0,0 +1,32 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "a.h"
+
+// server
+int main()
+{
+  char buf[255];
+  int fd, fd2;
+  
+  mkfifo (MSP_PIPE_1, S_IRUSR|S_IWUSR);
+  mkfifo (MSP_PIPE_2, S_IRUSR|S_IWUSR);  
+  
+  fd = open (MSP_PIPE_1, O_RDONLY);
+  
+  while (read (fd, buf, 1) < 1) ;
+  puts ("there you are!");
+
+  fd2 = open (MSP_PIPE_2, O_WRONLY);
+  
+//  sprintf (buf, "1\na.mod\n2\n1\n3\n1\nnot.wav\n2\n0\n3\n");
+//  sprintf (buf, "1\n/tmp/ENIGMA.uni\n2\n1\n3\n");
+  sprintf(buf, "1\n/usr/local/share/sounds/samples/not.wav\n2\n0\n5\n-1\n3\n");
+  write (fd2, buf, strlen(buf)+1);  
+  sleep (10000);
+  
+  return 0;
+}
diff -upNr tintin++/src/main.c tintin++-msp/src/main.c
--- tintin++/src/main.c	Mon Apr 25 13:34:15 1994
+++ tintin++-msp/src/main.c	Fri Apr 10 18:09:48 1998
@@ -9,16 +9,21 @@
 #include <signal.h>
 #include "tintin.h"
 #include <stdlib.h>
-#if IRIX
-#include <time.h>  
+#include <sys/time.h>
 #include <unistd.h>
 #include <sys/types.h>
-#include <sys/time.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#if IRIX
+#include <time.h>  
 #endif
-
+#include <ctype.h>
 #ifndef BADSIG
   #define BADSIG (void (*)())-1
 #endif
+#include <fcntl.h>
+#include "msp.h"
+
 /*************** globals ******************/
 int term_echoing=TRUE;
 int echo=DEFAULT_ECHO;
@@ -39,7 +44,8 @@ int antisubnum=0;
 int verbatim=0;
 char homepath[1025];
 char E=27;
-
+char soundpath[1025];
+int msp=TRUE;
 struct session *sessionlist, *activesession;
 struct listnode *common_aliases, *common_actions, *common_subs, *common_myvars;
 struct listnode *common_highs, *common_antisubs, *common_pathdirs;
@@ -62,6 +68,9 @@ void snoop();
 void tintin_puts2();
 int interpret_buffer();
 
+int msp_fdi;
+int msp_fdo;
+
 /************ externs *************/
 extern int ticker_interrupted, time0;
 extern int tick_size, sec_to_tick;
@@ -77,8 +86,8 @@ extern struct listnode *init_list();
 extern void term_noecho();
 extern void read_complete();
 extern void syserr();
-extern int time();
-extern void alarm();
+extern int mytime();
+//extern void alarm();
 extern int do_one_antisub();
 extern void do_one_sub();
 extern void do_one_high();
@@ -123,6 +132,341 @@ void tstphandler(sig, code, scp, addr)
 /* main() - show title - setup signals - init lists - readcoms - tintin() */
 /**************************************************************************/
 
+typedef struct msp_song   msp_song;
+struct msp_song
+{
+  char file[0xff-12];
+  int vol;
+  int repeats;
+  int cont;  
+};
+
+typedef struct msp_sample msp_sample;
+struct msp_sample
+{
+  msp_sample *next;
+  char file[0xff-24];
+  int vol;
+  int repeats;
+  int priority;
+  int voice;
+  int cont;
+};
+
+msp_sample *msp_queue;
+msp_song   actual_song;
+msp_sample actual_sample;
+
+void msp_fork()
+{
+  tintin_puts2 ("Waiting for soundserver.. ",NULL);
+#ifdef MSP_FINAL
+  if (fork() == 0) {
+    if (execlp ("./sndserver", "./sndserver", 0) < 0) {
+      tintin_puts2 ("Couldn't start sndserver.",NULL);
+    };
+    exit(1);
+  }
+#endif  
+}
+
+// return a number between 0 and n-1 included
+#define RANDOM_RAGNAR(n) ((int) ((float)(n)*rand()/(RAND_MAX+1.0)))
+
+void init_msp()
+{
+  char buf[0xf];
+  msp_queue = 0;  
+  
+  actual_song.file[0] = 0;
+  actual_song.repeats = 0;
+  actual_song.vol = 0;
+  actual_song.cont = 0;
+  
+  actual_sample.file[0] = 0;
+  actual_sample.repeats = 0;
+  actual_sample.vol = 0;
+  actual_sample.cont = 0;
+  actual_sample.priority = -1;
+  actual_sample.voice = -1;
+ 
+  mkfifo (MSP_PIPE_1, S_IRUSR|S_IWUSR);
+  mkfifo (MSP_PIPE_2, S_IRUSR|S_IWUSR);    
+  msp_fdi = open (MSP_PIPE_1, O_RDONLY);  
+  while (read (msp_fdi, buf, 1) < 1) ;
+  msp_fdo = open (MSP_PIPE_2, O_WRONLY);
+
+  srand (time(NULL) ^ getpid());
+}
+
+void end_msp()
+{
+  close (msp_fdo);
+  close (msp_fdi);
+}
+
+msp_sample* new_msp_sample()
+{
+  return (msp_sample*) malloc(sizeof(msp_sample));
+}
+
+void msp_find_file (const char *file, char *dest, int sound)
+{
+  DIR *d;
+  struct dirent *de;
+  char files[256][32];
+  int max = 0;
+  char buf[256];
+  char *p = &buf[0];
+  
+  strcpy (dest, DEFAULT_SOUND_PATH);
+  strcat (dest, sound ? "/samples/" : "/modules/");
+  strcpy (buf, file);
+
+  while (*p && *p != '*') {
+    ++p;
+  }
+
+  if (!p) {
+    p = &buf[0];
+    while (*p && *p != '.') {
+      ++p;
+    }
+    strcat (dest, file);
+    if (!p) {
+      strcat (dest, sound ? ".wav" : ".uni");
+    }
+    return;
+  }
+  
+  *p = 0;  
+  d = opendir (dest);
+  
+  // cheap way.. forget about glob or scandir, mallocs or frees
+  while (1) {
+    de = readdir (d);
+    if (!de || max == 256) {
+      break;
+    }
+    if (strstr (de->d_name, file) == &de->d_name[0]) {
+      strncpy (files[max], de->d_name, 32);
+      files[max++][31] = 0;
+    }
+  }
+  
+  strcat (dest, max ? files[RANDOM_RAGNAR(max)] : file );
+  closedir (d);
+}
+
+void msp_request_sample (char *file, int vol, int repeats, int priority, int cont)
+{
+  char buf[0xff];    
+  
+  if (file[0] && !strcmp (file, "Off") && actual_sample.voice >= 0) {
+    sprintf (buf, "%d\n%d\n%d\n%d\n%d\n", 
+	     MSP_TYPE, MSP_TYPE_SOUND,
+	     MSP_OFF,  actual_sample.voice,
+	     MSP_APPLY);    
+    actual_sample.file[0] = 0;
+    actual_sample.voice = -1;
+  } else {
+
+    sprintf (buf, "%d\n%d\n%d\n%d\n",
+	     MSP_TYPE,    MSP_TYPE_SOUND,
+	     MSP_PLAYING, actual_sample.voice);
+    write (msp_fdo, buf, strlen(buf)+1);
+    read (msp_fdi, buf, 32);
+
+    if (atoi(buf) == FALSE ) { // worked with 1 too
+      // clean sample if sound is over
+      actual_sample.priority = -1;
+      actual_sample.file[0]  = 0;
+      actual_sample.voice    = -1;   
+    }
+    
+    if (actual_sample.voice > -1) {
+      if (priority < actual_sample.priority) {
+	return;
+      }
+    }
+
+    msp_find_file (file, &buf[0], 1);
+    
+    if (!strcmp(buf, actual_sample.file) && actual_sample.cont) {
+      actual_sample.repeats = repeats;
+      actual_sample.vol     = vol;
+      sprintf (buf, "%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n",
+	       MSP_RESET, 
+	       MSP_TYPE,   MSP_TYPE_SOUND,
+	       MSP_VOLUME, 128 * actual_sample.vol / 100,
+	       MSP_COUNT,  actual_sample.repeats,
+	       MSP_APPLY);	       
+    } else {
+      strcpy (actual_sample.file, buf);
+      actual_sample.vol      = vol;
+      actual_sample.repeats  = repeats;
+      actual_sample.cont     = cont;
+      actual_sample.priority = priority;
+      
+      sprintf (buf, "%d\n%s\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n",
+	       MSP_FILE,   actual_sample.file,
+	       MSP_TYPE,   MSP_TYPE_SOUND,
+	       MSP_VOLUME, 128 * actual_sample.vol / 100,
+	       MSP_COUNT,  actual_sample.repeats,
+	       MSP_PLAY);
+      
+      write (msp_fdo, buf, strlen(buf)+1);
+      read (msp_fdi, buf, 32);
+      actual_sample.voice = atoi (buf);
+      return;
+    }
+  }  
+  
+  write (msp_fdo, buf, strlen(buf)+1);
+}
+
+void msp_request_music (char *file, int vol, int repeats, int priority, int cont)
+{
+  char buf[0xff];
+  
+  if (!strcmp (file, "Off")) {
+    sprintf (buf, "%d\n%d\n%d\n%d\n", 
+	     MSP_TYPE, MSP_TYPE_MUSIC,
+	     MSP_OFF,
+	     MSP_APPLY);    
+    actual_song.file[0] = 0;
+  } else {
+
+    msp_find_file (file, &buf[0], 0);
+    
+    if (!strcmp(buf, actual_song.file) && actual_song.cont) {
+      actual_song.repeats = repeats;
+      actual_song.vol     = vol;
+      sprintf (buf, "%d\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n",
+	       MSP_RESET, 
+	       MSP_TYPE,   MSP_TYPE_MUSIC,
+	       MSP_VOLUME, 128 * actual_song.vol  / 100,
+	       MSP_COUNT,  actual_song.repeats,
+	       MSP_APPLY);	       
+    } else {
+      strcpy (actual_song.file, buf);
+      actual_song.vol      = vol;
+      actual_song.repeats  = repeats;
+      actual_song.cont     = cont;
+      
+      sprintf (buf, "%d\n%s\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n",
+	       MSP_FILE,   actual_song.file,
+	       MSP_TYPE,   MSP_TYPE_MUSIC,
+	       MSP_VOLUME, 128 * actual_song.vol / 100,
+	       MSP_COUNT,  actual_song.repeats,
+	       MSP_PLAY);
+    }
+  }
+  
+  write (msp_fdo, buf, strlen(buf)+1);
+}
+
+
+int check_msp (char *arg)
+{
+  char sound;
+  char file[0xff];
+  char buf[0xff];
+  int vol = 100;
+  int repeats = 1;
+  int priority = 50;
+  int cont = 1;
+  int *what;
+  
+  char *h = arg;
+  char *p;
+      
+  if (!msp || arg[0] != '!') {
+    return FALSE;
+  }    
+  
+  if (strstr(arg, "!!SOUND(")) {
+    sound = 1; 
+  } else if (strstr(arg, "!!MUSIC(")) {
+    sound = 0;
+  } else {
+    return FALSE;
+  }
+  
+  while (*h && *h != ')') {
+    ++h;
+  }
+  
+  if (!h) {
+    return FALSE;
+  }    
+    
+  h = arg;
+    
+  while (isspace(*h) || *h != '(') {
+    ++h;
+  }
+  ++h;
+  
+  p = &file[0];
+  while (*h != ')' && !isspace(*h)) {
+    *(p++) = *(h++);
+  }
+  *p = 0;
+
+  while (1) {
+    p = &buf[0];
+    while (*h && (isspace(*h) || *h == '=')) {
+      ++h;
+    }
+    if (!*h || *h == ')') {
+      break;
+    }
+    switch (*h) {
+      case 'V':
+        what = &vol; break;
+      case 'L':
+        what = &repeats; break;
+      case 'P':
+        what = &priority; break;
+      case 'C':
+        what = &cont; break;
+      case 'T':
+      default:        
+//        what = NULL; return FALSE;
+        while (*h && !isspace(*(h++))) ;
+        continue;
+    }
+    
+    //    if (!what) {
+    //      while (!isspace(*h) || *h == '=') {
+    //	++h;
+    //      };
+    //    } else {
+    while (!isdigit(*h) && *h != '-') {
+      ++h;
+    }
+      
+    while (!isspace(*h) && *h != ')') {
+      *(p++) = *(h++);
+    }
+    *p = 0;  
+    *what = atoi (buf);
+    
+  }
+  
+  if (sound) {
+    msp_request_sample (file, vol, repeats, priority, cont);
+  } else {
+    msp_request_music (file, vol, repeats, priority, cont);
+  }
+  
+  // we choose to ignore the rest of the line
+  arg[0] = '.'; arg[1] = 0;
+  return TRUE;
+}
+
+
 void main(argc, argv, environ)
      int argc;
      char **argv;
@@ -131,6 +475,9 @@ void main(argc, argv, environ)
   struct session *ses;
   char *strptr, temp[BUFFER_SIZE];
   int arg_num;
+  
+  msp_fork();
+  
 #if defined(SYSV)
   init_echo();
 #endif
@@ -158,6 +505,8 @@ void main(argc, argv, environ)
     syserr("signal SIGINT");
   if(signal(SIGALRM, tick_func)==BADSIG)
     syserr("signal SIGALRM");
+  if(signal(SIGPIPE, myquitsig)==BADSIG)
+    syserr("signal SIGPIPE");
   /* CHANGED to get rid of double-echoing bug when tintin++ gets suspended */
   if(signal(SIGTSTP, tstphandler)==BADSIG)
     syserr("signal SIGTSTP");
@@ -183,8 +532,9 @@ void main(argc, argv, environ)
   mesvar[5]=DEFAULT_VARIABLE_MESS;
   mesvar[6]=DEFAULT_PATHDIR_MESS;
   *homepath='\0';
+  *soundpath='\0';
   if (!strcmp(DEFAULT_FILE_DIR, "HOME"))
-    if (strptr = getenv("HOME"))
+    if ( (strptr = getenv("HOME")) )
       strcpy(homepath, strptr);
     else *homepath = '\0';
   else strcpy(homepath, DEFAULT_FILE_DIR);
@@ -195,6 +545,7 @@ void main(argc, argv, environ)
       verbose=TRUE;
     }
   }
+  strcpy (soundpath, DEFAULT_SOUND_PATH);
   if(argc > arg_num && argv[arg_num]) {
     activesession=read_command(argv[arg_num], NULL);
   }
@@ -203,6 +554,7 @@ void main(argc, argv, environ)
     strcat(temp,"/.tintinrc");
     activesession=read_command(temp, NULL); 
   }
+  init_msp();
   tintin();
 }
 
@@ -214,16 +566,19 @@ void tintin()
   char buffer[BUFFER_SIZE], strng[80];
   int didget;
   int readfdmask, done;
+  signed int sel;
   struct session *sesptr;
   *k_input='\0';
+  
   while(TRUE) {
 
     readfdmask=1;
     for(sesptr=sessionlist; sesptr; sesptr=sesptr->next)
       readfdmask|=sesptr->socketbit;
     ticker_interrupted=FALSE;
-    if(select(32, &readfdmask,  0, 0, 0)<0 && !ticker_interrupted)
-      syserr("select");
+    sel = select (32, &readfdmask, 0, 0, 0);
+    if(sel<0 && !ticker_interrupted)
+        syserr("select");
     if(ticker_interrupted)
       ticker_interrupted=FALSE;
     else {
@@ -624,6 +979,9 @@ void do_one_line(line, ses)
      char *line;
      struct session *ses;
 {   
+    if (check_msp(line)) {
+      return;
+    }
     if (!presub && !ses->ignore)
       check_all_actions(line,ses);
     if (!togglesubs) 
@@ -785,10 +1143,13 @@ static void myquitsig()
 
   if (is_split)
     write(1, strng, strlen(strng));
+  end_msp();
   printf("\n\rYour fireball hits TINTIN with full force, causing an immediate death.\n\r");
   printf("TINTIN is dead! R.I.P.\n\r");
   printf("Your blood freezes as you hear TINTIN's death cry.\n\r");
   term_echo();
+  unlink (MSP_PIPE_1);
+  unlink (MSP_PIPE_2);  
   exit(0);
 }
 
diff -upNr tintin++/src/mikcvt.c tintin++-msp/src/mikcvt.c
--- tintin++/src/mikcvt.c	Thu Jan  1 01:00:00 1970
+++ tintin++-msp/src/mikcvt.c	Fri Apr 10 18:08:24 1998
@@ -0,0 +1,388 @@
+/*
+
+ --> Mikmod UniFormat Conversion Utility
+  -> Version 3.0 -- Released August 20th, 1997.
+
+FileName: MIKCVT.C
+
+Description:
+Program to convert any supported module into a UniMOD [.UNI] module.
+If compiled and run under DOS/Windows, this MIKCVT.C does not sup-
+port wildcards.  This feature is intentionally left out for now to
+maintain easy portability to the Unix platform.
+
+DOS users can add wildcard support themselves by following the ngetopt
+example in MIKMOD.C.
+
+
+Portability:
+All systems - all compilers
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mikmod/include/mikmod.h"
+
+
+//  Declare external loaders:
+
+
+static FILE   *fpi, *fpo;
+static UBYTE  unitag[]  = "UN06\x1A";
+
+
+/***************************************************************************
+***************************************************************************/
+
+BOOL TrkCmp(UBYTE * t1, UBYTE * t2)
+{
+    UWORD l1, l2;
+
+    if((t1 == NULL) || (t2 == NULL))
+    return 0;
+
+    l1 = TrkLen(t1);
+    l2 = TrkLen(t2);
+
+    if (l1 != l2)
+    return 0;
+
+    return (MyCmp(t1, t2, l1));
+}
+
+
+
+void ReplaceTrack(UNIMOD * mf, int t1, int t2)
+{
+    int t;
+
+    for(t=0; t<mf->numpat*mf->numchn; t++)
+    {   if(mf->patterns[t] == t1)
+             mf->patterns[t] = t2;
+    }
+}
+
+
+
+void OptimizeTracks(UNIMOD * mf)
+
+//  Optimizes the number of tracks in a modfile by removing tracks with
+//  identical contents.
+
+{
+    int   t, u, same, newcnt = 0;
+    UBYTE *ta;
+    UBYTE **newtrk;
+
+    if (!(newtrk = (UBYTE **)malloc(mf->numtrk * sizeof(UBYTE *)))) return;
+
+    for(t=0; t<mf->numtrk; t++)
+    {   // ta is track to examine
+
+        ta = mf->tracks[t];
+
+        // does ta look familiar ?
+
+        for(same = u = 0; u<newcnt; u++)
+        {   if(TrkCmp(ta, newtrk[u]))
+            {   same = 1;
+                break;
+            }
+        }
+
+        if(same)
+        {   ReplaceTrack(mf, t, u);
+        } else
+        {   ReplaceTrack(mf, t, newcnt);
+            newtrk[newcnt++] = ta;
+        }
+
+        printf("\rOptimizing: %d\%", (t * 100L) / mf->numtrk);
+    }
+
+    printf("\rOptimized : %d tracks -> %d tracks\n", mf->numtrk, newcnt);
+
+    free(mf->tracks);
+    mf->tracks = newtrk;
+    mf->numtrk = newcnt;
+}
+
+
+/***************************************************************************
+***************************************************************************/
+
+CHAR *stripname(CHAR *path, CHAR *ext)
+
+// Strips the filename from a path, and replaces or adds
+// a new extension to it.
+
+{
+        CHAR *n, *m;
+        static CHAR newname[256];
+
+        // extract the filename from the path
+
+#ifdef unix
+        n = ((n = strrchr(path, '/')) == NULL) ? path : n + 1;
+#else
+        n = ((n = strrchr(path, '\\')) == NULL) ? path : n + 1;
+        if(m = strrchr(n, ':')) n=m+1;
+#endif
+
+        // copy the filename into 'newname'
+        strncpy(newname,n,255);
+        newname[255] = 0;
+
+        // remove the extension
+        if (n = strrchr(newname, '.'))
+        *n = 0;
+
+        // and tack on the new extension
+        return strcat(newname, ext);
+}
+
+
+/***************************************************************************
+***************************************************************************/
+
+int main(int argc, char *argv[])
+{
+    UNIMOD     *mf;
+    SAMPLE     *s;
+    INSTRUMENT *i;
+    int        t, v, w, strip = 0, numsmp, numins;
+    CHAR       *outname;
+
+    
+
+    puts("\n"
+         "MIKCVT v3.0 - MikMod UniFormat Conversion Utility\n"
+         "=================================================\n");
+         
+
+    // Register the NoSound driver only - the modules will not be played.
+
+    MikMod_RegisterDriver(drv_nos);
+    MikMod_RegisterAllLoaders();
+
+    MD_Init();        // nosound driver HAS to initialize!
+
+
+    if(argc < 2)
+    {   // display a usage message
+
+        puts("Usage: MIKCVT [-strip] <fletch.mod> ...\n\n"
+             "-strip: Leave all text out from the UNI file.\n\n");
+
+        exit(-1);
+    }
+
+    for (t=1; t<argc; t++)
+    {   if((t == 1) && (!strcmp(argv[1], "-strip")))
+        {
+            strip = 1;
+            puts("Stripping all text.\n");
+
+            continue;
+        }
+
+        // Open the first file (source module)
+
+        printf("In file : %s\n", argv[t]);
+        if((fpi = fopen(argv[t], "rb")) == NULL)
+        {   printf("MikCvt Error: Error opening input file\n");
+            break;
+        }
+
+        // Open the desination module
+
+        outname = stripname(argv[t], ".uni");
+        printf("Out file: %s\n", outname);
+        if ((fpo = fopen(outname, "wb")) == NULL)
+        {   printf("MikCvt Error: Error opening output file\n");
+            break;
+        }
+
+        // load the module .. maxchan of 0, because we won't be playing
+        // anything.
+
+        mf = ML_LoadFP(fpi,0);
+
+        // didn't work -> exit with error
+
+        if (mf == NULL)
+        {   printf("MikCvt Error: %s\n", _mm_errmsg[_mm_errno]);
+            fclose(fpi);
+            break;
+        }
+
+        printf("Songname: %s\n"
+               "Modtype : %s\n",
+               mf->songname,
+               mf->modtype);
+
+        numsmp = mf->numsmp;
+        numins = mf->numins;
+        OptimizeTracks(mf);
+
+
+        // Write UNI header
+        // UNI format version 3.00 (#6)
+
+        _mm_write_UBYTES(unitag, 5, fpo);
+
+        _mm_write_M_UWORD(mf->flags, fpo);
+        _mm_write_UBYTE(mf->numchn, fpo);
+        _mm_write_UBYTE(mf->numvoices, fpo);
+        _mm_write_M_UWORD(mf->numpos, fpo);
+        _mm_write_M_UWORD(mf->numpat, fpo);
+        _mm_write_M_UWORD(mf->numtrk, fpo);
+        _mm_write_M_UWORD(numins, fpo);
+        _mm_write_M_UWORD(numsmp, fpo);
+        _mm_write_M_UWORD(mf->reppos, fpo);
+        _mm_write_UBYTE(mf->initspeed, fpo);
+        _mm_write_UBYTE(mf->inittempo, fpo);
+        _mm_write_UBYTE(mf->initvolume, fpo);
+
+        StringWrite(strip ? NULL : mf->songname, fpo);
+        StringWrite(strip ? NULL : mf->composer, fpo);
+        StringWrite(strip ? NULL : mf->comment, fpo);
+
+        _mm_write_UBYTES(mf->positions, mf->numpos, fpo);
+        _mm_write_M_UWORDS(mf->panning, mf->numchn, fpo);
+        _mm_write_UBYTES(mf->chanvol, mf->numchn, fpo);
+
+        // Write sample information
+
+        puts("Writing sample header information.. ");
+
+        s = mf->samples;
+        for(v=0; v<numsmp; v++, s++)
+        {   _mm_write_M_UWORD(s->flags, fpo);
+            _mm_write_M_ULONG(s->speed, fpo);
+            _mm_write_UBYTE(s->volume, fpo);
+            _mm_write_M_UWORD(s->panning, fpo);
+            _mm_write_M_ULONG(s->length, fpo);
+            _mm_write_M_ULONG(s->loopstart, fpo);
+            _mm_write_M_ULONG(s->loopend, fpo);
+            _mm_write_M_ULONG(s->susbegin, fpo);
+            _mm_write_M_ULONG(s->susend, fpo);
+
+            _mm_write_UBYTE(s->globvol, fpo);
+            _mm_write_UBYTE(s->vibflags, fpo);
+            _mm_write_UBYTE(s->vibtype, fpo);
+            _mm_write_UBYTE(s->vibsweep, fpo);
+            _mm_write_UBYTE(s->vibdepth, fpo);
+            _mm_write_UBYTE(s->vibrate, fpo);
+
+            StringWrite(strip ? NULL : s->samplename, fpo);
+        }
+
+
+        // Write instruments
+
+        if(mf->flags & UF_INST)
+        {   // This module uses instrument information, so save it out
+
+            puts("Writing instrument header information.. ");
+
+            i = mf->instruments;
+            for(v=0; v<numins; v++, i++)
+            {   _mm_write_UBYTE(i->flags, fpo);
+                _mm_write_UBYTE(i->nnatype, fpo);
+                _mm_write_UBYTE(i->dca, fpo);
+                _mm_write_UBYTE(i->dct, fpo);
+                _mm_write_UBYTE(i->globvol, fpo);
+                _mm_write_M_UWORD(i->panning, fpo);
+
+                _mm_write_UBYTE(i->pitpansep, fpo);
+                _mm_write_UBYTE(i->pitpancenter, fpo);
+                _mm_write_UBYTE(i->rvolvar, fpo);
+                _mm_write_UBYTE(i->rpanvar, fpo);
+
+                _mm_write_M_UWORD(i->volfade, fpo);
+    
+                // Dump out the volume envelope
+
+                _mm_write_UBYTE(i->volflg, fpo);
+                _mm_write_UBYTE(i->volpts, fpo);
+                _mm_write_UBYTE(i->volsusbeg, fpo);
+                _mm_write_UBYTE(i->volsusend, fpo);
+                _mm_write_UBYTE(i->volbeg, fpo);
+                _mm_write_UBYTE(i->volend, fpo);
+
+                for(w=0; w<i->volpts; w++)
+                {   _mm_write_M_SWORD(i->volenv[w].pos, fpo);
+                    _mm_write_M_SWORD(i->volenv[w].val, fpo);
+                }
+
+                // Dump out the panning envelope
+
+                _mm_write_UBYTE(i->panflg, fpo);
+                _mm_write_UBYTE(i->panpts, fpo);
+                _mm_write_UBYTE(i->pansusbeg, fpo);
+                _mm_write_UBYTE(i->pansusend, fpo);
+                _mm_write_UBYTE(i->panbeg, fpo);
+                _mm_write_UBYTE(i->panend, fpo);
+
+                for(w=0; w<i->panpts; w++)
+                {   _mm_write_M_SWORD(i->panenv[w].pos, fpo);
+                    _mm_write_M_SWORD(i->panenv[w].val, fpo);
+                }
+
+                // Dump out the pitch envelope
+
+                _mm_write_UBYTE(i->pitflg, fpo);
+                _mm_write_UBYTE(i->pitpts, fpo);
+                _mm_write_UBYTE(i->pitsusbeg, fpo);
+                _mm_write_UBYTE(i->pitsusend, fpo);
+                _mm_write_UBYTE(i->pitbeg, fpo);
+                _mm_write_UBYTE(i->pitend, fpo);
+
+                for(w=0; w<i->pitpts; w++)
+                {   _mm_write_M_SWORD(i->pitenv[w].pos, fpo);
+                    _mm_write_M_SWORD(i->pitenv[w].val, fpo);
+                }
+
+                _mm_write_UBYTES(i->samplenumber, 120, fpo);
+                _mm_write_UBYTES(i->samplenote, 120, fpo);
+
+                StringWrite(strip ? NULL : i->insname, fpo);
+            }
+        }
+
+        // Write pattern information
+
+        _mm_write_M_UWORDS(mf->pattrows, mf->numpat, fpo);
+        _mm_write_M_UWORDS(mf->patterns, mf->numpat * mf->numchn, fpo);
+
+        // Write track information
+
+        for(v=0; v<mf->numtrk; v++)
+        {   _mm_write_M_UWORD(TrkLen(mf->tracks[v]), fpo);
+            _mm_write_UBYTES(mf->tracks[v], TrkLen(mf->tracks[v]), fpo);
+        }
+
+
+        // Now dump out the sampled data
+        
+        s = mf->samples;
+        for(v=0; v<numsmp; v++, s++)
+        {   if(s->seekpos) _mm_fseek(fpi, s->seekpos, SEEK_SET);
+            _mm_copyfile(fpi, fpo, s->length * ((s->flags & SF_16BITS) ? 2 : 1));
+        }
+
+        puts("Done.");
+
+        // and clean up
+
+        fclose(fpo);
+        fclose(fpi);
+        MikMod_FreeSong(mf);
+    }
+    return 0;
+}
+
diff -upNr tintin++/src/misc.c tintin++-msp/src/misc.c
--- tintin++/src/misc.c	Sun Apr 24 21:00:52 1994
+++ tintin++-msp/src/misc.c	Fri Apr 10 17:54:48 1998
@@ -29,6 +29,7 @@ extern int togglesubs;
 extern char vars[10][BUFFER_SIZE]; /* the %0, %1, %2,....%9 variables */
 extern int mesvar[7];
 extern int verbatim;
+extern int msp;
 /****************************/
 /* the cr command           */
 /****************************/
@@ -322,6 +323,16 @@ void snoop_command(arg, ses)
   }
   else
     tintin_puts("#NO SESSION ACTIVE => NO SNOOPING", ses);
+}
+
+void msp_command(ses)
+     struct session *ses;
+{
+  msp=!msp;
+  if(msp)
+    tintin_puts("#MSP IS NOW ON.", ses);
+  else
+    tintin_puts("#MSP IS NOW OFF.", ses);
 }
 
 /**************************/
diff -upNr tintin++/src/msp.h tintin++-msp/src/msp.h
--- tintin++/src/msp.h	Thu Jan  1 01:00:00 1970
+++ tintin++-msp/src/msp.h	Fri Apr 10 17:54:48 1998
@@ -0,0 +1,22 @@
+#define MSP_POLL_USECS         100000
+#define MSPSND_MUSIC_CHANNELS  16
+
+#define MSP_FILE    1
+#define MSP_TYPE    2
+#define MSP_PLAY    3
+#define MSP_VOLUME  4
+#define MSP_COUNT   5
+#define MSP_OFF     6
+#define MSP_RESET   7
+#define MSP_APPLY   8
+#define MSP_PLAYING 9
+
+#define MSP_TYPE_SOUND 0
+#define MSP_TYPE_MUSIC 1
+
+#define TYPE_IGNORE 0
+#define TYPE_INT 1
+#define TYPE_STR 2
+
+#define MSP_PIPE_1 "/tmp/tintin++.msp-1"
+#define MSP_PIPE_2 "/tmp/tintin++.msp-2"
diff -upNr tintin++/src/parse.c tintin++-msp/src/parse.c
--- tintin++/src/parse.c	Mon Apr 25 13:51:30 1994
+++ tintin++-msp/src/parse.c	Fri Apr 10 17:54:48 1998
@@ -428,6 +428,9 @@ struct session *parse_tintin_command(com
   else if(is_abrev(command, "zap"))
     ses=zap_command(ses);
 
+  else if(is_abrev(command, "msp"))
+    msp_command(ses);
+  
   else {
     tintin_puts("#UNKNOWN TINTIN-COMMAND.", ses);
     prompt(NULL);
diff -upNr tintin++/src/sndserver.c tintin++-msp/src/sndserver.c
--- tintin++/src/sndserver.c	Thu Jan  1 01:00:00 1970
+++ tintin++-msp/src/sndserver.c	Fri Apr 10 17:58:38 1998
@@ -0,0 +1,396 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "mikmod/include/mikmod.h"
+#include "msp.h"
+
+// should we reset the structure upon startup?
+
+#define UNSET -0x33
+
+void msp_init();
+void msp_end();
+void msp_update();
+int msp_play();
+void read_str (int fd, char *buf);
+void msp_music_off();
+void msp_sound_off (int v);
+
+int eof_str = 1;
+
+struct msp_object
+{
+  char file[0xff];
+  int type;
+  UNIMOD *mf;
+  SAMPLE *sample;
+  int volume;
+  int count;
+  int voice;
+};
+
+typedef struct msp_object msp_object;
+
+msp_object obj;
+msp_object song;
+msp_object sound;
+
+int main (int argc, char *argv[])
+{ 
+  char str[0xff];
+  char *where_str;
+  int *where_i;
+  int fdo, fdi;
+  int type;
+  int what;
+  int v;
+  struct timeval tv;
+  fd_set readfds;
+  int s;
+
+  song.mf      = 0;
+  song.count   = 0;
+  sound.sample = 0;
+  sound.voice  = -1;
+  sound.count  = 0;
+  
+#ifdef MSP_FINAL
+  close (STDOUT_FILENO);
+#endif
+  
+  msp_init(); 
+  
+  mkfifo (MSP_PIPE_1, S_IRUSR|S_IWUSR);
+  mkfifo (MSP_PIPE_2, S_IRUSR|S_IWUSR);      
+  
+  fdo = open (MSP_PIPE_1, O_WRONLY);
+
+  // say hi 
+  str[0] = 33;
+  write (fdo, str, 1);
+  str[0] = 0;
+  fdi = open (MSP_PIPE_2, O_RDONLY);
+
+  type = TYPE_IGNORE;
+  while (1) {    
+    if (type == TYPE_STR) {
+      read_str (fdi, &where_str[0]);
+      printf ("%d %s\n", what, where_str);
+      type = TYPE_IGNORE;
+      continue;
+    } else if (type == TYPE_INT) {
+      read_str (fdi, &str[0]);
+      *where_i = atoi (str);
+      printf ("%d %d\n", what, *where_i);
+      type = TYPE_IGNORE;
+      continue;
+    }
+
+    do {
+      tv.tv_sec = 0;
+      tv.tv_usec = MSP_POLL_USECS;
+
+      FD_ZERO (&readfds);
+      FD_SET (fdi, &readfds);
+      s = select (fdi+1, &readfds, NULL, NULL, &tv);
+      
+//      puts ("update!");
+      msp_update();
+      
+      if (s < 0) {
+	exit(1);
+      }
+      
+    } while (eof_str && (s < 1 || !FD_ISSET (fdi, &readfds)));
+    
+    read_str(fdi, &str[0]);    
+    printf ("incoming: %s<<\n", str);
+    
+    what = atoi(str);
+    switch (what) {
+      default:
+        type = TYPE_IGNORE;
+        where_str = &str[0];
+        break;      
+      case MSP_FILE:
+        type = TYPE_STR;
+        where_str = &obj.file[0];
+        break;
+      case MSP_TYPE:
+        type = TYPE_INT;
+        where_i = &obj.type;
+        break;
+      case MSP_VOLUME:
+        type = TYPE_INT;
+        where_i = &obj.volume;
+        break;
+      case MSP_PLAYING:
+        type = TYPE_IGNORE;
+        if (obj.type == MSP_TYPE_SOUND) {	  
+	  read_str (fdi, &str[0]);
+	  sprintf (str, Voice_Stopped (atoi(str)) ? "0" : "1");
+	} else {
+	  sprintf (str, "1");
+	}
+        write (fdo, str, strlen(str)+1);
+        break;
+      case MSP_COUNT:
+        type = TYPE_INT;
+        where_i = &obj.count;
+        break;
+      case MSP_RESET:
+        type = TYPE_IGNORE;
+        obj.file[0] = 0;
+        obj.type    = MSP_TYPE_SOUND;
+        obj.mf      = 0;
+        obj.volume  = UNSET;
+        obj.count   = UNSET;
+        break;
+      case MSP_APPLY:
+        if (obj.type == MSP_TYPE_SOUND) {
+	  if (obj.volume != UNSET) {
+	    // ick!
+	  }
+	  if (obj.count != UNSET) {
+	    // ick!
+	  }
+	} else {
+	  if (obj.volume != UNSET) {
+	    song.volume = obj.volume;
+	    md_musicvolume = song.volume;
+	  }
+	  if (obj.count != UNSET) {
+	    song.count = obj.count;
+	  }
+	}
+        break;
+      case MSP_OFF:
+        if (obj.type == MSP_TYPE_SOUND) {
+	  read_str (fdi, &str[0]);
+//	  Voice_Stop (atoi (str));
+	  msp_sound_off (atoi(str));
+	  printf ("Silencing voice %s\n", str);
+	} else {
+	  msp_music_off();
+	  printf ("Silencing music\n");	 
+	}
+        break;
+      case MSP_PLAY:
+        type = TYPE_IGNORE;
+        if (obj.type == MSP_TYPE_SOUND) {
+	  v = msp_play();
+	  printf ("using voice: %d\n", v);
+	  sprintf (str, "%d\n", v);
+	  write (fdo, str, strlen(str)+1);
+	} else if (obj.type == MSP_TYPE_MUSIC) {
+	  printf ("playing song\n");
+	  msp_play();
+	}
+        break;
+    }    
+    
+  }
+}
+
+
+void msp_init()
+{
+  md_mixfreq      = 44100;
+  md_dmabufsize   = 32768;
+  md_device       = 0;
+  md_volume       = 128;
+  md_musicvolume  = 128;
+  md_sndfxvolume  = 128;
+  md_pansep       = 128;
+  md_reverb       = 0;
+  md_mode = DMODE_16BITS | DMODE_STEREO | DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
+//  MikMod_RegisterLoader(load_mod);
+  MikMod_RegisterDriver(drv_oss);
+  MikMod_RegisterAllLoaders();
+//  MikMod_RegisterAllDrivers();
+  MikMod_Init();
+  MikMod_SetNumVoices (MSPSND_MUSIC_CHANNELS, 8);  
+  msp_music_off();  
+}
+
+// ideally we should look for the sound that is using voice v, but since
+// msp is dumb enough to only support one sample channel, we dont have to.
+void msp_sound_off (int v)
+{
+  if (sound.voice == -1) {
+    return;
+  }
+  
+  Voice_Stop (sound.voice);
+  sound.voice = -1;
+}
+
+void msp_music_off()
+{
+  UNIMOD *mf;
+ 
+  // this return should be enabled only if loop is set on below
+  if (!strcmp(song.file, "/usr/local/share/sounds/modules/null.uni")) {
+    return;
+  }
+  
+  strcpy (song.file, "/usr/local/share/sounds/modules/null.uni");
+  mf = MikMod_LoadSong (song.file, MSPSND_MUSIC_CHANNELS);
+  if (!mf) {
+    puts ("msp_music_off: !mf");
+    return;
+  }    
+  if (song.mf) {
+    MikMod_FreeSong (song.mf);
+  }
+  song.mf        = mf;
+  song.volume    = 0;
+  md_musicvolume = song.volume;
+  mf->loop = 1;
+  Player_Start (song.mf);        
+}
+
+void msp_end()
+{
+  Player_Stop();
+  MikMod_FreeSong (song.mf);
+  MikMod_Exit();
+  // :TODO: we need something else here?
+}
+
+
+// has to return voice
+int msp_play (void)
+{
+  if (obj.type == MSP_TYPE_SOUND) {
+    SAMPLE *sample;
+    sample = WAV_LoadFN (obj.file);
+    if (!sample) {
+      puts ("msp_play: !sample");
+      return -1;
+    }
+    if (sound.voice > -1) {
+      Voice_Stop (sound.voice);
+    }
+    if (sound.sample) {
+      WAV_Free (sound.sample);
+    }
+    sound.sample = sample;
+    sound.voice  = MikMod_PlaySample (sound.sample, 0, 0);
+    sound.count  = obj.count;
+    sound.volume = obj.volume;
+    return sound.voice;
+    
+  } else if (obj.type == MSP_TYPE_MUSIC) {
+    UNIMOD *mf;
+    mf = MikMod_LoadSong (obj.file, MSPSND_MUSIC_CHANNELS);
+    if (!mf) {
+      puts ("msp_play: !mf");
+      return -1;
+    }    
+    Player_Stop();
+    if (song.mf) {
+      MikMod_FreeSong (song.mf);
+    }
+    song.mf        = mf;
+    song.volume    = obj.volume;
+    md_musicvolume = song.volume;
+//    md_musicvolume = 120;
+    mf->loop = 0;
+    Player_Start (song.mf);
+//    md_musicvolume = 120;
+    sound.count = obj.count;
+    return 0;
+  }
+  
+  puts ("msp_play: !obj.type");
+  return -1;
+}
+
+// calling this instead of something else might not make much sense, but
+// i tried _everything_ else and mikmod refuses to continue playing it
+void msp_voice_restart()
+{
+  if (sound.voice < 1) {
+    return;
+  }
+
+  // heh
+  Voice_Stop (sound.voice);
+  
+  if (sound.sample) {
+    WAV_Free (sound.sample);
+  }
+
+  sound.sample = WAV_LoadFN (obj.file);
+  sound.voice  = MikMod_PlaySample (sound.sample, 0, 0);
+}
+
+void msp_update()
+{
+  // update music
+  if (!Player_Active()) {
+    printf ("!active\n");
+    if (song.count != 0) {
+      if (song.count > 0) {
+	--song.count;
+      }
+      Player_SetPosition(0);      
+    } else {     
+      msp_music_off();
+    }
+    
+    msp_voice_restart();
+  }
+  
+  // update sounds. see comment in msp_sound_off()
+  // this thing is a mess.. clean up
+  if (sound.voice != -1 && Voice_Stopped (sound.voice)) {
+    if (sound.count >= -1) {
+      --sound.count;
+      if (sound.count == -1) {
+	sound.voice = -1;       
+      }  else {
+	if (sound.count == -2) {
+	  sound.count++;
+	}
+	Voice_Play (sound.voice, sound.sample, 0);
+	// looks like mikmod needs this
+	printf ("idle wait \n");
+	while (Voice_Stopped (sound.voice)) {
+	  MikMod_Update();
+	  usleep(1000);
+	}
+	printf ("replaying sample on voice %d\n", sound.voice);
+      }
+    }
+  }
+  
+  MikMod_Update();
+}
+
+
+void read_str (int fd, char *buf)
+{
+  static char b[0xff];
+  static char *p = &b[0];
+  
+  if (!*p) {
+    if (read (fd, b, 0xff) <= 0) {
+      exit(1);
+    };
+    p = &b[0];    
+  }
+  
+  while (*p != '\n' && *p != '\0') {
+    *(buf++) = *(p++);
+  }
+  
+  eof_str = (*(++p) == '\0');
+  *buf = '\0';
+}
+      
+      
+