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;
case SM_FINISHED: /* scratched the playing track */
case SM_STILLBORN: /* scratched too early */
case SM_UNKNOWN: /* scratched WAY too early */
- if(playing && !strcmp(sm.id, playing->id))
+ if(playing && !strcmp(sm.id, playing->id)) {
+ if((playing->state == playing_unplayed
+ || playing->state == playing_started)
+ && sm.type == SM_FINISHED)
+ playing->state = playing_ok;
finished(ev);
+ }
break;
case SM_PLAYING:
/* track ID is playing, DATA seconds played */
D(("SM_PLAYING %s %ld", sm.id, sm.data));
playing->sofar = sm.data;
break;
+ case SM_ARRIVED: {
+ /* track ID is now prepared */
+ struct queue_entry *q;
+ for(q = qhead.next; q != &qhead && strcmp(q->id, sm.id); q = q->next)
+ ;
+ if(q && q->preparing) {
+ q->preparing = 0;
+ q->prepared = 1;
+ /* We might be waiting to play the now-prepared track */
+ play(ev);
+ }
+ break;
+ }
default:
disorder_error(0, "unknown speaker message type %d", sm.type);
}
* 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,
D(("start %s", q->id));
/* Find the player plugin. */
- if(!(player = find_player(q)) < 0)
+ if(!(player = find_player(q)))
return START_HARDFAIL; /* No player */
if(!(q->pl = open_plugin(player->s[1], 0)))
return START_HARDFAIL;
if(q->pid >= 0)
return START_OK;
/* If the track is already prepared, do nothing */
- if(q->prepared)
+ if(q->prepared || q->preparing)
return START_OK;
/* Find the player plugin */
- if(!(player = find_player(q)) < 0)
+ if(!(player = find_player(q)))
return START_HARDFAIL; /* No player */
q->pl = open_plugin(player->s[1], 0);
q->type = play_get_type(q->pl);
if((q->type & DISORDER_PLAYER_TYPEMASK) != DISORDER_PLAYER_RAW)
return START_OK; /* Not a raw player */
- const int rc = play_background(ev, player, q, prepare_child, NULL);
+ int rc = play_background(ev, player, q, prepare_child, NULL);
if(rc == START_OK) {
ev_child(ev, q->pid, 0, player_finished, q);
- q->prepared = 1;
+ q->preparing = 1;
+ /* Actually the track is still "in flight" */
+ rc = START_SOFTFAIL;
}
return rc;
}
* 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);
+ /* If playing is enabled then add a scratch to the queue. Having a scratch
+ * appear in the queue when further play is disabled is weird and
+ * contradicts implicit assumptions made elsewhere, so we try to avoid
+ * it. */
+ if(playing_is_enabled()) {
+ /* 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);