chiark / gitweb /
New compact mode for Disobediene.
authorRichard Kettlewell <rjk@greenend.org.uk>
Tue, 1 Dec 2009 18:50:57 +0000 (18:50 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Tue, 1 Dec 2009 18:50:57 +0000 (18:50 +0000)
Queue tab now shows playing track with green background, like the
web interface.

CHANGES.html
cgi/macros-disorder.c
lib/trackname.c
lib/trackname.h
lib/trackorder.c
lib/tracksort.c
server/disorder-server.h
server/normalize.c
server/play.c
server/queue-ops.c

index 17da9515ac280b84e29e7002822bf9e1dc49b570..e6312949c003bb9f8ffcf391c7df3e95cb3e9826 100644 (file)
@@ -39,11 +39,21 @@ h4 {
 }
 
 table.bugs {
-  width: 100%
+  width: 100%;
+  font-size: 12pt;
+  border-collapse: collapse;
+  border:1px
 }
 
 table.bugs th {
-  text-align: left
+  text-align: left;
+  border: 1px solid black;
+  background-color: black;
+  color: white
+}
+
+table.bugs td {
+  border: 1px solid
 }
 
 span.command {
@@ -58,7 +68,7 @@ span.command {
 <p>This file documents recent user-visible changes to <a
  href="http://www.greenend.org.uk/rjk/disorder/">DisOrder</a>.</p>
 
-<h2>Changes up to version 4.4</h2>
+<h2>Changes up to version 5.0</h2>
 
   <div class=section>
   
@@ -75,7 +85,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>
@@ -157,7 +168,12 @@ span.command {
           <th>ID</th>
           <th>Description</th>
         </tr>
-        
+
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=22">#22</a></td>
+          <td>Background decoders interact badly with server reload</td>
+        </tr>
+
         <tr>
           <td><a href="http://code.google.com/p/disorder/issues/detail?id=27">#27</a></td>
           <td>Mac DisOrder uses wrong sound device</td>
@@ -208,6 +224,11 @@ span.command {
           <td>disobedience doesn't configure its back end</td>
         </tr>
 
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=46">#46</a></d>
+          <td>Sort search results in web interface</td>
+        </tr>
+
         <tr>
           <td><a href="http://code.google.com/p/disorder/issues/detail?id=48">#48</a></d>
           <td>build-time dependency on <tt>oggdec</tt> removed</td>
@@ -218,6 +239,18 @@ 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>
+
+        <tr>
+          <td>(none)</td>
+          <td>Disobedience would sometimes fail to notice when a track
+          started, leading to its display getting out of date.</td>
+        </tr>
+
       </table>
     </div>
   </div>
index 30614c1a26d2fecca465457e52e053169d88d466..29835bbc849c196fcea60124fc55b10969cf6bde 100644 (file)
@@ -859,24 +859,36 @@ static int exp__files_dirs(int nargs,
   /* Get the list */
   if(fn(dcgi_client, dir, re, &tracks, &ntracks))
     return 0;
-  /* Sort it.  NB trackname_transform() does not go to the server. */
-  tsd = tracksort_init(ntracks, tracks, type);
-  /* Expand the subsiduary templates.  We chuck in @sort and @display because
-   * it is particularly easy to do so. */
-  for(n = 0; n < ntracks; ++n)
-    if((rc = mx_expand(mx_rewritel(m,
-                                   "index", make_index(n),
-                                   "parity", n % 2 ? "odd" : "even",
-                                   "track", tsd[n].track,
-                                   "first", n == 0 ? "true" : "false",
-                                   "last", n + 1 == ntracks ? "false" : "true",
-                                   "sort", tsd[n].sort,
-                                   "display", tsd[n].display,
-                                   (char *)0),
-                       output, u)))
-      return rc;
+  if(type) {
+    /* Sort it.  NB trackname_transform() does not go to the server. */
+    tsd = tracksort_init(ntracks, tracks, type);
+    /* Expand the subsiduary templates.  We chuck in @sort and @display because
+     * it is particularly easy to do so. */
+    for(n = 0; n < ntracks; ++n)
+      if((rc = mx_expand(mx_rewritel(m,
+                                     "index", make_index(n),
+                                     "parity", n % 2 ? "odd" : "even",
+                                     "track", tsd[n].track,
+                                     "first", n == 0 ? "true" : "false",
+                                     "last", n + 1 == ntracks ? "false" : "true",
+                                     "sort", tsd[n].sort,
+                                     "display", tsd[n].display,
+                                     (char *)0),
+                         output, u)))
+        return rc;
+  } else {
+    for(n = 0; n < ntracks; ++n)
+      if((rc = mx_expand(mx_rewritel(m,
+                                     "index", make_index(n),
+                                     "parity", n % 2 ? "odd" : "even",
+                                     "track", tracks[n],
+                                     "first", n == 0 ? "true" : "false",
+                                     "last", n + 1 == ntracks ? "false" : "true",
+                                     (char *)0),
+                         output, u)))
+        return rc;
+  }
   return 0;
-
 }
 
 /*$ @tracks{DIR}{RE}{TEMPLATE}
@@ -936,14 +948,12 @@ static int exp__search_shim(disorder_client *c, const char *terms,
  * - @parity: "even" or "odd" alternately
  * - @first: "true" on the first directory and "false" otherwise
  * - @last: "true" on the last directory and "false" otherwise
- * - @sort: the sort key for this track
- * - @display: the UNQUOTED display string for this track
  */
 static int exp_search(int nargs,
                       const struct mx_node **args,
                       struct sink *output,
                       void *u) {
-  return exp__files_dirs(nargs, args, output, u, "track", exp__search_shim);
+  return exp__files_dirs(nargs, args, output, u, NULL, exp__search_shim);
 }
 
 /*$ @label{NAME}
index 4e2e06e22e32487ceea76ac08e70750d710399da..aa11e2e819286fc38ec8ea84b354e44e47180918 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 63b881bb836c523ad962b81f26118e4d47129c9f..56f933ee95682d21911e856f2d3255b7018585b7 100644 (file)
@@ -51,7 +51,14 @@ int compare_path_raw(const unsigned char *ap, size_t an,
 /* Comparison function for path names that groups all entries in a directory
  * together */
 
-/* Convenient wrapper for compare_path_raw */
+/** @brief Compare two paths
+ * @param ap First path
+ * @param bp Second path
+ * @return -ve, 0 or +ve for ap <, = or > bp
+ *
+ * Sorts files within a directory together.
+ * A wrapper around compare_path_raw().
+ */
 static inline int compare_path(const char *ap, const char *bp) {
   return compare_path_raw((const unsigned char *)ap, strlen(ap),
                          (const unsigned char *)bp, strlen(bp));
index d0ae4486f597927de06222858fa65863ce959a0d..94d2bd5e9cde7dece533e64728a8cc83c5f8fbad 100644 (file)
 #include "log.h"
 #include "unicode.h"
 
+/** @brief Compare two tracks
+ * @param sa First sort key
+ * @param sb Second sort key
+ * @param da First display string
+ * @param db Second display string
+ * @param ta First raw track
+ * @param tb Second raw track
+ * @return -ve, 0 or +ve for a <, = or > b
+ *
+ * Tries the following comparisons until a difference is found:
+ * - case-independent comparison of sort keys
+ * - case-dependent comparison of sort keys
+ * - case-independent comparison of display strings
+ * - case-dependent comparison of display strings
+ * - case-dependent comparison of paths (see compare_path())
+ */
 int compare_tracks(const char *sa, const char *sb,
                   const char *da, const char *db,
                   const char *ta, const char *tb) {
@@ -43,6 +59,17 @@ int compare_tracks(const char *sa, const char *sb,
   return compare_path(ta, tb);
 }
 
+/** @brief Compare two paths
+ * @param ap First path
+ * @param an Length of @p ap
+ * @param bp Second path
+ * @param bn Length @p bp
+ * @return -ve, 0 or +ve for ap <, = or > bp
+ *
+ * Sorts files within a directory together.
+ *
+ * See also compare_path().
+ */
 int compare_path_raw(const unsigned char *ap, size_t an,
                     const unsigned char *bp, size_t bn) {
   /* Don't change this function!  The database sort order depends on it */
index 2f81739098583802ff2f382ebfa6e57470c64531..b1fcc3af87d01f0134613e4fa2b19455d7a2cac9 100644 (file)
@@ -32,6 +32,16 @@ static int tracksort_compare(const void *a, const void *b) {
                        ea->track, eb->track);
 }
 
+/** @brief Sort tracks
+ * @param ntracks Number of tracks to sort
+ * @param tracks List of tracks
+ * @param type Comparison type
+ * @return Sorted track data
+ *
+ * Tracks are compared using compare_tracks(), with the sort key and display
+ * string set according to @p type, which should be "track" if the tracks are
+ * really tracks and "dir" if they are directories.
+ */
 struct tracksort_data *tracksort_init(int ntracks,
                                       char **tracks,
                                       const char *type) {
index f980b6f3ea9a1ab1aabbe171dc4d466c996ee58e..e8f4dabd17c6aea1a3df02eb623766a0bbe82d7e 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 89359273561602345b549d24f004bf26453edc80..0f97a2ff024a9d3360526af306828b3ad7767630 100644 (file)
@@ -278,7 +278,6 @@ int main(int argc, char attribute((unused)) **argv) {
                                                  converted, 0);
         //syslog(LOG_INFO, "used=%zu consumed=%zu", used, consumed);
         D(("consumed=%zu", consumed));
-        assert(consumed != 0);
         memmove(buffer, buffer + consumed, used - consumed);
         used -= consumed;
       }
index f93fd5a8707d96de6af9558fbd08ed7e088b5a0f..aa5b8c7270eaa4e52c4114efc5e48a89ab7d22e7 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 e513d164b61410e30a7efe3ed332ec6b645b0bda..7dcaa845bef920505f6b29979b1659bbac5f3367 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)