#include <lopsub.h>
#include "recv_cmd.lsg.h"
+#include "filter_cmd.lsg.h"
#include "play_cmd.lsg.h"
#include "write_cmd.lsg.h"
#include "play.lsg.h"
#include "write.h"
#include "fd.h"
-/**
- * Besides playback tasks which correspond to the receiver/filter/writer nodes,
- * para_play creates two further tasks: The play task and the i9e task. It is
- * important whether a function can be called in the context of para_play or
- * i9e or both. As a rule, all command handlers are called only in i9e context via
- * the line handler (input mode) or the key handler (command mode) below.
- *
- * Playlist handling is done exclusively in play context.
- */
-
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
assert(false);
};
+/* returns number of milliseconds */
static long unsigned get_play_time(void)
{
char state = get_playback_state();
return 0;
if (pt->num_chunks == 0 || pt->seconds == 0)
return 0;
- /* where the stream started (in seconds) */
- result = pt->start_chunk * pt->seconds / pt->num_chunks;
+ /* where the stream started (in milliseconds) */
+ result = 1000ULL * pt->start_chunk * pt->seconds / pt->num_chunks;
if (pt->wn.btrn) { /* Add the uptime of the writer node */
struct timeval diff = {.tv_sec = 0}, wstime;
btr_get_node_start(pt->wn.btrn, &wstime);
if (wstime.tv_sec > 0)
tv_diff(now, &wstime, &diff);
- result += diff.tv_sec;
+ result += tv2ms(&diff);
}
- result = PARA_MIN(result, pt->seconds);
+ result = PARA_MIN(result, pt->seconds * 1000);
result = PARA_MAX(result, 0UL);
return result;
}
return 0;
if (task_status(pt->rn.task) >= 0)
return 0;
- if (err == -E_BTR_EOF || err == -E_RECV_EOF || err == -E_EOF
- || err == -E_WRITE_COMMON_EOF)
+ if (err == -E_EOF)
return 1;
return err;
}
if (decoder->close)
decoder->close(&pt->fn);
btr_remove_node(&pt->fn.btrn);
+ lls_free_parse_result(pt->fn.lpr, FILTER_CMD(pt->fn.filter_num));
free(pt->fn.conf);
memset(&pt->fn, 0, sizeof(struct filter_node));
static void init_shuffle_map(void)
{
unsigned n, num_inputs = lls_num_inputs(play_lpr);
- shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+ shuffle_map = arr_alloc(num_inputs, sizeof(unsigned));
for (n = 0; n < num_inputs; n++)
shuffle_map[n] = n;
if (!OPT_GIVEN(RANDOMIZE))
if (!p)
return NULL;
len = p - kma;
- result = para_malloc(len + 1);
+ result = alloc(len + 1);
memcpy(result, kma, len);
result[len] = '\0';
return result;
if (len == 1 && isprint(*seq))
return seq;
- sseq = para_malloc(2 + 2 * len + 1);
+ sseq = alloc(2 + 2 * len + 1);
sseq[0] = '0';
sseq[1] = 'x';
for (n = 0; n < len; n++) {
char **result;
int i;
- result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
+ result = arr_alloc(NUM_MAPPED_KEYS + 1, sizeof(char *));
FOR_EACH_MAPPED_KEY(i) {
char *seq = get_key_map_seq(i);
result[i] = seq;
static int com_pause(__a_unused struct lls_parse_result *lpr)
{
char state;
- long unsigned seconds, ss;
+ uint64_t ms;
+ unsigned long cn; /* chunk num */
state = get_playback_state();
pt->playing = false;
if (state != 'P')
return 0;
- seconds = get_play_time();
+ ms = get_play_time();
pt->playing = false;
- ss = 0;
+ cn = 0;
if (pt->seconds > 0)
- ss = seconds * pt->num_chunks / pt->seconds + 1;
- ss = PARA_MAX(ss, 0UL);
- ss = PARA_MIN(ss, pt->num_chunks);
- pt->start_chunk = ss;
+ cn = ms * pt->num_chunks / pt->seconds / 1000 + 1;
+ cn = PARA_MIN(cn, pt->num_chunks);
+ pt->start_chunk = cn;
pt->rq = CRT_REPOS;
kill_stream();
return 0;
return ret;
if (pt->playing && !pt->fn.btrn)
return 0;
- seconds += get_play_time();
+ seconds += (get_play_time() + 500) / 1000;
seconds = PARA_MIN(seconds, (typeof(seconds))pt->seconds - 4);
seconds = PARA_MAX(seconds, 0);
pt->start_chunk = pt->num_chunks * seconds / pt->seconds;
char *dot_para = make_message("%s/.paraslash", home);
free(home);
- ret = para_mkdir(dot_para, 0777);
+ ret = para_mkdir(dot_para);
/* warn, but otherwise ignore mkdir error */
- if (ret < 0 && ret != -ERRNO_TO_PARA_ERROR(EEXIST))
+ if (ret < 0)
PARA_WARNING_LOG("Can not create %s: %s\n", dot_para,
para_strerror(-ret));
history_file = make_message("%s/play.history", dot_para);
length = pt->seconds;
if (length == 0)
return xasprintf(result, "0:00 [0:00] (0%%/0:00)");
- seconds = get_play_time();
+ seconds = (get_play_time() + 500) / 1000;
return xasprintf(result, "#%u: %d:%02d [%d:%02d] (%d%%/%d:%02d) %s",
pt->current_file,
seconds / 60,
/**
* The main function of para_play.
*
- * \param argc Standard.
- * \param argv Standard.
+ * \param argc See man page.
+ * \param argv See man page.
+ *
+ * para_play distributes its work by submitting various tasks to the paraslash
+ * scheduler. The receiver, filter and writer tasks, which are used to play an
+ * audio file, require one task each to maintain their underlying buffer tree
+ * node. These tasks only exist when an audio file is playing.
+ *
+ * The i9 task, which is submitted and maintained by the i9e subsystem, reads
+ * an input line and calls the corresponding command handler such as com_stop()
+ * which is implemented in this file. The command handlers typically write a
+ * request to the global play_task structure, whose contents are read and acted
+ * upon by another task, the play task.
+ *
+ * As a rule, playlist handling is performed exclusively in play context, i.e.
+ * in the post-monitor step of the play task, while command handlers are only
+ * called in i9e context.
*
- * \return \p EXIT_FAILURE or \p EXIT_SUCCESS.
+ * \return EXIT_FAILURE or EXIT_SUCCESS.
*/
int main(int argc, char *argv[])
{
session_open();
num_inputs = lls_num_inputs(play_lpr);
init_shuffle_map();
- pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
+ pt->invalid = arr_zalloc(num_inputs, sizeof(*pt->invalid));
pt->rq = CRT_FILE_CHANGE;
pt->playing = true;
pt->task = task_register(&(struct task_info){