<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>
<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>
<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>
<td>Disobedience's 'When' column gets out of date</td>
</tr>
+ <tr>
+ <td>(none)</td>
+ <td>“found track in no collection” messages for scratches
+ are now suppressed</td>
+ </tr>
+
</table>
</div>
</div>
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;
* 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,
* 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;
}
}
/* 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",
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);