From: Richard Kettlewell Date: Tue, 1 Dec 2009 18:50:57 +0000 (+0000) Subject: New compact mode for Disobediene. X-Git-Tag: 5.0~27 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/a89e0ecf7c67ef6af6678985060a0d8e7bae5fba?hp=c2f519409c8d38f59e169abfc39bc97586222f85 New compact mode for Disobediene. Queue tab now shows playing track with green background, like the web interface. --- diff --git a/CHANGES.html b/CHANGES.html index 17da951..e631294 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -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 {

This file documents recent user-visible changes to DisOrder.

-

Changes up to version 4.4

+

Changes up to version 5.0

@@ -75,7 +85,8 @@ span.command {

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.

+ that bugs may have been (re-)introduced. Decoding of scratches is also + initiated ahead of time, giving more reliable playback.

The command backend now (optionally) sends silence instead of suspending writes when a pause occurs or no track is playing.

@@ -157,7 +168,12 @@ span.command { ID Description - + + + #22 + Background decoders interact badly with server reload + + #27 Mac DisOrder uses wrong sound device @@ -208,6 +224,11 @@ span.command { disobedience doesn't configure its back end + + #46 + Sort search results in web interface + + #48 build-time dependency on oggdec removed @@ -218,6 +239,18 @@ span.command { Disobedience's 'When' column gets out of date + + (none) + “found track in no collection” messages for scratches + are now suppressed + + + + (none) + Disobedience would sometimes fail to notice when a track + started, leading to its display getting out of date. + +
diff --git a/cgi/macros-disorder.c b/cgi/macros-disorder.c index 30614c1..29835bb 100644 --- a/cgi/macros-disorder.c +++ b/cgi/macros-disorder.c @@ -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} diff --git a/lib/trackname.c b/lib/trackname.c index 4e2e06e..aa11e2e 100644 --- a/lib/trackname.c +++ b/lib/trackname.c @@ -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; } diff --git a/lib/trackname.h b/lib/trackname.h index 63b881b..56f933e 100644 --- a/lib/trackname.h +++ b/lib/trackname.h @@ -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)); diff --git a/lib/trackorder.c b/lib/trackorder.c index d0ae448..94d2bd5 100644 --- a/lib/trackorder.c +++ b/lib/trackorder.c @@ -27,6 +27,22 @@ #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 */ diff --git a/lib/tracksort.c b/lib/tracksort.c index 2f81739..b1fcc3a 100644 --- a/lib/tracksort.c +++ b/lib/tracksort.c @@ -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) { diff --git a/server/disorder-server.h b/server/disorder-server.h index f980b6f..e8f4dab 100644 --- a/server/disorder-server.h +++ b/server/disorder-server.h @@ -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); diff --git a/server/normalize.c b/server/normalize.c index 8935927..0f97a2f 100644 --- a/server/normalize.c +++ b/server/normalize.c @@ -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; } diff --git a/server/play.c b/server/play.c index f93fd5a..aa5b8c7 100644 --- a/server/play.c +++ b/server/play.c @@ -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); diff --git a/server/queue-ops.c b/server/queue-ops.c index e513d16..7dcaa84 100644 --- a/server/queue-ops.c +++ b/server/queue-ops.c @@ -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)