changeset 1414:5f892afeb8e1 audacious-plugins-1.4.0-dr2

- amidi-plug 0.8 beta1; support for gthread and v3 plugin system; needs testing and refinements
author Giacomo Lozito <james@develia.org>
date Mon, 06 Aug 2007 14:01:24 +0200
parents 4731d28ea19d
children 9dbea7b32361
files src/amidi-plug/amidi-plug.c src/amidi-plug/amidi-plug.h src/amidi-plug/backend-alsa/b-alsa.c src/amidi-plug/backend-dummy/b-dummy.c src/amidi-plug/backend-fluidsynth/b-fluidsynth.c src/amidi-plug/i_backend.c src/amidi-plug/i_backend.h src/amidi-plug/i_common.h src/amidi-plug/i_midi.c src/amidi-plug/i_midi.h
diffstat 10 files changed, 236 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/src/amidi-plug/amidi-plug.c	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/amidi-plug.c	Mon Aug 06 14:01:24 2007 +0200
@@ -1,6 +1,6 @@
 /*
 *
-* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2006
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
@@ -88,6 +88,12 @@
 static void amidiplug_init( void )
 {
   g_log_set_handler(NULL , G_LOG_LEVEL_WARNING , g_log_default_handler , NULL);
+  
+  amidiplug_gettime_mutex = g_mutex_new();
+  amidiplug_playing_mutex = g_mutex_new();
+  amidiplug_pause_cond = g_cond_new();
+  amidiplug_seekonpause_cond = g_cond_new();
+  
   DEBUGMSG( "init, read configuration\n" );
   /* read configuration for amidi-plug */
   i_configure_cfg_ap_read();
@@ -101,6 +107,13 @@
 static void amidiplug_cleanup( void )
 {
   i_backend_unload(); /* unload currently loaded backend */
+  
+  g_mutex_free(amidiplug_gettime_mutex);
+  g_mutex_free(amidiplug_playing_mutex);
+  g_cond_free(amidiplug_pause_cond);
+  g_cond_free(amidiplug_seekonpause_cond);
+  amidiplug_pause_cond = NULL;
+  amidiplug_seekonpause_cond = NULL;
 }
 
 
@@ -126,29 +139,38 @@
 
 static void amidiplug_stop( InputPlayback * playback )
 {
+  gboolean is_error = FALSE;
   DEBUGMSG( "STOP request at tick: %i\n" , midifile.playing_tick );
-  pthread_mutex_lock( &amidiplug_playing_mutex );
-  if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) ||
-      ( amidiplug_playing_status == AMIDIPLUG_STOP ))
+
+  g_mutex_lock( amidiplug_playing_mutex );
+  if ( amidiplug_playing_status == AMIDIPLUG_ERR )
+    is_error = TRUE;
+  g_mutex_unlock( amidiplug_playing_mutex );
+  
+  if ( !is_error )
   {
-    amidiplug_playing_status = AMIDIPLUG_STOP;
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
-    pthread_join( amidiplug_play_thread , NULL );
-    if ( backend.autonomous_audio == FALSE )
-      pthread_join( amidiplug_audio_thread , NULL );
-    DEBUGMSG( "STOP activated (play thread joined)\n" );
+    /* if stop wasn't called due to error condition,
+       wait until the play thread reach a 'safe state'
+       (that is, when amidiplug_play_thread exists) */
+    while ( !amidiplug_play_thread )
+      g_usleep( 20000 );
   }
-  else if ( amidiplug_playing_status == AMIDIPLUG_PAUSE )
+
+  g_mutex_lock( amidiplug_playing_mutex );
+  amidiplug_playing_status = AMIDIPLUG_STOP;
+  g_cond_signal( amidiplug_pause_cond );
+  g_mutex_unlock( amidiplug_playing_mutex );
+  if ( amidiplug_play_thread )
   {
-    amidiplug_playing_status = AMIDIPLUG_STOP;
-    DEBUGMSG( "STOP activated (from PAUSE to STOP)\n" );
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
+    g_thread_join( amidiplug_play_thread );
+    amidiplug_play_thread = NULL;
   }
-  else /* AMIDIPLUG_ERR */
+  if (( backend.autonomous_audio == FALSE ) && ( amidiplug_audio_thread ))
   {
-    DEBUGMSG( "STOP activated (in error handling, ok)\n" );
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
+    g_thread_join( amidiplug_audio_thread );
+    amidiplug_audio_thread = NULL;
   }
+  DEBUGMSG( "STOP activated (play thread joined)\n" );
 
   /* kill the sequencer (while it may have been already killed if coming
      from pause, it's safe to do anyway since it checks for multiple calls) */
@@ -176,84 +198,49 @@
 {
   if ( paused )
   {
+    g_mutex_lock( amidiplug_playing_mutex );
+    if ( amidiplug_playing_status == AMIDIPLUG_SEEK )
+    {
+      DEBUGMSG( "handle SEEK ON PAUSE situation\n" , midifile.playing_tick );
+      /* uh oh, we have a pending seek; this can happen when a seek
+         is requested while playback is paused; wait for that seek to
+         be completed before pausing the song again */
+      while ( amidiplug_playing_status != AMIDIPLUG_PLAY )
+        g_cond_wait( amidiplug_seekonpause_cond , amidiplug_playing_mutex );
+    }
+    g_mutex_unlock( amidiplug_playing_mutex );
     DEBUGMSG( "PAUSE request at tick: %i\n" , midifile.playing_tick );
-    pthread_mutex_lock( &amidiplug_playing_mutex );
-    /* this cond is used to avoid race conditions */
-    while ( amidiplug_playing_status != AMIDIPLUG_PLAY )
-      pthread_cond_wait( &amidiplug_playing_cond , &amidiplug_playing_mutex );
+    g_mutex_lock( amidiplug_playing_mutex );
     amidiplug_playing_status = AMIDIPLUG_PAUSE;
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
-
-    pthread_join( amidiplug_play_thread , NULL );
-    if ( backend.autonomous_audio == FALSE )
-      pthread_join( amidiplug_audio_thread , NULL );
-    DEBUGMSG( "PAUSE activated (play thread joined)\n" , midifile.playing_tick );
-
+    g_mutex_unlock( amidiplug_playing_mutex );
     if ( backend.autonomous_audio == FALSE )
       playback->output->pause(paused);
-
-    /* kill the sequencer */
-    backend.seq_off();
   }
   else
   {
-    DEBUGMSG( "PAUSE deactivated, returning to tick %i\n" , midifile.playing_tick );
-    /* revive the sequencer */
-    backend.seq_on();
-    /* re-set initial tempo */
-    i_midi_setget_tempo( &midifile );
-    backend.seq_queue_tempo( midifile.current_tempo , midifile.ppq );
-    /* get back to the previous state */
-    amidiplug_skipto( midifile.playing_tick );
-
+    DEBUGMSG( "PAUSE deactivated, resume playing from tick %i\n" , midifile.playing_tick );
     if ( backend.autonomous_audio == FALSE )
       playback->output->pause(paused);
-
-    pthread_mutex_lock( &amidiplug_playing_mutex );
-    /* play play play! */
-    DEBUGMSG( "PAUSE deactivated, starting play thread again\n" );
-    pthread_create(&amidiplug_play_thread, NULL, amidiplug_play_loop, playback);
-    /* this cond is used to avoid race conditions */
-    while ( amidiplug_playing_status != AMIDIPLUG_PLAY )
-      pthread_cond_wait( &amidiplug_playing_cond , &amidiplug_playing_mutex );
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
+    g_mutex_lock( amidiplug_playing_mutex );
+    amidiplug_playing_status = AMIDIPLUG_PLAY;
+    g_cond_signal( amidiplug_pause_cond );
+    /* wait for unpause operation to be completed */
+    g_cond_wait( amidiplug_pause_cond , amidiplug_playing_mutex );
+    g_mutex_unlock( amidiplug_playing_mutex );
   }
 }
 
 
 static void amidiplug_seek( InputPlayback * playback, gint time )
 {
-  DEBUGMSG( "SEEK requested (time %i), pausing song...\n" , time );
-  pthread_mutex_lock( &amidiplug_playing_mutex );
-  /* this cond is used to avoid race conditions */
-  while ( amidiplug_playing_status != AMIDIPLUG_PLAY )
-    pthread_cond_wait( &amidiplug_playing_cond , &amidiplug_playing_mutex );
-  amidiplug_playing_status = AMIDIPLUG_PAUSE;
-  pthread_mutex_unlock( &amidiplug_playing_mutex );
-
-  pthread_join( amidiplug_play_thread , NULL );
-  if ( backend.autonomous_audio == FALSE )
-    pthread_join( amidiplug_audio_thread , NULL );
-  DEBUGMSG( "SEEK requested (time %i), song paused\n" , time );
-  /* kill the sequencer */
-  backend.seq_off();
-  /* revive the sequencer */
-  backend.seq_on();
-  /* re-set initial tempo */
-  i_midi_setget_tempo( &midifile );
-  backend.seq_queue_tempo( midifile.current_tempo , midifile.ppq );
-  /* get back to the previous state */
-  DEBUGMSG( "SEEK requested (time %i), moving to tick %i of %i\n" ,
-            time , (gint)((time * 1000000) / midifile.avg_microsec_per_tick) , midifile.max_tick );
-  midifile.playing_tick = (gint)((time * 1000000) / midifile.avg_microsec_per_tick);
-  amidiplug_skipto( midifile.playing_tick );
+  DEBUGMSG( "SEEK requested (time %i)...\n" , time );
+  g_mutex_lock( amidiplug_playing_mutex );
+  midifile.seeking_tick = (gint)((time * 1000000) / midifile.avg_microsec_per_tick);
+  amidiplug_playing_status = AMIDIPLUG_SEEK;
+  g_mutex_unlock( amidiplug_playing_mutex );
 
   if ( backend.autonomous_audio == FALSE )
     playback->output->flush(time * 1000);
-
-  /* play play play! */
-  DEBUGMSG( "SEEK done, starting play thread again\n" );
-  pthread_create(&amidiplug_play_thread, NULL, amidiplug_play_loop, playback);
 }
 
 
@@ -261,23 +248,24 @@
 {
   if ( backend.autonomous_audio == FALSE )
   {
-    pthread_mutex_lock( &amidiplug_playing_mutex );
+    g_mutex_lock( amidiplug_playing_mutex );
     if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) ||
         ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) ||
+        ( amidiplug_playing_status == AMIDIPLUG_SEEK ) ||
         (( amidiplug_playing_status == AMIDIPLUG_STOP ) && ( playback->output->buffer_playing() )))
     {
-      pthread_mutex_unlock( &amidiplug_playing_mutex );
+      g_mutex_unlock( amidiplug_playing_mutex );
       return playback->output->output_time();
     }
     else if ( amidiplug_playing_status == AMIDIPLUG_STOP )
     {
-      pthread_mutex_unlock( &amidiplug_playing_mutex );
+      g_mutex_unlock( amidiplug_playing_mutex );
       DEBUGMSG( "GETTIME on stopped song, returning -1\n" , time );
       return -1;
     }
     else /* AMIDIPLUG_ERR */
     {
-      pthread_mutex_unlock( &amidiplug_playing_mutex );
+      g_mutex_unlock( amidiplug_playing_mutex );
       DEBUGMSG( "GETTIME on halted song (an error occurred?), returning -1 and stopping the player\n" );
       audacious_drct_stop();
       return -1;
@@ -286,25 +274,26 @@
   else
   {
     gint pt;
-    pthread_mutex_lock( &amidiplug_playing_mutex );
+    g_mutex_lock( amidiplug_playing_mutex );
     if (( amidiplug_playing_status == AMIDIPLUG_PLAY ) ||
-        ( amidiplug_playing_status == AMIDIPLUG_PAUSE ))
+        ( amidiplug_playing_status == AMIDIPLUG_PAUSE ) ||
+        ( amidiplug_playing_status == AMIDIPLUG_SEEK ))
     {
-      pthread_mutex_unlock( &amidiplug_playing_mutex );
-      pthread_mutex_lock(&amidiplug_gettime_mutex);
+      g_mutex_unlock( amidiplug_playing_mutex );
+      g_mutex_lock( amidiplug_gettime_mutex );
       pt = midifile.playing_tick;
-      pthread_mutex_unlock(&amidiplug_gettime_mutex);
+      g_mutex_unlock( amidiplug_gettime_mutex );
       return (gint)((pt * midifile.avg_microsec_per_tick) / 1000);
     }
     else if ( amidiplug_playing_status == AMIDIPLUG_STOP )
     {
-      pthread_mutex_unlock( &amidiplug_playing_mutex );
+      g_mutex_unlock( amidiplug_playing_mutex );
       DEBUGMSG( "GETTIME on stopped song, returning -1\n" , time );
       return -1;
     }
     else /* AMIDIPLUG_ERR */
     {
-      pthread_mutex_unlock( &amidiplug_playing_mutex );
+      g_mutex_unlock( amidiplug_playing_mutex );
       DEBUGMSG( "GETTIME on halted song (an error occurred?), returning -1 and stopping the player\n" , time );
       audacious_drct_stop();
       return -1;
@@ -475,10 +464,17 @@
                              au_samplerate , au_channels );
       g_free( filename );
 
+      /* done with file */
+      VFS_FCLOSE( midifile.file_pointer );
+      midifile.file_pointer = NULL;
+
       /* play play play! */
       DEBUGMSG( "PLAY requested, starting play thread\n" );
+      g_mutex_lock( amidiplug_playing_mutex );
       amidiplug_playing_status = AMIDIPLUG_PLAY;
-      pthread_create(&amidiplug_play_thread, NULL, amidiplug_play_loop, playback);
+      g_mutex_unlock( amidiplug_playing_mutex );
+      amidiplug_play_thread = g_thread_self();
+      amidiplug_play_loop(playback);
       break;
     }
 
@@ -490,44 +486,34 @@
     }
   }
 
-  VFS_FCLOSE( midifile.file_pointer );
+  if ( midifile.file_pointer )
+  {
+    /* done with file */
+    VFS_FCLOSE( midifile.file_pointer );
+    midifile.file_pointer = NULL;
+  }
 }
 
 
 
-void * amidiplug_play_loop( void * arg )
+gpointer amidiplug_play_loop( gpointer arg )
 {
   InputPlayback *playback = arg;
-  gint i = 0;
-  gboolean rewind = FALSE;
-
-  pthread_mutex_lock( &amidiplug_playing_mutex );
-  if ( amidiplug_playing_status != AMIDIPLUG_PAUSE )
-  {
-    DEBUGMSG( "PLAY thread, rewind tracks to their first event\n" );
-    rewind = TRUE;
-  }
-  else
-  {
-    DEBUGMSG( "PLAY thread, do not rewind tracks to their first event (coming from a PAUSE status)\n" );
-    amidiplug_playing_status = AMIDIPLUG_PLAY;
-    pthread_cond_signal( &amidiplug_playing_cond );
-  }
-  pthread_mutex_unlock( &amidiplug_playing_mutex );
+  gint j = 0;
+  gboolean rewind = TRUE;
 
   if ( rewind )
   {
     /* initialize current position in each track */
-    for (i = 0; i < midifile.num_tracks; ++i)
-      midifile.tracks[i].current_event = midifile.tracks[i].first_event;
-    backend.seq_queue_start();
+    for (j = 0; j < midifile.num_tracks; ++j)
+      midifile.tracks[j].current_event = midifile.tracks[j].first_event;
   }
 
   if ( backend.autonomous_audio == FALSE )
-  {
-    pthread_create(&amidiplug_audio_thread, NULL, amidiplug_audio_loop, playback);
-  }
+    amidiplug_audio_thread = g_thread_create(amidiplug_audio_loop, playback, TRUE, NULL);
 
+  /* queue start */
+  backend.seq_queue_start();
   /* common settings for all our events */
   backend.seq_event_init();
 
@@ -537,6 +523,46 @@
     midievent_t * event = NULL;
     midifile_track_t * event_track = NULL;
     gint i, min_tick = midifile.max_tick + 1;
+    
+    /* check if the song has been paused/seeked/stopped */
+    g_mutex_lock( amidiplug_playing_mutex );
+    if ( amidiplug_playing_status != AMIDIPLUG_PLAY )
+    {
+      DEBUGMSG( "PLAY thread, PAUSE/SEEK/STOP requested, handle in play loop\n" );
+      if ( amidiplug_playing_status == AMIDIPLUG_PAUSE )
+      {
+        DEBUGMSG( "PLAY thread, PAUSE requested, shut notes and make the play loop wait\n" );
+        backend.seq_event_allnoteoff( midifile.playing_tick );
+        backend.seq_queue_stop();
+        while (( amidiplug_playing_status != AMIDIPLUG_PLAY ) &&
+                ( amidiplug_playing_status != AMIDIPLUG_STOP ))
+          g_cond_wait( amidiplug_pause_cond , amidiplug_playing_mutex );
+        DEBUGMSG( "PLAY thread, UNPAUSE requested, resume playing\n" );
+        g_mutex_lock( amidiplug_gettime_mutex );
+        midifile.skip_offset = midifile.playing_tick;
+        g_mutex_unlock( amidiplug_gettime_mutex );
+        if ( backend.autonomous_audio == FALSE )
+          amidiplug_audio_thread = g_thread_create(amidiplug_audio_loop, playback, TRUE, NULL);
+        backend.seq_queue_start();
+        g_cond_signal( amidiplug_pause_cond );
+      }
+      if ( amidiplug_playing_status == AMIDIPLUG_SEEK )
+      {
+        backend.seq_event_allnoteoff( midifile.playing_tick );
+        backend.seq_queue_stop();
+        amidiplug_skipto( midifile.seeking_tick );
+        midifile.seeking_tick = -1;
+        amidiplug_playing_status = AMIDIPLUG_PLAY;
+        g_cond_signal( amidiplug_seekonpause_cond );
+      }
+      if ( amidiplug_playing_status == AMIDIPLUG_STOP )
+      {
+        DEBUGMSG( "PLAY thread, STOP requested, stopping...\n" );
+        g_mutex_unlock( amidiplug_playing_mutex );  
+        break; /* exit from the for (;;) loop */
+      }
+    }
+    g_mutex_unlock( amidiplug_playing_mutex );    
 
     /* search next event */
     for (i = 0; i < midifile.num_tracks; ++i)
@@ -551,15 +577,6 @@
       }
     }
 
-    /* check if the song has been stopped */
-    pthread_mutex_lock( &amidiplug_playing_mutex );
-    if ( amidiplug_playing_status != AMIDIPLUG_PLAY )
-    {
-      DEBUGMSG( "PLAY thread, PAUSE or STOP requested, exiting from play loop\n" );
-      event = NULL;
-    }
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
-
     if (!event)
       break; /* end of song reached */
 
@@ -599,9 +616,9 @@
         backend.seq_event_tempo( event );
         DEBUGMSG( "PLAY thread, processing tempo event with value %i on tick %i\n" ,
                   event->data.tempo , event->tick );
-        pthread_mutex_lock(&amidiplug_gettime_mutex);
+        g_mutex_lock( amidiplug_gettime_mutex );
         midifile.current_tempo = event->data.tempo;
-        pthread_mutex_unlock(&amidiplug_gettime_mutex);
+        g_mutex_unlock( amidiplug_gettime_mutex );
         break;
       case SND_SEQ_EVENT_META_TEXT:
         /* do nothing */
@@ -614,9 +631,9 @@
         break;
     }
 
-    pthread_mutex_lock(&amidiplug_gettime_mutex);
+    g_mutex_lock( amidiplug_gettime_mutex );
     midifile.playing_tick = event->tick;
-    pthread_mutex_unlock(&amidiplug_gettime_mutex);
+    g_mutex_unlock( amidiplug_gettime_mutex );
 
     if ( backend.autonomous_audio == TRUE )
     {
@@ -627,15 +644,14 @@
 
   backend.seq_output_shut( midifile.max_tick , midifile.skip_offset );
 
-  pthread_mutex_lock( &amidiplug_playing_mutex );
+  g_mutex_lock( amidiplug_playing_mutex );
   if ( amidiplug_playing_status != AMIDIPLUG_PAUSE )
   {
     amidiplug_playing_status = AMIDIPLUG_STOP;
     DEBUGMSG( "PLAY thread, song stopped/ended\n" );
   }
-  pthread_mutex_unlock( &amidiplug_playing_mutex );
-
-  pthread_exit(NULL);
+  g_mutex_unlock( amidiplug_playing_mutex );
+  return NULL;
 }
 
 
@@ -724,9 +740,9 @@
         break;
       case SND_SEQ_EVENT_TEMPO:
         backend.seq_event_tempo( event );
-        pthread_mutex_lock(&amidiplug_gettime_mutex);
+        g_mutex_lock( amidiplug_gettime_mutex );
         midifile.current_tempo = event->data.tempo;
-        pthread_mutex_unlock(&amidiplug_gettime_mutex);
+        g_mutex_unlock( amidiplug_gettime_mutex );
         break;
     }
 
@@ -743,7 +759,7 @@
 }
 
 
-void * amidiplug_audio_loop( void * arg )
+gpointer amidiplug_audio_loop( gpointer arg )
 {
   InputPlayback *playback = arg;
   gboolean going = 1;
@@ -758,12 +774,13 @@
       produce_audio( playback->output->written_time() ,
                      FMT_S16_NE , 2 , buffer_size , buffer , &going );
     }
-    pthread_mutex_lock( &amidiplug_playing_mutex );
-    if ( amidiplug_playing_status != AMIDIPLUG_PLAY )
+    g_mutex_lock( amidiplug_playing_mutex );
+    if (( amidiplug_playing_status != AMIDIPLUG_PLAY ) &&
+        ( amidiplug_playing_status != AMIDIPLUG_SEEK ))
       going = FALSE;
-    pthread_mutex_unlock( &amidiplug_playing_mutex );
+    g_mutex_unlock( amidiplug_playing_mutex );
   }
   if ( buffer != NULL )
     g_free( buffer );
-  pthread_exit(NULL);
+  return NULL;
 }
--- a/src/amidi-plug/amidi-plug.h	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/amidi-plug.h	Mon Aug 06 14:01:24 2007 +0200
@@ -24,7 +24,8 @@
 #define AMIDIPLUG_STOP	0
 #define AMIDIPLUG_PLAY	1
 #define AMIDIPLUG_PAUSE	2
-#define AMIDIPLUG_ERR	3
+#define AMIDIPLUG_SEEK  3
+#define AMIDIPLUG_ERR	4
 
 #include "i_common.h"
 #include <audacious/plugin.h>
@@ -39,11 +40,12 @@
 #include "i_utils.h"
 
 
-static pthread_t amidiplug_play_thread;
-static pthread_t amidiplug_audio_thread;
-static pthread_mutex_t amidiplug_gettime_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t amidiplug_playing_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t amidiplug_playing_cond = PTHREAD_COND_INITIALIZER;
+static GThread * amidiplug_play_thread = NULL;
+static GThread * amidiplug_audio_thread = NULL;
+static GMutex * amidiplug_gettime_mutex = NULL;
+static GMutex * amidiplug_playing_mutex = NULL;
+static GCond * amidiplug_pause_cond = NULL;
+static GCond * amidiplug_seekonpause_cond = NULL;
 
 gint amidiplug_playing_status = AMIDIPLUG_STOP;
 
@@ -63,8 +65,8 @@
 
 gchar *amidiplug_vfs_extensions[] = { "mid" , "midi" , "rmi" , "rmid" , NULL };
 
-void * amidiplug_play_loop( void * );
-void * amidiplug_audio_loop( void * );
+gpointer amidiplug_play_loop( gpointer );
+gpointer amidiplug_audio_loop( gpointer );
 void amidiplug_skipto( gint );
 static void amidiplug_init( void );
 static void amidiplug_cleanup( void );
--- a/src/amidi-plug/backend-alsa/b-alsa.c	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/backend-alsa/b-alsa.c	Mon Aug 06 14:01:24 2007 +0200
@@ -179,6 +179,12 @@
 }
 
 
+gint sequencer_queue_stop( void )
+{
+  return snd_seq_stop_queue( sc.seq , sc.queue , NULL );
+}
+
+
 gint sequencer_event_init( void )
 {
   /* common settings for all our events */
@@ -291,6 +297,31 @@
 }
 
 
+gint sequencer_event_allnoteoff( gint unused )
+{
+  gint i = 0 , c = 0;
+  /* send "ALL SOUNDS OFF" to all channels on all ports */
+  sc.ev.type = SND_SEQ_EVENT_CONTROLLER;
+  sc.ev.time.tick = 0;
+  snd_seq_ev_set_fixed(&sc.ev);
+  sc.ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
+  sc.ev.data.control.value = 0;
+  for ( i = 0 ; i < sc.dest_port_num ; i++ )
+  {
+    sc.ev.queue = sc.queue;
+    sc.ev.dest = sc.dest_port[i];
+
+    for ( c = 0 ; c < 16 ; c++ )
+    {
+      sc.ev.data.control.channel = c;
+      snd_seq_event_output(sc.seq, &sc.ev);
+      snd_seq_drain_output(sc.seq);
+    }
+  }
+  return 1;
+}
+
+
 gint sequencer_output( gpointer * buffer , gint * len )
 {
   snd_seq_event_output( sc.seq , &sc.ev );
--- a/src/amidi-plug/backend-dummy/b-dummy.c	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/backend-dummy/b-dummy.c	Mon Aug 06 14:01:24 2007 +0200
@@ -178,6 +178,12 @@
 }
 
 
+gint sequencer_queue_stop( void )
+{
+  return 1;
+}
+
+
 gint sequencer_event_init( void )
 {
   /* common settings for all our events */
@@ -277,6 +283,12 @@
 }
 
 
+gint sequencer_event_allnoteoff( gint unused )
+{
+  return 1;
+}
+
+
 gint sequencer_output( gpointer * buffer , gint * len )
 {
   return 0;
--- a/src/amidi-plug/backend-fluidsynth/b-fluidsynth.c	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/backend-fluidsynth/b-fluidsynth.c	Mon Aug 06 14:01:24 2007 +0200
@@ -163,12 +163,21 @@
 
 gint sequencer_queue_start( void )
 {
+  sc.last_sample_time = 0;
   g_timer_start( sc.timer_seq ); /* reset the sequencer timer */
   g_timer_start( sc.timer_sample ); /* reset the sampler timer */
   return 1;
 }
 
 
+gint sequencer_queue_stop( void )
+{
+  g_timer_stop( sc.timer_seq );
+  g_timer_stop( sc.timer_sample );
+  return 1;
+}
+
+
 gint sequencer_event_init( void )
 {
   /* common settings for all our events */
@@ -274,6 +283,17 @@
 }
 
 
+gint sequencer_event_allnoteoff( gint unused )
+{
+  gint c = 0;
+  for ( c = 0 ; c < 16 ; c++ )
+  {
+    fluid_synth_all_notes_off( sc.synth , c );
+  }
+  return 1;
+}
+
+
 gint sequencer_output( gpointer * buffer , gint * len )
 {
   gdouble current_time = g_timer_elapsed( sc.timer_sample , NULL );
--- a/src/amidi-plug/i_backend.c	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/i_backend.c	Mon Aug 06 14:01:24 2007 +0200
@@ -122,9 +122,11 @@
     g_module_symbol( backend.gmodule , "sequencer_off" , (gpointer *)&backend.seq_off );
     g_module_symbol( backend.gmodule , "sequencer_queue_tempo" , (gpointer *)&backend.seq_queue_tempo );
     g_module_symbol( backend.gmodule , "sequencer_queue_start" , (gpointer *)&backend.seq_queue_start );
+    g_module_symbol( backend.gmodule , "sequencer_queue_stop" , (gpointer *)&backend.seq_queue_stop );
     g_module_symbol( backend.gmodule , "sequencer_event_init" , (gpointer *)&backend.seq_event_init );
     g_module_symbol( backend.gmodule , "sequencer_event_noteon" , (gpointer *)&backend.seq_event_noteon );
     g_module_symbol( backend.gmodule , "sequencer_event_noteoff" , (gpointer *)&backend.seq_event_noteoff );
+    g_module_symbol( backend.gmodule , "sequencer_event_allnoteoff" , (gpointer *)&backend.seq_event_allnoteoff );
     g_module_symbol( backend.gmodule , "sequencer_event_keypress" , (gpointer *)&backend.seq_event_keypress );
     g_module_symbol( backend.gmodule , "sequencer_event_controller" , (gpointer *)&backend.seq_event_controller );
     g_module_symbol( backend.gmodule , "sequencer_event_pgmchange" , (gpointer *)&backend.seq_event_pgmchange );
--- a/src/amidi-plug/i_backend.h	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/i_backend.h	Mon Aug 06 14:01:24 2007 +0200
@@ -54,9 +54,11 @@
   gint (*seq_off)( void );
   gint (*seq_queue_tempo)( gint , gint );
   gint (*seq_queue_start)( void );
+  gint (*seq_queue_stop)( void );
   gint (*seq_event_init)( void );
   gint (*seq_event_noteon)( midievent_t * );
   gint (*seq_event_noteoff)( midievent_t * );
+  gint (*seq_event_allnoteoff)( gint );
   gint (*seq_event_keypress)( midievent_t * );
   gint (*seq_event_controller)( midievent_t * );
   gint (*seq_event_pgmchange)( midievent_t * );
--- a/src/amidi-plug/i_common.h	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/i_common.h	Mon Aug 06 14:01:24 2007 +0200
@@ -30,6 +30,8 @@
 
 #include <audacious/i18n.h>
 
+#define DEBUG 1
+
 #define textdomain(Domain)
 #define bindtextdomain(Package, Directory)
 
@@ -43,7 +45,7 @@
 #endif /* DEBUG */
 
 
-#define AMIDIPLUG_VERSION "0.7p1"
+#define AMIDIPLUG_VERSION "0.8b1"
 #define PLAYER_NAME "Audacious"
 #define PLAYER_LOCALRCDIR ".audacious"
 #define G_PATH_GET_BASENAME(x) g_path_get_basename(x)
--- a/src/amidi-plug/i_midi.c	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/i_midi.c	Mon Aug 06 14:01:24 2007 +0200
@@ -509,6 +509,7 @@
   mf->ppq = 0;
   mf->current_tempo = 0;
   mf->playing_tick = 0;
+  mf->seeking_tick = -1;
   mf->avg_microsec_per_tick = 0;
   mf->length = 0;
   mf->skip_offset = 0;
--- a/src/amidi-plug/i_midi.h	Mon Aug 06 03:20:01 2007 +0200
+++ b/src/amidi-plug/i_midi.h	Mon Aug 06 14:01:24 2007 +0200
@@ -251,6 +251,7 @@
   gint current_tempo;
 
   gint playing_tick;
+  gint seeking_tick;
   gint avg_microsec_per_tick;
   gint length;