Merge scratch fixes branch
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 28 Nov 2009 11:11:17 +0000 (11:11 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 28 Nov 2009 11:11:17 +0000 (11:11 +0000)
CHANGES.html
lib/trackname.c
server/disorder-server.h
server/play.c
server/queue-ops.c

index b9ed4e3..6ad6c5e 100644 (file)
@@ -75,7 +75,8 @@ span.command {
       <p>Gapless play should be more reliable, and playback latency over RTP
       should be a bit lower.  Note though that all the sound output code has
       been reorganized and in some cases completely rewritten, so it's possible
-      that bugs may have been (re-)introduced.</p>
+      that bugs may have been (re-)introduced.  Decoding of scratches is also
+      initiated ahead of time, giving more reliable playback.</p>
       
       <p>The <tt>command</tt> backend now (optionally) sends silence instead
       of suspending writes when a pause occurs or no track is playing.</p>
@@ -217,6 +218,12 @@ span.command {
           <td>Disobedience's 'When' column gets out of date</td>
         </tr>
 
+        <tr>
+          <td>(none)</td>
+         <td>&ldquo;found track in no collection&rdquo; messages for scratches
+         are now suppressed</td>
+        </tr>
+
       </table>
     </div>
   </div>
index 4e2e06e..aa11e2e 100644 (file)
@@ -51,6 +51,10 @@ const char *find_track_root(const char *track) {
   const struct collection *c = find_track_collection(track);
   if(c)
     return c->root;
+  /* Suppress this message for scratches */
+  for(int n = 0; n < config->scratch.n; ++n)
+    if(!strcmp(track, config->scratch.s[n]))
+      return 0;
   disorder_error(0, "found track in no collection '%s'", track);
   return 0;
 }
index f980b6f..e8f4dab 100644 (file)
@@ -140,6 +140,7 @@ struct queue_entry *queue_add(const char *track, const char *submitter,
 #define WHERE_END 1                    /* Add to end of queue */
 #define WHERE_BEFORE_RANDOM 2          /* End, or before random track */
 #define WHERE_AFTER 3                   /* After the target */
+#define WHERE_NOWHERE 4                 /* Don't add to queue at all */
 /* add an entry to the queue.  Return a pointer to the new entry. */
 
 void queue_remove(struct queue_entry *q, const char *who);
index f93fd5a..aa5b8c7 100644 (file)
@@ -39,6 +39,7 @@ static int start_child(struct queue_entry *q,
 static int prepare_child(struct queue_entry *q, 
                          const struct pbgc_params *params,
                          void attribute((unused)) *bgdata);
+static void ensure_next_scratch(ev_source *ev);
 
 /** @brief File descriptor of our end of the socket to the speaker */
 static int speaker_fd = -1;
@@ -195,7 +196,9 @@ static void finished(ev_source *ev) {
  * some time before the speaker reports it as finished) or when a non-raw
  * (i.e. non-speaker) player terminates.  In the latter case it's imaginable
  * that the OS has buffered the last few samples.
- * 
+ *
+ * NB.  The finished track might NOT be in the queue (yet) - it might be a
+ * pre-chosen scratch.
  */
 static int player_finished(ev_source *ev,
                           pid_t pid,
@@ -609,6 +612,8 @@ void play(ev_source *ev) {
      * potentially be a just-added random track. */
     if(qhead.next != &qhead)
       prepare(ev, qhead.next);
+    /* Make sure there is a prepared scratch */
+    ensure_next_scratch(ev);
     break;
   }
 }
@@ -656,12 +661,27 @@ void disable_random(const char *who) {
 
 /* Scratching --------------------------------------------------------------- */
 
+/** @brief Track to play next time something is scratched */
+static struct queue_entry *next_scratch;
+
+/** @brief Ensure there isa prepared scratch */
+static void ensure_next_scratch(ev_source *ev) {
+  if(next_scratch)                      /* There's one already */
+    return;
+  if(!config->scratch.n)                /* There are no scratches */
+    return;
+  int r = rand() * (double)config->scratch.n / (RAND_MAX + 1.0);
+  next_scratch = queue_add(config->scratch.s[r], NULL,
+                           WHERE_NOWHERE, NULL, origin_scratch);
+  if(ev)
+    prepare(ev, next_scratch);
+}
+
 /** @brief Scratch a track
  * @param who User responsible (or NULL)
  * @param id Track ID (or NULL for current)
  */
 void scratch(const char *who, const char *id) {
-  struct queue_entry *q;
   struct speaker_message sm;
 
   D(("scratch playing=%p state=%d id=%s playing->id=%s",
@@ -692,12 +712,14 @@ void scratch(const char *who, const char *id) {
       speaker_send(speaker_fd, &sm);
       D(("sending SM_CANCEL for %s", playing->id));
     }
-    /* put a scratch track onto the front of the queue (but don't
-     * bother if playing is disabled) */
-    if(playing_is_enabled() && config->scratch.n) {
-      int r = rand() * (double)config->scratch.n / (RAND_MAX + 1.0);
-      q = queue_add(config->scratch.s[r], who, WHERE_START, NULL, 
-                    origin_scratch);
+    /* Try to make sure there is a scratch */
+    ensure_next_scratch(NULL);
+    /* Insert it at the head of the queue */
+    if(next_scratch){
+      next_scratch->submitter = who;
+      queue_insert_entry(&qhead, next_scratch);
+      eventlog_raw("queue", queue_marshall(next_scratch), (const char *)0);
+      next_scratch = NULL;
     }
     notify_scratch(playing->track, playing->submitter, who,
                   xtime(0) - playing->played);
index e513d16..7dcaa84 100644 (file)
@@ -104,6 +104,8 @@ struct queue_entry *queue_add(const char *track, const char *submitter,
     }
     queue_insert_entry(afterme, q);
     break;
+  case WHERE_NOWHERE:
+    return q;
   }
   /* submitter will be a null pointer for a scratch */
   if(submitter)