OSDN Git Service

TiMidity++-2.12.0-pre1
authorMasanao Izumo <mo@goice.co.jp>
Fri, 18 Jan 2002 02:25:47 +0000 (11:25 +0900)
committerMasanao Izumo <mo@goice.co.jp>
Fri, 18 Jan 2002 02:25:47 +0000 (11:25 +0900)
18 files changed:
ChangeLog
NEWS
acinclude.m4
aclocal.m4
configs/msc-config.h
configure
configure.in
doc/C/Makefile.am
doc/C/Makefile.in
doc/C/README.alsaseq [new file with mode: 0644]
interface/alsaseq_c.c
timidity/alsa_a.c
timidity/instrum.c
timidity/instrum.h
timidity/modmid_a.c
timidity/playmidi.c
timidity/sndfont.c
timidity/timidity.c

index 3cc07fe..df4427b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,3 @@
-2002-01-18  Masanao Izumo <mo@goice.co.jp>
-
-       * Version 2.11.3 released.
-
 2002-01-17  Daisuke Aoki <dai@sweetparty.ne.jp>
 
        * configs/msc-config.h,interface/{w32g_i.c,w32g_pref.c,w32g_res.h,
 
        * timidity/C/timidity.1: Document fixed.
 
+2002-01-16  Takashi Iwai <tiwai@suse.de>
+           Masanao Izumo <mo@goice.co.jp>
+
+       * acinclude.m4,doc/C/Makefile.am,doc/C/README.alsaseq,
+         interface/alsaseq_c.c,timidity/alsa_a.c:
+         - Support ALSA 0.9
+         - Added alsaseq document (doc/C/README.alsaseq)
+
 2002-01-16  Saito <timidity@flashmail.com>
 
        * timidity/{playmidi.c,readmidi.c}: Bug fixed
+       * timidity/{playmidi.c,modmid_a.c}:
+         Change 'MOD -> MIDI file conversion' identifier from 'm' to 'M'.
 
 2002-01-16  Kentaro Sato <kentaro@ps.catv.ne.jp>
 
        * timidity/readmidi.c: Added TIMIDITY_TOOLS macro to build for timidity
          tools.
 
+2002-01-15  Masanao Izumo <mo@goice.co.jp>
+
+       * timidity/{instrum.c,instrum.h,timidity.c}:
+         New configuration parameter tune=[+-]number (float)
+         For example
+               bank 0
+               0 piano.pat tune=+1             # up 1 note key
+               1 britepno.pat tune=+2          # up 2 note key
+               2 synpiano.pat tune=-1.5        # down 1.5 note key
+
 2002-01-12  Masanao Izumo <mo@goice.co.jp>
 
        * Version 2.11.2 released.
diff --git a/NEWS b/NEWS
index 121ce5e..d74aeda 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,3 @@
-01/18, 2002
-    * Version 2.11.3 released.
-
 01/12, 2002
     * Version 2.11.2 released.
 
index a347ca6..167fcf7 100644 (file)
@@ -165,7 +165,7 @@ exit(0);
 )
 
 dnl Now that we know that we have the right version, let's see if we have the library and not just the headers.
-AC_CHECK_LIB([asound], [snd_cards],,
+AC_CHECK_LIB([asound], [snd_ctl_open],,
        [AC_MSG_RESULT(No linkable libasound was found.)]
 )
 
index 016d788..bae7576 100644 (file)
@@ -178,7 +178,7 @@ exit(0);
 )
 
 dnl Now that we know that we have the right version, let's see if we have the library and not just the headers.
-AC_CHECK_LIB([asound], [snd_cards],,
+AC_CHECK_LIB([asound], [snd_ctl_open],,
        [AC_MSG_RESULT(No linkable libasound was found.)]
 )
 
index ea30255..a2b6054 100644 (file)
 \r
 /* In VDS Macro AAA=BBB is not available. */\r
 #define __W32__\r
-#define TIMID_VERSION  "2.11.3"\r
+#define TIMID_VERSION  "2.12.0-pre1"\r
 #define DEFAULT_PATH   ".\\"\r
 #define AU_W32\r
 #define AU_VORBIS\r
index caff7c4..4ff1996 100755 (executable)
--- a/configure
+++ b/configure
@@ -1400,7 +1400,7 @@ fi
 
 # Define the identity of the package.
 PACKAGE=TiMidity++
-VERSION=2.11.3
+VERSION=2.12.0-pre1
 
 cat >>confdefs.h <<EOF
 #define PACKAGE "$PACKAGE"
@@ -7671,9 +7671,9 @@ echo "${ECHO_T}not present." >&6
 fi
 rm -f conftest.$ac_objext conftest.$ac_ext
 
-echo "$as_me:7674: checking for snd_cards in -lasound" >&5
-echo $ECHO_N "checking for snd_cards in -lasound... $ECHO_C" >&6
-if test "${ac_cv_lib_asound_snd_cards+set}" = set; then
+echo "$as_me:7674: checking for snd_ctl_open in -lasound" >&5
+echo $ECHO_N "checking for snd_ctl_open in -lasound... $ECHO_C" >&6
+if test "${ac_cv_lib_asound_snd_ctl_open+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   ac_check_lib_save_LIBS=$LIBS
@@ -7688,11 +7688,11 @@ extern "C"
 #endif
 /* We use char because int might match the return type of a gcc2
    builtin and then its argument prototype would still apply.  */
-char snd_cards ();
+char snd_ctl_open ();
 int
 main ()
 {
-snd_cards ();
+snd_ctl_open ();
   ;
   return 0;
 }
@@ -7709,18 +7709,18 @@ if { (eval echo "$as_me:7701: \"$ac_link\"") >&5
   ac_status=$?
   echo "$as_me:7710: \$? = $ac_status" >&5
   (exit $ac_status); }; }; then
-  ac_cv_lib_asound_snd_cards=yes
+  ac_cv_lib_asound_snd_ctl_open=yes
 else
   echo "$as_me: failed program was:" >&5
 cat conftest.$ac_ext >&5
-ac_cv_lib_asound_snd_cards=no
+ac_cv_lib_asound_snd_ctl_open=no
 fi
 rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-echo "$as_me:7721: result: $ac_cv_lib_asound_snd_cards" >&5
-echo "${ECHO_T}$ac_cv_lib_asound_snd_cards" >&6
-if test $ac_cv_lib_asound_snd_cards = yes; then
+echo "$as_me:7721: result: $ac_cv_lib_asound_snd_ctl_open" >&5
+echo "${ECHO_T}$ac_cv_lib_asound_snd_ctl_open" >&6
+if test $ac_cv_lib_asound_snd_ctl_open = yes; then
   cat >>confdefs.h <<EOF
 #define HAVE_LIBASOUND 1
 EOF
index 51a68ee..db164bc 100644 (file)
@@ -55,7 +55,7 @@ dnl
 AC_INIT(timidity/timidity.c)
 SHELL=${CONFIG_SHELL-/bin/sh}
 AC_CANONICAL_SYSTEM
-AM_INIT_AUTOMAKE(TiMidity++, 2.11.3)
+AM_INIT_AUTOMAKE(TiMidity++, 2.12.0-pre1)
 
 dnl To use CONTAINS() macro (See acinclude.m4)
 CONTAINS_INIT
index 758cd99..325ea27 100644 (file)
@@ -27,4 +27,5 @@ EXTRA_DIST = \
        README.sf \
        README.tk \
        README.xskin \
-       README.xaw
+       README.xaw \
+       README.alsaseq
index 7f0f166..d3eaf65 100644 (file)
@@ -136,7 +136,8 @@ EXTRA_DIST = \
        README.sf \
        README.tk \
        README.xskin \
-       README.xaw
+       README.xaw \
+       README.alsaseq
 
 subdir = doc/C
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
diff --git a/doc/C/README.alsaseq b/doc/C/README.alsaseq
new file mode 100644 (file)
index 0000000..19932b0
--- /dev/null
@@ -0,0 +1,135 @@
+=======================================================================
+                      ALSA Sequencer Interface
+          Copyright (c) 2000  Takashi Iwai <tiwai@suse.de>
+=======================================================================
+
+DESCRIPTION
+===========
+
+This document describes about the Advanced Linux Sound Architecture
+(ALSA) sequencer interface.  The ALSA sequencer interface communicates
+between ALSA sequencer core and timidity.  The interface receives
+events from sequencer and plays it in (quasi-)real-time.
+On this mode, TiMidity works purely as the software real-time MIDI
+render, that is as a software MIDI synth engine on ALSA.
+There is no scheduling routine in this interface, since all scheduling
+is done by ALSA sequencer core.
+
+For invoking ALSA sequencer interface, run timidity as follows:
+       % timidity -iA -B2,8 -Os -q0/0 -k0
+The fragment size is adjustable.  The smaller number gives better
+real-time response.  Then timidity shows new port numbers which were
+newly created (128:0 and 128:1 below).
+       ---------------------------------------
+       % timidity -iA -B2,8 -Os -q0/0 -k0
+       TiMidity starting in ALSA server mode
+       Opening sequencer port 128:0 128:1
+       ---------------------------------------
+These ports can be connected with any other sequencer ports.
+For example, playing a MIDI file via pmidi (what's an overkill :-),
+       % pmidi -p128:0 foo.mid
+If a midi file needs two ports, you may connect like this:
+       % pmidi -p128:0,128:1 bar.mid
+Connecting from external MIDI keyboard may become like this:
+       % aconnect 64:0 128:0
+
+INSTALLATION
+============
+
+Configure with --enable-alsaseq and --enable-audio=alsa option.
+Of course, other audio devices or interfaces can be chosen
+additionally, too.
+
+For getting better real-time response, timidity must be run as root
+(see below).  Set-UID root is the easiest way to achieve this.  You
+may change the owner and the permission of installed timidity binary
+as follows:
+       # chown root /usr/local/bin/timidity
+       # chmod 4755 /usr/local/bin/timidity
+
+Be aware that this might cause a security hole!
+
+
+REAL-TIME RESPONSE
+==================
+
+The interface tries to reset process scheduling to SCHED_FIFO and as
+high priority as possible.  The SCHED_FIFO'd program exhibits much
+better real-time response.  For example, without SCHED_FIFO, timidity
+may cause significant pauses at every time /proc is accessed. 
+For enabling this feature, timidity must be invoked by root or
+installed with set-uid root.
+
+
+INSTRUMENT LOADING
+==================
+
+Timidity loads instruments dynamically at each time a program change
+event is received.  This causes sometimes pauses due to buffer
+underrun during playback.  Furthermore, timidity resets the loaded
+instruments when the all subscriptions are disconnected.  Thus for
+keeping all loaded instruments even after playback is finished, you
+need to connect a dummy port (e.g. midi input port) to a timidity port
+via aconnect:
+       % aconnect 64:0 128:0
+
+
+RESET PLAYBACK
+==============
+
+You may stop all sounds during playback by sending SIGHUP signal to
+timiditiy.  The connections will be kept even after reset, but the
+events will be no longer processed.  For enabling the audio again, you 
+have to reconnect the ports.
+
+
+VISUALIZATION
+=============
+
+If you prefer a bit more fancy visual output, try my tiny program,
+aseqview.
+       % aseqview -p2 &
+Then connect two ports to timidity ports (assumed 129:0 and 129:1 were 
+created by aseqview):
+       % aconnect 129:0 128:0
+       % aconnect 129:1 128:1
+The outputs ought to be redirected to 129:0,1 instead of 128:0,1.
+       % pmidi -p129:0,129:1 foo.mid
+
+
+COMPATIBILITY WITH OSS
+======================
+
+You may access to timidity also via OSS MIDI emulation on ALSA
+sequencer.  Take a look at /proc/asound/seq/oss for checking the
+device number to be accessed.
+       ---------------------------------------
+       % cat /proc/asound/seq/oss
+       OSS sequencer emulation version 0.1.8
+       ALSA client number 63
+       ALSA receiver port 0
+       ...
+       midi 1: [TiMidity port 0] ALSA port 128:0
+         capability write / opened none
+       
+       midi 2: [TiMidity port 1] ALSA port 128:1
+         capability write / opened none
+       ---------------------------------------
+In the case above, the MIDI devices 1 and 2 are assigned to timidity.
+Now, play with playmidi:
+       % playmidi -e -D1 foo.mid
+
+
+BUGS
+====
+
+Well, well, they must be there..
+
+
+RESOURCES
+=========
+
+- ALSA home page
+       http://www.alsa-project.org
+- My ALSA hack page (including aseqview)
+       http://members.tripod.de/iwai/alsa.html
index b185fe8..799226e 100644 (file)
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-
     alsaseq_c.c - ALSA sequencer server interface
         Copyright (c) 2000  Takashi Iwai <tiwai@suse.de>
 
-
-    DESCRIPTION
-    ===========
-
-    This interface provides an ALSA sequencer interface which receives 
+    This interface provides an ALSA sequencer client which receives 
     events and plays it in real-time.  On this mode, TiMidity works
-    purely as software (real-time) MIDI render.  There is no
-    scheduling routine in this interface, since all scheduling is done 
-    by ALSA seqeuncer core.
-
-    For invoking ALSA sequencer interface, run timidity as folows:
-      % timidity -iA -B2,8 -q0/0 -k0
-    The fragment size can be adjustable.  The smaller number gives
-    better real-time response.  Then timidity shows new port numbers
-    which were newly created (128:0 and 128:1 below).
-    ---------------------------------------
-      % timidity -iA -B2,8 -q0/0 -k0
-      TiMidity starting in ALSA server mode
-      Opening sequencer port 128:0 128:1
-    ---------------------------------------
-    These ports can be connected with any other sequencer ports.
-    For example, playing a MIDI file via pmidi (what's an overkill :-),
-      % pmidi -p128:0 foo.mid
-    If a midi file needs two ports, you may connect like this:
-      % pmidi -p128:0,128:1 bar.mid
-    Vertual keyboard:
-      % vkeybd --addr 128:0
-    Connecting from external MIDI keyboard may become like this:
-      % aconnect 64:0 128:0
-
-    See also http://www.alsa-project.org/~iwai/alsa.html
-
-    The interface tries to reset process scheduling as SCHED_FIFO
-    and as high priority as possible.  For enabling this feature,
-    timidity must be invoked by root or installed with set-uid root.
-    The SCHED_FIFO'd program shows much better real-time response.
-    For example, without rescheduled, timidity may cause pauses at
-    every time /proc is accessed.
-
-    Timidity loads instruments dynamically at each time a PRM_CHANGE
-    event is received.  This causes sometimes pauses during playback.
-    It occurs often in the playback via pmidi.
-    Furthermore, timidity resets the loaded instruments when the all
-    subscriptions are disconnected.  Thus for keeping all loaded
-    instruments also after playback is finished, you need to connect a
-    dummy port (e.g. midi input port) to timidity port via aconnect:
-      % aconnect 64:0 128:0
-
-    If you prefer a bit more fancy visual output, use my tiny program, 
-    aseqview.
-      % aseqview -p2 &amp;
-    Then connect two ports to timidity ports:
-      % aconnect 129:0 128:0
-      % aconnect 129:1 128:1
-    The outputs ought to be redirected to 129:0,1 instead of 128:0,1.
-
-    You may access to timidity also via OSS MIDI emulation on ALSA
-    sequencer.  Take a look at /proc/asound/seq/oss for checking the
-    device number to be accessed.
-    ---------------------------------------
-      % cat /proc/asound/seq/oss
-      OSS sequencer emulation version 0.1.8
-      ALSA client number 63
-      ALSA receiver port 0
-      ...
-      midi 1: [TiMidity port 0] ALSA port 128:0
-        capability write / opened none
-
-      midi 2: [TiMidity port 1] ALSA port 128:1
-        capability write / opened none
-    ---------------------------------------
-    In the case above, the MIDI devices 1 and 2 are assigned to
-    timidity.  Now, play with playmidi:
-      % playmidi -e -D1 foo.mid
-
-
-    BUGS
-    ====
-
-    Well, well, they must be there..
-
-
-    */
+    as a software (quasi-)real-time MIDI synth engine.
+
+    See doc/C/README.alsaseq for more details.
+*/
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #include <sched.h>
 #include <sys/types.h>
 #include <sys/time.h>
-#include <netinet/in.h>
 #ifndef NO_STRING_H
 #include <string.h>
 #else
 #endif
 #include <signal.h>
 
-#ifdef HAVE_SYS_SOUNDCARD_H
 #include <sys/asoundlib.h>
-#else
-#include "server_defs.h"
-#endif /* HAVE_SYS_SOUNDCARD_H */
 
 #include "timidity.h"
 #include "common.h"
 #include "aq.h"
 #include "timer.h"
 
+
 #define NUM_PORTS      2       /* number of ports;
                                 * this should be configurable via command line..
                                 */
@@ -156,6 +74,67 @@ struct seq_context {
        int active;             /* */
 };
 
+static struct seq_context alsactx;
+
+#if SND_LIB_MINOR >= 6
+/* !! this is a dirty hack.  not sure to work in future !! */
+static int snd_seq_file_descriptor(snd_seq_t *handle)
+{
+       int pfds = snd_seq_poll_descriptors_count(handle, POLLIN);
+       if (pfds > 0) {
+               struct pollfd pfd;
+               if (snd_seq_poll_descriptors(handle, &pfd, 1, POLLIN) >= 0)
+                       return pfd.fd;
+       }
+       return -ENXIO;
+}
+
+static int alsa_seq_open(snd_seq_t **seqp)
+{
+       return snd_seq_open(seqp, "hw", SND_SEQ_OPEN_INPUT, 0);
+}
+
+static int alsa_create_port(snd_seq_t *seq, int index)
+{
+       snd_seq_port_info_t *pinfo;
+       char name[32];
+       int port;
+
+       sprintf(name, "TiMidity port %d", index);
+       port = snd_seq_create_simple_port(seq, name,
+                                         SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
+                                         SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+       if (port < 0) {
+               fprintf(stderr, "error in snd_seq_create_simple_port\n");
+               return -1;
+       }
+       return port;
+}
+
+#else
+static int alsa_seq_open(snd_seq_t **seqp)
+{
+       return snd_seq_open(seqp, SND_SEQ_OPEN_IN);
+}
+
+static int alsa_create_port(snd_seq_t *seq, int index)
+{
+       snd_seq_port_info_t pinfo;
+
+       memset(&pinfo, 0, sizeof(pinfo));
+       sprintf(pinfo.name, "TiMidity port %d", index);
+       pinfo.capability = SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE;
+       pinfo.type = SND_SEQ_PORT_TYPE_MIDI_GENERIC;
+       strcpy(pinfo.group, SND_SEQ_GROUP_DEVICE);
+       if (snd_seq_create_port(alsactx.handle, &pinfo) < 0) {
+               fprintf(stderr, "error in snd_seq_create_simple_port\n");
+               return -1;
+       }
+       return pinfo.port;
+}
+
+#endif
+
 static int ctl_open(int using_stdin, int using_stdout);
 static void ctl_close(void);
 static int ctl_read(int32 *valp);
@@ -181,10 +160,9 @@ ControlMode ctl=
     ctl_event
 };
 
+static int time_advance;
 static int32 event_time_offset;
 static FILE *outfp;
-static struct seq_context alsactx;
-static struct seq_context *ctxp = &alsactx;
 
 /*ARGSUSED*/
 static int ctl_open(int using_stdin, int using_stdout)
@@ -239,17 +217,29 @@ static RETSIGTYPE sig_timeout(int sig)
     /* Expect EINTR */
 }
 
-static void doit(void);
-static int do_sequencer(void);
+static void doit(struct seq_context *ctxp);
+static int do_sequencer(struct seq_context *ctxp);
+static int start_sequencer(struct seq_context *ctxp);
+static void stop_sequencer(struct seq_context *ctxp);
 static void server_reset(void);
 
+/* reset all when SIGHUP is received */
+static RETSIGTYPE sig_reset(int sig)
+{
+       if (alsactx.active) {
+               stop_sequencer(&alsactx);
+               server_reset();
+       }
+       signal(SIGHUP, sig_reset);
+}
+
+/*
+ * set the process to realtime privs
+ */
 static int set_realtime_priority(void)
 {
        struct sched_param schp;
 
-        /*
-         * set the process to realtime privs
-         */
         memset(&schp, 0, sizeof(schp));
         schp.sched_priority = sched_get_priority_max(SCHED_FIFO);
 
@@ -263,7 +253,7 @@ static int set_realtime_priority(void)
 
 static void ctl_pass_playing_list(int n, char *args[])
 {
-       snd_seq_port_info_t pinfo;
+       double btime;
        int i;
 
 #ifdef SIGPIPE
@@ -274,45 +264,53 @@ static void ctl_pass_playing_list(int n, char *args[])
 
        set_realtime_priority();
 
-       if (snd_seq_open(&ctxp->handle, SND_SEQ_OPEN) < 0) {
+       if (alsa_seq_open(&alsactx.handle) < 0) {
                fprintf(stderr, "error in snd_seq_open\n");
                return;
        }
-       ctxp->client = snd_seq_client_id(ctxp->handle);
-       ctxp->fd = snd_seq_file_descriptor(ctxp->handle);
+       alsactx.client = snd_seq_client_id(alsactx.handle);
+       alsactx.fd = snd_seq_file_descriptor(alsactx.handle);
+       snd_seq_set_client_pool_input(alsactx.handle, 1000); /* enough? */
 
        printf("Opening sequencer port:");
        for (i = 0; i < NUM_PORTS; i++) {
-               snd_seq_port_info_t pinfo;
-               memset(&pinfo, 0, sizeof(pinfo));
-               sprintf(pinfo.name, "TiMidity port %d", i);
-               pinfo.capability = SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE;
-               pinfo.type = SND_SEQ_PORT_TYPE_MIDI_GENERIC;
-               strcpy(pinfo.group, SND_SEQ_GROUP_DEVICE);
-               if (snd_seq_create_port(ctxp->handle, &pinfo) < 0) {
-                       fprintf(stderr, "error in snd_seq_create_simple_port\n");
+               int port;
+               port = alsa_create_port(alsactx.handle, i);
+               if (port < 0)
                        return;
-               }
-               ctxp->port[i] = pinfo.port;
-               printf(" %d:%d", ctxp->client, ctxp->port[i]);
+               alsactx.port[i] = port;
+               printf(" %d:%d", alsactx.client, alsactx.port[i]);
        }
        printf("\n");
 
-       ctxp->used = 0;
-       ctxp->active = 0;
+       alsactx.used = 0;
+       alsactx.active = 0;
 
        opt_realtime_playing = 2; /* Enable loading patch while playing */
        allocate_cache_size = 0; /* Don't use pre-calclated samples */
-       /* aq_set_soft_queue(-1.0, 0.0); */
+
+       /* set the audio queue size as minimum as possible, since
+        * we don't have to use audio queue..
+        */
+       play_mode->acntl(PM_REQ_GETFRAGSIZ, &time_advance);
+       if (!(play_mode->encoding & PE_MONO))
+               time_advance >>= 1;
+       if (play_mode->encoding & PE_16BIT)
+               time_advance >>= 1;
+       btime = (double)time_advance / play_mode->rate;
+       btime *= 1.01; /* to be sure */
+       aq_set_soft_queue(btime, 0.0);
+
        alarm(0);
        signal(SIGALRM, sig_timeout);
        signal(SIGINT, safe_exit);
        signal(SIGTERM, safe_exit);
+       signal(SIGHUP, sig_reset);
 
        play_mode->close_output();
        for (;;) {
                server_reset();
-               doit();
+               doit(&alsactx);
        }
 }
 
@@ -341,28 +339,23 @@ static void stop_playing(void)
        }
 }
 
-static void doit(void)
+static void doit(struct seq_context *ctxp)
 {
        for (;;) {
                while (snd_seq_event_input_pending(ctxp->handle, 1)) {
-                       if (do_sequencer())
+                       if (do_sequencer(ctxp))
                                goto __done;
                }
                if (ctxp->active) {
                        double fill_time;
                        MidiEvent ev;
 
-                       aq_add(NULL, 0);
-#if 0
-                       fill_time = high_time_at - (double)aq_filled() / play_mode->rate;
-                       if (fill_time <= 0)
-                               continue;
-                       event_time_offset += (int32)(fill_time * play_mode->rate);
-#endif
-                       event_time_offset += play_mode->rate / TICKTIME_HZ;
+                       /*event_time_offset += play_mode->rate / TICKTIME_HZ;*/
+                       event_time_offset += time_advance;
                        ev.time = event_time_offset;
                        ev.type = ME_NONE;
                        play_event(&ev);
+                       aq_fill_nonblocking();
                } else {
                        fd_set rfds;
                        FD_ZERO(&rfds);
@@ -374,11 +367,7 @@ static void doit(void)
 
 __done:
        if (ctxp->active) {
-               stop_playing();
-               play_mode->close_output();
-               free_instruments(0);
-               free_global_mblock();
-               ctxp->active = 0;
+               stop_sequencer(ctxp);
        }
 }
 
@@ -391,10 +380,32 @@ static void server_reset(void)
        event_time_offset = 0;
 }
 
+static int start_sequencer(struct seq_context *ctxp)
+{
+       if (play_mode->open_output() < 0) {
+               ctl.cmsg(CMSG_FATAL, VERB_NORMAL,
+                        "Couldn't open %s (`%c')",
+                        play_mode->id_name, play_mode->id_character);
+               return 0;
+       }
+       ctxp->active = 1;
+       return 1;
+}
+
+static void stop_sequencer(struct seq_context *ctxp)
+{
+       stop_playing();
+       play_mode->close_output();
+       free_instruments(0);
+       free_global_mblock();
+       ctxp->used = 0;
+       ctxp->active = 0;
+}
+
 #define NOTE_CHAN(ev)  ((ev)->dest.port * 16 + (ev)->data.note.channel)
 #define CTRL_CHAN(ev)  ((ev)->dest.port * 16 + (ev)->data.control.channel)
 
-static int do_sequencer(void)
+static int do_sequencer(struct seq_context *ctxp)
 {
        int n;
        MidiEvent ev;
@@ -447,6 +458,23 @@ static int do_sequencer(void)
                        seq_play_event(&ev);
                break;
 
+       case SND_SEQ_EVENT_CONTROL14:
+               if (aevp->data.control.param < 0 || aevp->data.control.param >= 32)
+                       break;
+               if (! convert_midi_control_change(CTRL_CHAN(aevp),
+                                                 aevp->data.control.param,
+                                                 (aevp->data.control.value >> 7) & 0x7f,
+                                                 &ev))
+                       break;
+               seq_play_event(&ev);
+               if (! convert_midi_control_change(CTRL_CHAN(aevp),
+                                                 aevp->data.control.param + 32,
+                                                 aevp->data.control.value & 0x7f,
+                                                 &ev))
+                       break;
+               seq_play_event(&ev);
+               break;
+                   
        case SND_SEQ_EVENT_PITCHBEND:
                ev.type    = ME_PITCHWHEEL;
                ev.channel = CTRL_CHAN(aevp);
@@ -463,32 +491,97 @@ static int do_sequencer(void)
                seq_play_event(&ev);
                break;
                
+       case SND_SEQ_EVENT_NONREGPARAM:
+               /* Break it back into its controler values */
+               ev.type = ME_NRPN_MSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = (aevp->data.control.param >> 7) & 0x7f;
+               seq_play_event(&ev);
+               ev.type = ME_NRPN_LSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = aevp->data.control.param & 0x7f;
+               seq_play_event(&ev);
+               ev.type = ME_DATA_ENTRY_MSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = (aevp->data.control.value >> 7) & 0x7f;
+               seq_play_event(&ev);
+               ev.type = ME_DATA_ENTRY_LSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = aevp->data.control.value & 0x7f;
+               seq_play_event(&ev);
+               break;
+
+       case SND_SEQ_EVENT_REGPARAM:
+               /* Break it back into its controler values */
+               ev.type = ME_RPN_MSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = (aevp->data.control.param >> 7) & 0x7f;
+               seq_play_event(&ev);
+               ev.type = ME_RPN_LSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = aevp->data.control.param & 0x7f;
+               seq_play_event(&ev);
+               ev.type = ME_DATA_ENTRY_MSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = (aevp->data.control.value >> 7) & 0x7f;
+               seq_play_event(&ev);
+               ev.type = ME_DATA_ENTRY_LSB;
+               ev.channel = CTRL_CHAN(aevp);
+               ev.a = aevp->data.control.value & 0x7f;
+               seq_play_event(&ev);
+               break;
+
        case SND_SEQ_EVENT_SYSEX:
                if (parse_sysex_event(aevp->data.ext.ptr + 1, aevp->data.ext.len - 1, &ev))
                        seq_play_event(&ev);
                break;
 
+#if SND_LIB_MINOR >= 6
+#define snd_seq_addr_equal(a,b)        ((a)->client == (b)->client && (a)->port == (b)->port)
+       case SND_SEQ_EVENT_PORT_SUBSCRIBED:
+               if (snd_seq_addr_equal(&aevp->data.connect.dest, &aevp->dest)) {
+                       if (! ctxp->active) {
+                               if (! start_sequencer(ctxp)) {
+                                       snd_seq_free_event(aevp);
+                                       return 0;
+                               }
+                       }
+                       ctxp->used++;
+               }
+               break;
+
+       case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
+               if (snd_seq_addr_equal(&aevp->data.connect.dest, &aevp->dest)) {
+                       if (ctxp->active) {
+                               ctxp->used--;
+                               if (ctxp->used <= 0) {
+                                       snd_seq_free_event(aevp);
+                                       return 1; /* quit now */
+                               }
+                       }
+               }
+               break;
+#else
        case SND_SEQ_EVENT_PORT_USED:
-               if (ctxp->used == 0) {
-                       if (play_mode->open_output() < 0) {
-                               ctl.cmsg(CMSG_FATAL, VERB_NORMAL,
-                                        "Couldn't open %s (`%c')",
-                                        play_mode->id_name, play_mode->id_character);
+               if (! ctxp->active) {
+                       if (! start_sequencer(ctxp)) {
                                snd_seq_free_event(aevp);
                                return 0;
                        }
-                       ctxp->active = 1;
                }
                ctxp->used++;
                break;
 
        case SND_SEQ_EVENT_PORT_UNUSED:
-               ctxp->used--;
-               if (ctxp->used <= 0) {
-                       snd_seq_free_event(aevp);
-                       return 1; /* quit now */
+               if (ctxp->active) {
+                       ctxp->used--;
+                       if (ctxp->used <= 0) {
+                               snd_seq_free_event(aevp);
+                               return 1; /* quit now */
+                       }
                }
                break;
+#endif
                
        default:
                /*printf("Unsupported event %d\n", aevp->type);*/
index 7aa405a..d804381 100644 (file)
@@ -3,6 +3,7 @@
     Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
     Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
     ALSA 0.[56] support by Katsuhiro Ueno <katsu@blue.sky.or.jp>
+                rewritten by Takashi Iwai <tiwai@suse.de>
 
     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
@@ -65,11 +66,6 @@ static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
 static void close_output(void);
 static int output_data(char *buf, int32 nbytes);
 static int acntl(int request, void *arg);
-static int total_bytes;
-static int output_counter;
-#if ALSA_LIB >= 5
-static int bytes_to_go;
-#endif
 
 /* export the playback mode */
 
@@ -80,7 +76,9 @@ PlayMode dpm = {
   -1,
   {0}, /* default: get all the buffer fragments you can */
   "ALSA pcm device", 's',
-  "/dev/snd/pcm00",
+  "", /* here leave it empty so that the pcm device name can be given
+       * via command line option.
+       */
   open_output,
   close_output,
   output_data,
@@ -96,16 +94,41 @@ PlayMode dpm = {
 
 /*ALSA PCM handler*/
 static snd_pcm_t* handle = NULL;
+#if ALSA_LIB <= 5
 static int card = 0;
 static int device = 0;
+#endif
+static int total_bytes = -1;
 static int frag_size = 0;
+static int sample_shift = 0;
+static int output_counter;
+
+#if ALSA_LIB > 5
+static char *alsa_device_name(void)
+{
+  static char name[32];
+  if (dpm.name && *dpm.name)
+    return dpm.name;
+  else
+    return "alsa pcm";
+}
+#else
+static char *alsa_device_name(void)
+{
+  static char name[32];
+  sprintf(name, "card%d/device%d", card, device);
+  return name;
+}
+#endif
 
 static void error_report (int snd_error)
 {
   ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
-           dpm.name, snd_strerror (snd_error));
+           alsa_device_name(), snd_strerror (snd_error));
 }
 
+
+#if ALSA_LIB < 6
 /*return value == 0 sucess
                == -1 fails
  */
@@ -212,6 +235,383 @@ static int check_sound_cards (int* card__, int* device__,
 
   return 0;
 }
+#endif
+
+
+#if ALSA_LIB > 5
+/*================================================================
+ * ALSA API version 0.9.x
+ *================================================================*/
+
+/*return value == 0 sucess
+               == 1 warning
+               == -1 fails
+ */
+static int open_output(void)
+{
+  int orig_rate = dpm.rate;
+  int ret_val = 0;
+  int tmp, frags, r, pfds;
+  int buf_time, rate;
+  static const char default_pcm_name[] = "default";
+  const char* env_pcm_name = getenv("TIMIDITY_PCM_NAME");
+  snd_pcm_hw_params_t *pinfo;
+  snd_pcm_sw_params_t *swpinfo;
+
+  if (! dpm.name || ! *dpm.name) {
+    if (env_pcm_name && *env_pcm_name)
+      dpm.name = strdup(env_pcm_name);
+  }
+  if (! dpm.name || ! *dpm.name)
+    dpm.name = strdup(default_pcm_name);
+
+  tmp = snd_pcm_open(&handle, dpm.name, SND_PCM_STREAM_PLAYBACK, 0);
+  if (tmp < 0) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Can't open pcm device '%s'.", dpm.name);
+    return -1;
+  }
+
+  snd_pcm_hw_params_alloca(&pinfo);
+  snd_pcm_sw_params_alloca(&swpinfo);
+
+  if (snd_pcm_hw_params_any(handle, pinfo) < 0) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+             "ALSA pcm '%s' can't initialize hw_params",
+             alsa_device_name());
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+#ifdef LITTLE_ENDIAN
+#define S16_FORMAT     SND_PCM_FORMAT_S16_LE
+#define U16_FORMAT     SND_PCM_FORMAT_U16_LE
+#else
+#define S16_FORMAT     SND_PCM_FORMAT_S16_BE
+#define U16_FORMAT     SND_PCM_FORMAT_U16_LE
+#endif
+
+  dpm.encoding &= ~(PE_ULAW|PE_ALAW|PE_BYTESWAP);
+  /*check sample bit*/
+  if (snd_pcm_hw_params_test_format(handle, pinfo, S16_FORMAT) < 0 &&
+      snd_pcm_hw_params_test_format(handle, pinfo, U16_FORMAT) < 0)
+    dpm.encoding &= ~PE_16BIT; /*force 8bit samples*/
+  if (snd_pcm_hw_params_test_format(handle, pinfo, SND_PCM_FORMAT_U8) < 0 &&
+      snd_pcm_hw_params_test_format(handle, pinfo, SND_PCM_FORMAT_S8) < 0)
+    dpm.encoding |= PE_16BIT; /*force 16bit samples*/
+
+  /*check format*/
+  if (dpm.encoding & PE_16BIT) {
+    /*16bit*/
+    if (snd_pcm_hw_params_set_format(handle, pinfo, S16_FORMAT) == 0)
+      dpm.encoding |= PE_SIGNED;
+    else if (snd_pcm_hw_params_set_format(handle, pinfo, U16_FORMAT) == 0)
+      dpm.encoding &= ~PE_SIGNED;
+    else {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+               "ALSA pcm '%s' doesn't support 16 bit sample width",
+               alsa_device_name());
+      snd_pcm_close(handle);
+      return -1;
+    }
+  } else {
+    /*8bit*/
+    if (snd_pcm_hw_params_set_format(handle, pinfo, SND_PCM_FORMAT_U8) == 0)
+      dpm.encoding &= ~PE_SIGNED;
+    else if (snd_pcm_hw_params_set_format(handle, pinfo, SND_PCM_FORMAT_S8) == 0)
+      dpm.encoding |= PE_SIGNED;
+    else {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+               "ALSA pcm '%s' doesn't support 8 bit sample width",
+               alsa_device_name());
+      snd_pcm_close(handle);
+      return -1;
+    }
+  }
+
+  if (snd_pcm_hw_params_set_access(handle, pinfo,
+                                  SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+             "ALSA pcm '%s' doesn't support interleaved data",
+             alsa_device_name());
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+  /*check rate*/
+  r = snd_pcm_hw_params_get_rate_min(pinfo, NULL);
+  if (r >= 0 && r > dpm.rate) {
+    dpm.rate = r;
+    ret_val = 1;
+  }
+  r = snd_pcm_hw_params_get_rate_max(pinfo, NULL);
+  if (r >= 0 && r < dpm.rate) {
+    dpm.rate = r;
+    ret_val = 1;
+  }
+  if ((rate = snd_pcm_hw_params_set_rate_near(handle, pinfo, dpm.rate, 0)) < 0) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+             "ALSA pcm '%s' can't set rate %d",
+             alsa_device_name(), dpm.rate);
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+  /*check channels*/
+  if (dpm.encoding & PE_MONO) {
+    if (snd_pcm_hw_params_test_channels(handle, pinfo, 1) < 0)
+      dpm.encoding &= ~PE_MONO;
+  } else {
+    if (snd_pcm_hw_params_test_channels(handle, pinfo, 2) < 0)
+      dpm.encoding |= PE_MONO;
+  }
+    
+  if (dpm.encoding & PE_MONO) {
+    if (snd_pcm_hw_params_set_channels(handle, pinfo, 1) < 0) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+               "ALSA pcm '%s' can't set mono channel",
+               alsa_device_name());
+      snd_pcm_close(handle);
+      return -1;
+    }
+  } else {
+    if (snd_pcm_hw_params_set_channels(handle, pinfo, 2) < 0) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+               "ALSA pcm '%s' can't set stereo channels",
+               alsa_device_name());
+      snd_pcm_close(handle);
+      return -1;
+    }
+  }
+
+  sample_shift = 0;
+  if (!(dpm.encoding & PE_MONO))
+    sample_shift++;
+  if (dpm.encoding & PE_16BIT)
+    sample_shift++;
+
+  /* Set buffer fragment size (in extra_param[1]) */
+  if (dpm.extra_param[1] != 0)
+    frag_size = dpm.extra_param[1];
+  else
+    frag_size = audio_buffer_size << sample_shift;
+
+  /* Set buffer fragments (in extra_param[0]) */
+  if (dpm.extra_param[0] == 0)
+    frags = 4;
+  else
+    frags = dpm.extra_param[0];
+
+  total_bytes = frag_size * frags;
+  ctl->cmsg(CMSG_INFO, VERB_VERBOSE,
+           "Requested buffer size %d, fragment size %d",
+           total_bytes, frag_size);
+  if ((tmp = snd_pcm_hw_params_set_buffer_size_near(handle, pinfo, total_bytes >> sample_shift)) < 0) {
+    ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+             "ALSA pcm '%s' can't set buffer size %d",
+             alsa_device_name(), total_bytes);
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+  if ((tmp = snd_pcm_hw_params_set_period_size_near(handle, pinfo, frag_size >> sample_shift, 0)) < 0) {
+    ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+             "ALSA pcm '%s' can't set period size %d",
+             alsa_device_name(), frag_size);
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+  if (snd_pcm_hw_params(handle, pinfo) < 0) {
+    snd_output_t *log;
+    ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+             "ALSA pcm '%s' can't set hw_params", alsa_device_name());
+    snd_output_stdio_attach(&log, stderr, 0);
+    snd_pcm_hw_params_dump(pinfo, log);
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+  total_bytes = snd_pcm_hw_params_get_buffer_size(pinfo) << sample_shift;
+  frag_size = snd_pcm_hw_params_get_period_size(pinfo, NULL) << sample_shift;
+  ctl->cmsg(CMSG_INFO, VERB_VERBOSE,
+           "ALSA pcm '%s' set buffer size %d, period size %d bytes",
+           alsa_device_name(), total_bytes, frag_size);
+  tmp = snd_pcm_hw_params_get_rate(pinfo, NULL);
+  if (tmp > 0 && tmp != dpm.rate) {
+    dpm.rate = tmp;
+    ret_val = 1;
+  }
+  if (orig_rate != dpm.rate) {
+    ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+             "Output rate adjusted to %d Hz (requested %d Hz)",
+             dpm.rate, orig_rate);
+  }
+  snd_pcm_sw_params_current(handle, swpinfo);
+  snd_pcm_sw_params_set_start_threshold(handle, swpinfo, total_bytes >> sample_shift);
+  snd_pcm_sw_params_set_stop_threshold(handle, swpinfo, total_bytes >> sample_shift);
+
+  tmp = snd_pcm_prepare(handle);
+  if (tmp < 0) {
+    ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+             "unable to prepare channel\n");
+    snd_pcm_close(handle);
+    return -1;
+  }
+
+  pfds = snd_pcm_poll_descriptors_count(handle);
+  if (pfds > 1) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "too many poll descriptors: %s",
+             alsa_device_name());
+    close_output ();
+    return -1;
+  } else if (pfds == 1) {
+    struct pollfd pfd;
+    if (snd_pcm_poll_descriptors(handle, &pfd, 1) >= 0)
+      dpm.fd = pfd.fd;
+    else
+      dpm.fd = -1;
+  } else
+    dpm.fd = -1;
+
+  output_counter = 0;
+
+  return ret_val;
+}
+
+static void close_output(void)
+{
+  if (handle) {
+    int ret = snd_pcm_close (handle);
+    if (ret < 0)
+      error_report (ret);
+    handle = NULL;
+  }
+
+  dpm.fd = -1;
+}
+
+static int output_data(char *buf, int32 nbytes)
+{
+  int n;
+  int nframes, shift;
+
+  if (! handle)
+    return -1;
+
+  nframes = nbytes;
+  shift = 0;
+  if (!(dpm.encoding & PE_MONO))
+    shift++;
+  if (dpm.encoding & PE_16BIT)
+    shift++;
+  nframes >>= shift;
+  
+  while (nframes > 0) {
+    n = snd_pcm_writei(handle, buf, nframes);
+    if (n == -EAGAIN || (n >= 0 && n < nframes)) {
+      snd_pcm_wait(handle, 1000);
+    } else if (n == -EPIPE) {
+      snd_pcm_status_t *status;
+      snd_pcm_status_alloca(&status);
+      if (snd_pcm_status(handle, status) < 0) {
+       ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: cannot get status", alsa_device_name());
+       return -1;
+      }
+      ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+               "%s: underrun at %ld", alsa_device_name(), output_counter << sample_shift);
+      snd_pcm_prepare(handle);
+    } else if (n < 0) {
+      ctl->cmsg(CMSG_WARNING, VERB_DEBUG,
+               "%s: %s", alsa_device_name(),
+               (n < 0) ? snd_strerror(n) : "write error");
+      return -1;
+    }
+    if (n > 0) {
+      nframes -= n;
+      buf += n << shift;
+      output_counter += n;
+    }
+  }
+
+  return 0;
+}
+
+static int acntl(int request, void *arg)
+{
+  int i;
+  snd_pcm_status_t *status;
+
+  if (handle == NULL)
+    return -1;
+
+  switch (request) {
+  case PM_REQ_GETFRAGSIZ:
+    if (frag_size == 0)
+      return -1;
+    *((int *)arg) = frag_size;
+    return 0;
+
+  case PM_REQ_GETQSIZ:
+    if (total_bytes == -1)
+      return -1;
+    *((int *)arg) = total_bytes;
+    return 0;
+
+  case PM_REQ_GETFILLABLE:
+    if (total_bytes == -1)
+      return -1;
+    snd_pcm_status_alloca(&status);
+    if (snd_pcm_status(handle, status) < 0)
+      return -1;
+    *((int *)arg) = snd_pcm_status_get_avail(status);
+    return 0;
+    
+  case PM_REQ_GETFILLED:
+    if (total_bytes == -1)
+      return -1;
+    snd_pcm_status_alloca(&status);
+    if (snd_pcm_status(handle, status) < 0)
+      return -1;
+    *((int *)arg) = snd_pcm_status_get_delay(status);
+    return 0;
+
+  case PM_REQ_GETSAMPLES:
+    if (total_bytes == -1)
+      return -1;
+    snd_pcm_status_alloca(&status);
+    if (snd_pcm_status(handle, status) < 0)
+      return -1;
+    *((int *)arg) = snd_pcm_status_get_delay(status) + output_counter;
+    return 0;
+
+  case PM_REQ_DISCARD:
+    if (snd_pcm_drop(handle) < 0)
+      return -1;
+    if (snd_pcm_prepare(handle) < 0)
+      return -1;
+    output_counter = 0;
+    return 0;
+
+  case PM_REQ_FLUSH:
+    if (snd_pcm_drain(handle) < 0)
+      return -1;
+    if (snd_pcm_prepare(handle) < 0)
+      return -1;
+    output_counter = 0;
+    return 0;
+  }
+  return -1;
+}
+
+
+/* end ALSA API 0.9.x */
+
+
+#elif ALSA_LIB == 5
+
+/*================================================================
+ * ALSA API version 0.5.x
+ *================================================================*/
 
 /*return value == 0 sucess
                == 1 warning
@@ -222,31 +622,16 @@ static int set_playback_info (snd_pcm_t* handle__,
                              const int32 extra_param[5])
 {
   int ret_val = 0;
-#if ALSA_LIB < 5
-  const int32 orig_encoding = *encoding__;
-#endif
   const int32 orig_rate = *rate__;
   int tmp;
-#if ALSA_LIB >= 5
   snd_pcm_channel_info_t   pinfo;
   snd_pcm_channel_params_t pparams;
   snd_pcm_channel_setup_t  psetup;
-#else
-  snd_pcm_playback_info_t pinfo;
-  snd_pcm_format_t pcm_format;
-  struct snd_pcm_playback_params pparams;
-  struct snd_pcm_playback_status pstatus;
-  memset (&pcm_format, 0, sizeof (pcm_format));
-#endif
-  memset (&pparams, 0, sizeof (pparams));
 
-#if ALSA_LIB >= 5
   memset (&pinfo, 0, sizeof (pinfo));
+  memset (&pparams, 0, sizeof (pparams));
   pinfo.channel = SND_PCM_CHANNEL_PLAYBACK;
   tmp = snd_pcm_channel_info (handle__, &pinfo);
-#else
-  tmp = snd_pcm_playback_info (handle__, &pinfo);
-#endif
   if (tmp < 0)
     {
       error_report (tmp);
@@ -254,31 +639,19 @@ static int set_playback_info (snd_pcm_t* handle__,
     }
 
   /*check sample bit*/
-#if ALSA_LIB >= 5
   if (!(pinfo.formats & ~(SND_PCM_FMT_S8 | SND_PCM_FMT_U8)))
     *encoding__ &= ~PE_16BIT; /*force 8bit samples*/
   if (!(pinfo.formats & ~(SND_PCM_FMT_S16 | SND_PCM_FMT_U16)))
     *encoding__ |= PE_16BIT; /*force 16bit samples*/
-#else
-  if ((pinfo.flags & SND_PCM_PINFO_8BITONLY) != 0)
-    *encoding__ &= ~PE_16BIT; /*force 8bit samples*/
-  if ((pinfo.flags & SND_PCM_PINFO_16BITONLY) != 0)
-    *encoding__ |= PE_16BIT; /*force 16bit samples*/
-#endif
 
   /*check rate*/
   if (pinfo.min_rate > *rate__)
     *rate__ = pinfo.min_rate;
   if (pinfo.max_rate < *rate__)
     *rate__ = pinfo.max_rate;
-#if ALSA_LIB >= 5
   pparams.format.rate = *rate__;
-#else
-  pcm_format.rate = *rate__;
-#endif
 
   /*check channels*/
-#if ALSA_LIB >= 5
   if ((*encoding__ & PE_MONO) != 0 && pinfo.min_voices > 1)
     *encoding__ &= ~PE_MONO;
   if ((*encoding__ & PE_MONO) == 0 && pinfo.max_voices < 2)
@@ -288,52 +661,35 @@ static int set_playback_info (snd_pcm_t* handle__,
     pparams.format.voices = 1; /*mono*/
   else
     pparams.format.voices = 2; /*stereo*/
-#else /* ALSA_LIB < 5 */
-  if ((*encoding__ & PE_MONO) != 0 && pinfo.min_channels > 1)
-    *encoding__ &= ~PE_MONO;
-  if ((*encoding__ & PE_MONO) == 0 && pinfo.max_channels < 2)
-    *encoding__ |= PE_MONO;
-
-  if ((*encoding__ & PE_MONO) != 0)
-    pcm_format.channels = 1; /*mono*/
-  else
-    pcm_format.channels = 2; /*stereo*/
-#endif
 
   /*check format*/
   if ((*encoding__ & PE_16BIT) != 0)
     { /*16bit*/
       if ((pinfo.formats & SND_PCM_FMT_S16_LE) != 0)
        {
-#if ALSA_LIB >= 5
          pparams.format.format = SND_PCM_SFMT_S16_LE;
-#else
-         pcm_format.format = SND_PCM_SFMT_S16_LE;
-#endif
          *encoding__ |= PE_SIGNED;
        }
-#if 0
       else if ((pinfo.formats & SND_PCM_FMT_U16_LE) != 0)
        {
-         pcm_format.format = SND_PCM_SFMT_U16_LE;
+         pparams.format.format = SND_PCM_SFMT_U16_LE;
          *encoding__ &= ~PE_SIGNED;
        }
       else if ((pinfo.formats & SND_PCM_FMT_S16_BE) != 0)
        {
-         pcm_format.format = SND_PCM_SFMT_S16_BE;
+         pparams.format.format = SND_PCM_SFMT_S16_BE;
          *encoding__ |= PE_SIGNED;
        }
       else if ((pinfo.formats & SND_PCM_FMT_U16_BE) != 0)
        {
-         pcm_format.format = SND_PCM_SFMT_U16_LE;
+         pparams.format.format = SND_PCM_SFMT_U16_BE;
          *encoding__ &= ~PE_SIGNED;
        }
-#endif
       else
        {
          ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
                    "%s doesn't support 16 bit sample width",
-                   dpm.name);
+                   alsa_device_name());
          return -1;
        }
     }
@@ -341,11 +697,7 @@ static int set_playback_info (snd_pcm_t* handle__,
     { /*8bit*/
       if ((pinfo.formats & SND_PCM_FMT_U8) != 0)
        {
-#if ALSA_LIB >= 5
          pparams.format.format = SND_PCM_SFMT_U8;
-#else
-         pcm_format.format = SND_PCM_SFMT_U8;
-#endif
          *encoding__ &= ~PE_SIGNED;
        }
 #if 0
@@ -359,119 +711,47 @@ static int set_playback_info (snd_pcm_t* handle__,
        {
          ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
                    "%s doesn't support 8 bit sample width",
-                   dpm.name);
+                   alsa_device_name());
          return -1;
        }
     }
 
 
-#if ALSA_LIB < 5
-  tmp = snd_pcm_playback_format (handle__, &pcm_format);
-  if (tmp < 0)
-    {
-      error_report (tmp);
-      return -1;
-    }
-  /*check result of snd_pcm_playback_format*/
-  if ((*encoding__ & PE_16BIT) != (orig_encoding & PE_16BIT ))
-    {
-      ctl->cmsg (CMSG_WARNING, VERB_VERBOSE,
-                "Sample width adjusted to %d bits",
-                ((*encoding__ & PE_16BIT) != 0)? 16:8);
-      ret_val = 1;
-    }
-  if (((pcm_format.channels == 1)? PE_MONO:0) != (orig_encoding & PE_MONO))
-    {
-      ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Sound adjusted to %sphonic",
-               ((*encoding__ & PE_MONO) != 0)? "mono" : "stereo");
-      ret_val = 1;
-    }
-#endif
+  sample_shift = 0;
+  if (!(dpm.encoding & PE_MONO))
+    sample_shift++;
+  if (dpm.encoding & PE_16BIT)
+    sample_shift++;
 
   /* Set buffer fragment size (in extra_param[1]) */
   if (extra_param[1] != 0)
     tmp = extra_param[1];
   else
-    {
-      tmp = audio_buffer_size;
-      if (!(*encoding__ & PE_MONO))
-       tmp <<= 1;
-      if (*encoding__ & PE_16BIT)
-       tmp <<= 1;
-    }
+    tmp = audio_buffer_size << sample_shift;
 
   /* Set buffer fragments (in extra_param[0]) */
-#if ALSA_LIB >= 5
-#if ALSA_LIB >= 6
-  pparams.frag_size = tmp;
-  if (extra_param[0] == 0)
-    pparams.buffer_size = pinfo.buffer_size;
-  else
-   {
-     int frags = extra_param[0] - (extra_param[0] % pinfo.fragment_align);
-     if (frags > pinfo.max_fragment_size)
-       frags = pinfo.max_fragment_size;
-     if (frags < pinfo.min_fragment_size)
-       frags = pinfo.min_fragment_size; 
-     pparams.buffer_size = tmp * frags;
-   }
-  pparams.buf.block.frags_xrun_max = 0;
-  pparams.buf.block.frags_min = 1;
-#else
   pparams.buf.block.frag_size = tmp;
   pparams.buf.block.frags_max = (extra_param[0] == 0) ? -1 : extra_param[0];
   pparams.buf.block.frags_min = 1;
-#endif
-
   pparams.mode = SND_PCM_MODE_BLOCK;
   pparams.channel = SND_PCM_CHANNEL_PLAYBACK;
-  pparams.start_mode = SND_PCM_START_GO;
-#if ALSA_LIB >= 6
-  pparams.xrun_mode  = SND_PCM_XRUN_FLUSH;
-#else
+  pparams.start_mode = SND_PCM_START_DATA; /* .. should be START_FULL */
   pparams.stop_mode  = SND_PCM_STOP_STOP;
-#endif
   pparams.format.interleave = 1;
-
   snd_pcm_channel_flush (handle__, SND_PCM_CHANNEL_PLAYBACK);
   tmp = snd_pcm_channel_params (handle__, &pparams);
-#else
-  pparams.fragment_size  = tmp;
-  pparams.fragments_max  = (extra_param[0] == 0) ? -1 : extra_param[0];
-  pparams.fragments_room = 1;
-  tmp = snd_pcm_playback_params (handle__, &pparams);
-#endif /* ALSA_LIB >= 5 */
   if (tmp < 0)
     {
-#if ALSA_LIB >= 6
-      ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
-               "%s doesn't support buffer fragments"
-               ":request frag_size=%d, buffer_size=%d, min=%d\n",
-               dpm.name,
-               pparams.frag_size,
-               pparams.buffer_size,
-               pparams.buf.block.frags_min);
-#elif ALSA_LIB == 5
       ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
                "%s doesn't support buffer fragments"
                ":request size=%d, max=%d, min=%d\n",
-               dpm.name,
+               alsa_device_name(),
                pparams.buf.block.frag_size,
                pparams.buf.block.frags_max,
                pparams.buf.block.frags_min);
-#else
-      ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
-               "%s doesn't support buffer fragments"
-               ":request size=%d, max=%d, room=%d\n",
-               dpm.name,
-               pparams.fragment_size,
-               pparams.fragments_max,
-               pparams.fragments_room);
-#endif
-      ret_val =1;
+      return -1;
     }
 
-#if ALSA_LIB >= 5
   tmp = snd_pcm_channel_prepare (handle__, SND_PCM_CHANNEL_PLAYBACK);
   if (tmp < 0)
     {
@@ -479,9 +759,7 @@ static int set_playback_info (snd_pcm_t* handle__,
                "unable to prepare channel\n");
       return -1;
     }
-#endif
 
-#if ALSA_LIB >= 5
   memset (&psetup, 0, sizeof(psetup));
   psetup.channel = SND_PCM_CHANNEL_PLAYBACK;
   tmp = snd_pcm_channel_setup (handle__, &psetup);
@@ -495,31 +773,10 @@ static int set_playback_info (snd_pcm_t* handle__,
          dpm.rate = psetup.format.rate;
          ret_val = 1;
        }
-#if ALSA_LIB >= 6
-      frag_size = psetup.frag_size;
-      total_bytes = frag_size * psetup.frags;
-#else
       frag_size = psetup.buf.block.frag_size;
       total_bytes = frag_size * psetup.buf.block.frags;
-#endif
-      bytes_to_go = total_bytes;
     }
-#else /* ALSA_LIB < 5 */
-  if (snd_pcm_playback_status(handle__, &pstatus) == 0)
-    {
-      if (pstatus.rate != orig_rate)
-       {
-         ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
-                   "Output rate adjusted to %d Hz (requested %d Hz)",
-                   pstatus.rate, orig_rate);
-         dpm.rate = pstatus.rate;
-         ret_val = 1;
-       }
-      frag_size = pstatus.fragment_size;
-      total_bytes = pstatus.count;
-    }
-#endif
-  else
+  else
     {
       frag_size = 0;
       total_bytes = -1; /* snd_pcm_playback_status fails */
@@ -528,6 +785,7 @@ static int set_playback_info (snd_pcm_t* handle__,
   return ret_val;
 }
 
+
 static int open_output(void)
 {
   int tmp, warnings=0;
@@ -542,7 +800,7 @@ static int open_output(void)
   if (ret < 0)
     {
       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
-               dpm.name, snd_strerror (ret));
+               alsa_device_name(), snd_strerror (ret));
       return -1;
     }
 
@@ -556,11 +814,7 @@ static int open_output(void)
       return -1;
     }
 
-#if ALSA_LIB >= 5
   dpm.fd = snd_pcm_file_descriptor (handle, SND_PCM_CHANNEL_PLAYBACK);
-#else
-  dpm.fd = snd_pcm_file_descriptor (handle);
-#endif
   output_counter = 0;
   return warnings;
 }
@@ -573,11 +827,8 @@ static void close_output(void)
     return;
 
   ret = snd_pcm_close (handle);
-#if ALSA_LIB == 5 /* Maybe alsa-driver 0.5 has a bug */
-  if (ret != -EINVAL)
-#endif
-    if (ret < 0)
-      error_report (ret);
+  if (ret < 0 && ret != -EINVAL) /* Maybe alsa-driver 0.5 has a bug */
+    error_report (ret);
   handle = NULL;
 
   dpm.fd = -1;
@@ -586,9 +837,10 @@ static void close_output(void)
 static int output_data(char *buf, int32 nbytes)
 {
   int n;
-#if ALSA_LIB >= 5
   snd_pcm_channel_status_t status;
 
+  if (! handle)
+    return -1;
   n = snd_pcm_write (handle, buf, nbytes);
   if (n <= 0)
     {
@@ -597,51 +849,326 @@ static int output_data(char *buf, int32 nbytes)
       if (snd_pcm_channel_status(handle, &status) < 0)
        {
          ctl->cmsg(CMSG_WARNING, VERB_DEBUG,
-                   "%s: could not get channel status", dpm.name);
+                   "%s: could not get channel status", alsa_device_name());
          return -1;
        }
-#if ALSA_LIB >= 6
-      if (status.status == SND_PCM_STATUS_XRUN)
-#else
-      if (status.status == SND_PCM_STATUS_UNDERRUN)
-#endif
+      if (status.status == SND_PCM_STATUS_UNDERRUN || n == -EPIPE)
        {
-#if ALSA_LIB >= 6
          ctl->cmsg(CMSG_INFO, VERB_DEBUG,
-                   "%s: underrun at %d", dpm.name, status.pos_io);
-         output_counter += status.pos_io;
-#else
-         ctl->cmsg(CMSG_INFO, VERB_DEBUG,
-                   "%s: underrun at %d", dpm.name, status.scount);
+                   "%s: underrun at %d", alsa_device_name(), status.scount);
          output_counter += status.scount;
-#endif
          snd_pcm_channel_flush (handle, SND_PCM_CHANNEL_PLAYBACK);
          snd_pcm_channel_prepare (handle, SND_PCM_CHANNEL_PLAYBACK);
-         bytes_to_go = total_bytes;
          n = snd_pcm_write (handle, buf, nbytes);
        }
       if (n <= 0)
        {
          ctl->cmsg(CMSG_WARNING, VERB_DEBUG,
-                   "%s: %s", dpm.name,
+                   "%s: %s", alsa_device_name(),
                    (n < 0) ? snd_strerror(n) : "write error");
          if (n != -EPIPE)  /* buffer underrun is ignored */
            return -1;
        }
     }
+  return 0;
+}
 
-  if (bytes_to_go > 0) {
-    bytes_to_go -= nbytes;
-    if (bytes_to_go <= 0) {
-      if (snd_pcm_channel_go(handle, SND_PCM_CHANNEL_PLAYBACK) < 0) {
-       ctl->cmsg(CMSG_WARNING, VERB_DEBUG,
-                 "%s: could not start playing", dpm.name);
-       return -1;
-      }
+
+static int acntl(int request, void *arg)
+{
+  int i;
+  snd_pcm_channel_status_t pstatus;
+  memset (&pstatus, 0, sizeof (pstatus));
+  pstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
+
+  if (handle == NULL)
+    return -1;
+
+  switch (request)
+    {
+      case PM_REQ_GETFRAGSIZ:
+       if (frag_size == 0)
+         return -1;
+       *((int *)arg) = frag_size;
+       return 0;
+
+      case PM_REQ_GETQSIZ:
+       if (total_bytes == -1)
+         return -1;
+       *((int *)arg) = total_bytes;
+       return 0;
+
+      case PM_REQ_GETFILLABLE:
+       if (total_bytes == -1)
+         return -1;
+       if (snd_pcm_channel_status(handle, &pstatus) < 0)
+         return -1;
+       i = pstatus.free >> sample_shift;
+       *((int *)arg) = i;
+       return 0;
+
+      case PM_REQ_GETFILLED:
+       if (total_bytes == -1)
+         return -1;
+       if (snd_pcm_channel_status(handle, &pstatus) < 0)
+         return -1;
+       i = pstatus.count >> sample_shift;
+       *((int *)arg) = i;
+       return 0;
+
+      case PM_REQ_GETSAMPLES:
+       if (total_bytes == -1)
+         return -1;
+       if (snd_pcm_channel_status(handle, &pstatus) < 0)
+         return -1;
+       i = (output_counter + pstatus.scount) >> sample_shift;
+       *((int *)arg) = i;
+       return 0;
+
+      case PM_REQ_DISCARD:
+       if (snd_pcm_playback_drain(handle) < 0)
+         return -1;
+       if (snd_pcm_channel_prepare(handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
+         return -1;
+       output_counter = 0;
+       return 0;
+
+      case PM_REQ_FLUSH:
+       if (snd_pcm_channel_flush(handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
+         return -1;
+       if (snd_pcm_channel_prepare(handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
+         return -1;
+       output_counter = 0;
+       return 0;
     }
-  }
+    return -1;
+}
+
+/* end ALSA API 0.5.x */
+
+#elif ALSA_LIB < 5
+
+/*================================================================
+ * ALSA API version 0.4.x
+ *================================================================*/
+
+/*return value == 0 sucess
+               == 1 warning
+               == -1 fails
+ */
+static int set_playback_info (snd_pcm_t* handle__,
+                             int32* encoding__, int32* rate__,
+                             const int32 extra_param[5])
+{
+  int ret_val = 0;
+  const int32 orig_encoding = *encoding__;
+  const int32 orig_rate = *rate__;
+  int tmp;
+  snd_pcm_playback_info_t pinfo;
+  snd_pcm_format_t pcm_format;
+  struct snd_pcm_playback_params pparams;
+  struct snd_pcm_playback_status pstatus;
+  memset (&pcm_format, 0, sizeof (pcm_format));
+
+  memset (&pinfo, 0, sizeof (pinfo));
+  memset (&pparams, 0, sizeof (pparams));
+  tmp = snd_pcm_playback_info (handle__, &pinfo);
+  if (tmp < 0)
+    {
+      error_report (tmp);
+      return -1;
+    }
+
+  /*check sample bit*/
+  if ((pinfo.flags & SND_PCM_PINFO_8BITONLY) != 0)
+    *encoding__ &= ~PE_16BIT; /*force 8bit samples*/
+  if ((pinfo.flags & SND_PCM_PINFO_16BITONLY) != 0)
+    *encoding__ |= PE_16BIT; /*force 16bit samples*/
+
+  /*check rate*/
+  if (pinfo.min_rate > *rate__)
+    *rate__ = pinfo.min_rate;
+  if (pinfo.max_rate < *rate__)
+    *rate__ = pinfo.max_rate;
+  pcm_format.rate = *rate__;
+
+  /*check channels*/
+  if ((*encoding__ & PE_MONO) != 0 && pinfo.min_channels > 1)
+    *encoding__ &= ~PE_MONO;
+  if ((*encoding__ & PE_MONO) == 0 && pinfo.max_channels < 2)
+    *encoding__ |= PE_MONO;
+
+  if ((*encoding__ & PE_MONO) != 0)
+    pcm_format.channels = 1; /*mono*/
+  else
+    pcm_format.channels = 2; /*stereo*/
+
+  /*check format*/
+  if ((*encoding__ & PE_16BIT) != 0)
+    { /*16bit*/
+      if ((pinfo.formats & SND_PCM_FMT_S16_LE) != 0)
+       {
+         pcm_format.format = SND_PCM_SFMT_S16_LE;
+         *encoding__ |= PE_SIGNED;
+       }
+      else
+       {
+         ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+                   "%s doesn't support 16 bit sample width",
+                   alsa_device_name());
+         return -1;
+       }
+    }
+  else
+    { /*8bit*/
+      if ((pinfo.formats & SND_PCM_FMT_U8) != 0)
+       {
+         pcm_format.format = SND_PCM_SFMT_U8;
+         *encoding__ &= ~PE_SIGNED;
+       }
+#if 0
+      else if ((pinfo.formats & SND_PCM_FMT_S8) != 0)
+       {
+         pcm_format.format = SND_PCM_SFMT_U16_LE;
+         *encoding__ |= PE_SIGNED;
+       }
+#endif
+      else
+       {
+         ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+                   "%s doesn't support 8 bit sample width",
+                   alsa_device_name());
+         return -1;
+       }
+    }
+
+
+  tmp = snd_pcm_playback_format (handle__, &pcm_format);
+  if (tmp < 0)
+    {
+      error_report (tmp);
+      return -1;
+    }
+  /*check result of snd_pcm_playback_format*/
+  if ((*encoding__ & PE_16BIT) != (orig_encoding & PE_16BIT ))
+    {
+      ctl->cmsg (CMSG_WARNING, VERB_VERBOSE,
+                "Sample width adjusted to %d bits",
+                ((*encoding__ & PE_16BIT) != 0)? 16:8);
+      ret_val = 1;
+    }
+  if (((pcm_format.channels == 1)? PE_MONO:0) != (orig_encoding & PE_MONO))
+    {
+      ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Sound adjusted to %sphonic",
+               ((*encoding__ & PE_MONO) != 0)? "mono" : "stereo");
+      ret_val = 1;
+    }
+
+  sample_shift = 0;
+  if (!(dpm.encoding & PE_MONO))
+    sample_shift++;
+  if (dpm.encoding & PE_16BIT)
+    sample_shift++;
+
+  /* Set buffer fragment size (in extra_param[1]) */
+  if (extra_param[1] != 0)
+    tmp = extra_param[1];
+  else
+    tmp = audio_buffer_size << sample_shift;
+
+  /* Set buffer fragments (in extra_param[0]) */
+  pparams.fragment_size  = tmp;
+  pparams.fragments_max  = (extra_param[0] == 0) ? -1 : extra_param[0];
+  pparams.fragments_room = 1;
+  tmp = snd_pcm_playback_params (handle__, &pparams);
+  if (tmp < 0)
+    {
+      ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+               "%s doesn't support buffer fragments"
+               ":request size=%d, max=%d, room=%d\n",
+               alsa_device_name(),
+               pparams.fragment_size,
+               pparams.fragments_max,
+               pparams.fragments_room);
+      ret_val = 1;
+    }
+
+  if (snd_pcm_playback_status(handle__, &pstatus) == 0)
+    {
+      if (pstatus.rate != orig_rate)
+       {
+         ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+                   "Output rate adjusted to %d Hz (requested %d Hz)",
+                   pstatus.rate, orig_rate);
+         dpm.rate = pstatus.rate;
+         ret_val = 1;
+       }
+      frag_size = pstatus.fragment_size;
+      total_bytes = pstatus.count;
+    }
+  else
+    {
+      frag_size = 0;
+      total_bytes = -1; /* snd_pcm_playback_status fails */
+    }
+
+  return ret_val;
+}
+
+static int open_output(void)
+{
+  int tmp, warnings=0;
+  int ret;
+
+  tmp = check_sound_cards (&card, &device, dpm.extra_param);
+  if (tmp < 0)
+    return -1;
+
+  /* Open the audio device */
+  ret = snd_pcm_open (&handle, card, device, SND_PCM_OPEN_PLAYBACK);
+  if (ret < 0)
+    {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+               alsa_device_name(), snd_strerror (ret));
+      return -1;
+    }
+
+  /* They can't mean these */
+  dpm.encoding &= ~(PE_ULAW|PE_ALAW|PE_BYTESWAP);
+  warnings = set_playback_info (handle, &dpm.encoding, &dpm.rate,
+                               dpm.extra_param);
+  if (warnings < 0)
+    {
+      close_output ();
+      return -1;
+    }
+
+  dpm.fd = snd_pcm_file_descriptor (handle);
+  output_counter = 0;
+  return warnings;
+}
+
+static void close_output(void)
+{
+  int ret;
+
+  if (handle == NULL)
+    return;
+
+  ret = snd_pcm_close (handle);
+  if (ret < 0)
+    error_report (ret);
+  handle = NULL;
+
+  dpm.fd = -1;
+}
+
+static int output_data(char *buf, int32 nbytes)
+{
+  int n;
 
-#else /* ALSA_LIB < 5 */
+  if (! handle)
+    return -1;
   while (nbytes > 0)
     {
       n = snd_pcm_write (handle, buf, nbytes);
@@ -649,7 +1176,7 @@ static int output_data(char *buf, int32 nbytes)
       if (n < 0)
         {
          ctl->cmsg(CMSG_WARNING, VERB_DEBUG,
-                   "%s: %s", dpm.name, snd_strerror(n));
+                   "%s: %s", alsa_device_name(), snd_strerror(n));
          if (n == -EWOULDBLOCK)
            continue;
          return -1;
@@ -658,7 +1185,6 @@ static int output_data(char *buf, int32 nbytes)
       nbytes -= n;
       output_counter += n;
     }
-#endif
 
   return 0;
 }
@@ -666,13 +1192,10 @@ static int output_data(char *buf, int32 nbytes)
 static int acntl(int request, void *arg)
 {
   int i;
-#if ALSA_LIB >= 5
-  snd_pcm_channel_status_t pstatus;
-  memset (&pstatus, 0, sizeof (pstatus));
-  pstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
-#else
   struct snd_pcm_playback_status pstatus;
-#endif
+
+  if (handle == NULL)
+    return -1;
 
   switch (request)
     {
@@ -691,93 +1214,47 @@ static int acntl(int request, void *arg)
       case PM_REQ_GETFILLABLE:
        if (total_bytes == -1)
          return -1;
-#if ALSA_LIB >= 5
-       if (snd_pcm_channel_status(handle, &pstatus) < 0)
-         return -1;
-#if ALSA_LIB >= 6
-       i = pstatus.bytes_free;
-#else
-       i = pstatus.free;
-#endif
-#else /* ALSA_LIB < 5 */
        if (snd_pcm_playback_status(handle, &pstatus) < 0)
          return -1;
-       i = pstatus.count;
-#endif
-       if(!(dpm.encoding & PE_MONO)) i >>= 1;
-       if(dpm.encoding & PE_16BIT) i >>= 1;
+       i = pstatus.count >> sample_shift;
        *((int *)arg) = i;
        return 0;
 
       case PM_REQ_GETFILLED:
        if (total_bytes == -1)
          return -1;
-#if ALSA_LIB >= 5
-       if (snd_pcm_channel_status(handle, &pstatus) < 0)
-         return -1;
-#if ALSA_LIB >= 6
-       i = pstatus.bytes_used;
-#else
-       i = pstatus.count;
-#endif
-#else /* ALSA_LIB < 5 */
        if (snd_pcm_playback_status(handle, &pstatus) < 0)
          return -1;
-       i = pstatus.queue;
-#endif
-       if(!(dpm.encoding & PE_MONO)) i >>= 1;
-       if(dpm.encoding & PE_16BIT) i >>= 1;
+       i = pstatus.queue >> sample_shift;
        *((int *)arg) = i;
        return 0;
 
       case PM_REQ_GETSAMPLES:
        if (total_bytes == -1)
          return -1;
-#if ALSA_LIB >= 5
-       if (snd_pcm_channel_status(handle, &pstatus) < 0)
-         return -1;
-#if ALSA_LIB >= 6
-       i = output_counter + pstatus.pos_io;
-#else
-       i = output_counter + pstatus.scount;
-#endif
-#else
        if (snd_pcm_playback_status(handle, &pstatus) < 0)
          return -1;
        i = output_counter - pstatus.queue;
-#endif
-       if (!(dpm.encoding & PE_MONO)) i >>= 1;
-       if (dpm.encoding & PE_16BIT) i >>= 1;
+       i >>= sample_shift;
        *((int *)arg) = i;
        return 0;
 
       case PM_REQ_DISCARD:
-#if ALSA_LIB >= 5
-       if (snd_pcm_playback_drain(handle) < 0)
-         return -1;
-       if (snd_pcm_channel_prepare(handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
-         return -1;
-       bytes_to_go = total_bytes;
-#else
        if (snd_pcm_drain_playback (handle) < 0)
          return -1; /* error */
-#endif
        output_counter = 0;
        return 0;
 
       case PM_REQ_FLUSH:
-#if ALSA_LIB >= 5
-       if (snd_pcm_channel_flush(handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
-         return -1;
-       if (snd_pcm_channel_prepare(handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
-         return -1;
-       bytes_to_go = total_bytes;
-#else
        if (snd_pcm_flush_playback(handle) < 0)
          return -1; /* error */
-#endif
        output_counter = 0;
        return 0;
     }
     return -1;
 }
+
+/* end ALSA API 0.4.x */
+
+#endif
+
index 50b8af2..cb57d24 100644 (file)
@@ -28,6 +28,7 @@
 #endif /* HAVE_CONFIG_H */
 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
 
 #ifndef NO_STRING_H
 #include <string.h>
@@ -286,6 +287,32 @@ static void store_instrument_cache(Instrument *ip,
     p->ip = ip;
 }
 
+static int
+adjust_tune_rate(int val, float tune)
+{
+  if (!tune)
+    return val;
+  return (int)(val * pow(2.0, tune / 12.0));
+}
+
+static void
+apply_bank_parameter(Instrument *ip, ToneBankElement *tone)
+{
+  int i;
+
+  if (!tone->tunenum)
+    return;
+
+  for (i = 0; i < ip->samples; i++) {
+    Sample *sp;
+    sp = &ip->sample[i];
+    if (tone->tunenum == 1)
+      sp->sample_rate = adjust_tune_rate(sp->sample_rate, tone->tune[0]);
+    else if (i < tone->tunenum)
+      sp->sample_rate = adjust_tune_rate(sp->sample_rate, tone->tune[i]);
+  }
+}
+
 /*
    If panning or note_to_use != -1, it will be used for all samples,
    instead of the sample-specific values in the instrument file.
@@ -307,6 +334,7 @@ static Instrument *load_gus_instrument(char *name,
   uint8 tmp[1024];
   int i,j,noluck=0;
   int panning, amp, note_to_use, strip_loop, strip_envelope, strip_tail;
+  ToneBankElement *tone;
 
 #ifdef PATCH_EXT_LIST
   static char *patch_ext[] = PATCH_EXT_LIST;
@@ -321,7 +349,6 @@ static Instrument *load_gus_instrument(char *name,
 
   if(bank)
   {
-      ToneBankElement *tone;
       tone = &bank->tone[prog];
       panning = tone->pan;
       amp = tone->amp;
@@ -334,14 +361,17 @@ static Instrument *load_gus_instrument(char *name,
   {
       panning = amp = note_to_use = -1;
       strip_loop = strip_envelope = strip_tail = 0;
+      tone = NULL;
   }
 
-  if((ip = search_instrument_cache(name, panning, amp, note_to_use,
-                                  strip_loop, strip_envelope, strip_tail))
-     != NULL)
-  {
-      ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * Cached");
-      return ip;
+  if (tone && tone->tunenum == 0) {
+    if((ip = search_instrument_cache(name, panning, amp, note_to_use,
+                                    strip_loop, strip_envelope, strip_tail))
+       != NULL)
+      {
+       ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * Cached");
+       return ip;
+      }
   }
 
   /* Open patch file */
@@ -464,6 +494,7 @@ static Instrument *load_gus_instrument(char *name,
       READ_LONG(sp->low_freq);
       READ_LONG(sp->high_freq);
       READ_LONG(sp->root_freq);
+
       skip(tf, 2); /* Why have a "root frequency" and then "tuning"?? */
       READ_CHAR(tmp[0]);
       ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Rate/Low/Hi/Root = %d/%d/%d/%d",
@@ -789,6 +820,7 @@ Instrument *load_instrument(int dr, int b, int prog)
            free(bank->tone[i].comment);
          bank->tone[i].comment = safe_strdup(ip->instname);
        }
+       apply_bank_parameter(ip, &bank->tone[prog]);
        return ip;
     }
 
@@ -835,6 +867,7 @@ Instrument *load_instrument(int dr, int b, int prog)
        }
     }
 
+    apply_bank_parameter(ip, &bank->tone[prog]);
     return ip;
 }
 
index a22c335..47525c4 100644 (file)
@@ -77,6 +77,8 @@ typedef struct {
                    2-255: reserved
                    */
   int16 amp;
+  int tunenum;
+  float *tune;
 } ToneBankElement;
 
 /* A hack to delay instrument loading until after reading the
index 130d551..f798de6 100644 (file)
@@ -35,7 +35,7 @@ PlayMode modmidi_play_mode = {
     PF_PCM_STREAM,
     -1,
     {0,0,0,0,0},
-    "MOD -> MIDI file conversion", 'm',
+    "MOD -> MIDI file conversion", 'M',
     NULL,
     open_output,
     close_output,
index 7d72afa..0cba9b0 100644 (file)
@@ -4654,7 +4654,7 @@ static int play_midi(MidiEvent *eventlist, int32 samples)
     int rc;
     static int play_count = 0;
 
-    if (play_mode->id_character == 'm') {
+    if (play_mode->id_character == 'M') {
        int cnt;
 
        convert_mod_to_midi_file(eventlist);
index f9546ad..adb7d0b 100644 (file)
@@ -1400,14 +1400,14 @@ static void set_rootkey(SFInfo *sf, SampleList *vp, LayerTable *tbl)
 
     /* correct too high pitch */
     if(vp->root >= vp->high + 60)
-       vp->root -= 60;
+      vp->root -= 60;
 
     /* correct tune with the sustain level of modulation envelope */
     vp->tune += ((int)tbl->val[SF_env1ToPitch] * (1000 - (int)tbl->val[SF_sustainEnv1])) / 1000;
 
     /* correct tune */
-    vp->tune += (int)tbl->val[SF_lfo1ToPitch]; 
-    vp->tune += (int)tbl->val[SF_lfo2ToPitch]; 
+    vp->tune += (int)tbl->val[SF_lfo1ToPitch];
+    vp->tune += (int)tbl->val[SF_lfo2ToPitch];
 }
 
 static void set_rootfreq(SampleList *vp)
@@ -1417,6 +1417,7 @@ static void set_rootfreq(SampleList *vp)
     root = vp->root;
     tune = vp->tune;
 
+#if 1
     while(tune <= -100)
     {
        root++;
@@ -1427,9 +1428,22 @@ static void set_rootfreq(SampleList *vp)
        root--;
        tune -= 100;
     }
-
     /* -100 < tune <= 0 */
+
     tune = (-tune * 256) / 100;
+#else
+
+    while (tune < 0) {
+      root--;
+      tune += 100;
+    }
+    while (tune >= 100) {
+      root++;
+      tune -= 100;
+    }
+    tune = (tune * 256) / 100;
+
+#endif
 
     if(root > 127)
        vp->v.root_freq = (int32)((FLOAT_T)freq_table[127] *
index 4f2d235..5463e09 100644 (file)
@@ -894,6 +894,37 @@ static void copybank(ToneBank *to, ToneBank *from)
     }
 }
 
+static float *
+config_parse_tune(char *cp, int *num)
+{
+  char *p;
+  float *tune_list;
+  int i;
+
+  /* count num */
+  p = cp;
+  *num = 1;
+  while ((p = strchr(p, ',')) != NULL) {
+    p++;
+    (*num)++;
+  }
+
+  /* alloc */
+  tune_list = (float *)malloc((*num) * sizeof(float));
+
+  /* regist */
+  i = 0;
+  p = cp;
+  tune_list[i] = atof(p);
+  while ((p = strchr(p, ',')) != NULL) {
+    p++;
+    i++;
+    tune_list[i] = atof(p);
+  }
+
+  return tune_list;
+}
+
 static int set_gus_patchconf_opts(char *name, int line, char *opts,
                                  ToneBankElement *tone)
 {
@@ -997,6 +1028,9 @@ static int set_gus_patchconf_opts(char *name, int line, char *opts,
            p++;
        }
     }
+    else if(!strcmp(opts, "tune")) {
+      tone->tune = config_parse_tune(cp, &tone->tunenum);
+    }
     else
     {
        ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
@@ -2099,6 +2133,8 @@ MAIN_INTERFACE void tmdy_free_config(void)
        free(elm->name);
       if (elm->comment)
        free(elm->comment);
+      if (elm->tune)
+       free(elm->tune);
     }
     if (i > 0) {
       free(bank);