$(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists)
$(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists)
-$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash | $(man_dir) $(help2man_dir)
+$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash \
+ git-version.h | $(man_dir) $(help2man_dir)
@[ -z "$(Q)" ] || echo 'MAN $<'
$(Q) \
COMMAND_LISTS="$(man_util_command_lists)" \
current master branch "cascading gradient"
------------------------------------------
+The highlight of this release is the new -m flag for para_afh which
+lets it modify the meta tags of the given audio file(s). This feature
+is supported for all audio formats. Many small cleanups and bug fixes
+not mentioned here have accumulated and are also part of the release.
+
- para_afh learned to modify meta tags of mp3 wma ogg spx
opus flac aac files.
- afs commands propagate error codes to the client.
inconsistencies.
- New -v flag for the version command (print verbose version string)
- New option --priority for para_server and para_audiod.
+ - New mood methods: image_id and lyrics_id.
+Download: ./releases/paraslash-git.tar.bz2 (tarball)
--------------------------------------
0.5.5 (2015-09-20) "magnetic momentum"
{
struct msghdr msg = {.msg_iov = NULL};
struct cmsghdr *cmsg;
- char control[255];
+ char control[255] __a_aligned(8);
int ret;
struct iovec iov;
I9E_DUMMY_COMPLETER(play);
I9E_DUMMY_COMPLETER(si);
I9E_DUMMY_COMPLETER(term);
-I9E_DUMMY_COMPLETER(version);
I9E_DUMMY_COMPLETER(stop);
I9E_DUMMY_COMPLETER(addatt);
I9E_DUMMY_COMPLETER(init);
result->matches = i9e_complete_commands(ci->word, completers);
}
+static void version_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ char *opts[] = {"-v", NULL};
+ i9e_complete_option(opts, ci, cr);
+}
+
static void stat_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
struct i9e_completion_result *cr)
{
char *opts[] = {"-i", "-l", "-r", NULL};
-
- if (ci->word[0] == '-')
- i9e_complete_option(opts, ci, cr);
- else
- complete_attributes(ci->word, &cr->matches);
+ i9e_complete_option(opts, ci, cr);
}
static void mvatt_completer(struct i9e_completion_info *ci,
static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
{
int i;
- /* this has to match sender.h */
- const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
+ const char *subcmds[] = {SENDER_SUBCOMMANDS NULL};
scd->sender_num = -1;
if (argc < 3)
return -E_COMMAND_SYNTAX;
if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
return -E_SENDER_CMD;
switch (scd->cmd_num) {
- case SENDER_ON:
- case SENDER_OFF:
+ case SENDER_on:
+ case SENDER_off:
if (argc != 3)
return -E_COMMAND_SYNTAX;
break;
- case SENDER_DENY:
- case SENDER_ALLOW:
+ case SENDER_deny:
+ case SENDER_allow:
if (argc != 4 || parse_cidr(argv[3], scd->host,
sizeof(scd->host), &scd->netmask) == NULL)
return -E_COMMAND_SYNTAX;
break;
- case SENDER_ADD:
- case SENDER_DELETE:
+ case SENDER_add:
+ case SENDER_delete:
if (argc != 4)
return -E_COMMAND_SYNTAX;
return parse_fec_url(argv[3], scd);
}
switch (scd.cmd_num) {
- case SENDER_ADD:
- case SENDER_DELETE:
+ case SENDER_add:
+ case SENDER_delete:
assert(senders[scd.sender_num].resolve_target);
ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
if (ret < 0)
s->shutdown_clients = dccp_shutdown_clients;
s->resolve_target = NULL;
s->help = generic_sender_help;
- s->client_cmds[SENDER_ON] = dccp_com_on;
- s->client_cmds[SENDER_OFF] = dccp_com_off;
- s->client_cmds[SENDER_DENY] = dccp_com_deny;
- s->client_cmds[SENDER_ALLOW] = dccp_com_allow;
- s->client_cmds[SENDER_ADD] = NULL;
- s->client_cmds[SENDER_DELETE] = NULL;
+ s->client_cmds[SENDER_on] = dccp_com_on;
+ s->client_cmds[SENDER_off] = dccp_com_off;
+ s->client_cmds[SENDER_deny] = dccp_com_deny;
+ s->client_cmds[SENDER_allow] = dccp_com_allow;
+ s->client_cmds[SENDER_add] = NULL;
+ s->client_cmds[SENDER_delete] = NULL;
k = conf.dccp_data_slices_per_group_arg;
n = conf.dccp_slices_per_group_arg;
PARA_ERROR(MP4ASC, "audio spec config error"), \
PARA_ERROR(AAC_AFH_INIT, "failed to init aac decoder"), \
PARA_ERROR(MP4V2, "mp4v2 library error"), \
- PARA_ERROR(NO_AUDIO_TRACK, "file contains no valid audio track"), \
#define AAC_COMMON_ERRORS \
PARA_ERROR(ESDS, "did not find esds atom"), \
waddstr(win, str);
} else {
add_spaces(win, num / 2);
- waddstr(win, str[0]? str: "");
+ waddstr(win, str);
add_spaces(win, num - num / 2);
}
return 1;
{
if (curses_active())
return;
- if (top.win && refresh() == ERR) /* refresh is really needed */
+ if (refresh() == ERR) /* refresh is really needed */
die(EXIT_FAILURE, "refresh() failed\n");
if (LINES < theme.lines_min || COLS < theme.cols_min)
die(EXIT_FAILURE, "Terminal (%dx%d) too small"
s->shutdown_clients = http_shutdown_clients;
s->resolve_target = NULL;
s->help = generic_sender_help;
- s->client_cmds[SENDER_ON] = http_com_on;
- s->client_cmds[SENDER_OFF] = http_com_off;
- s->client_cmds[SENDER_DENY] = http_com_deny;
- s->client_cmds[SENDER_ALLOW] = http_com_allow;
- s->client_cmds[SENDER_ADD] = NULL;
- s->client_cmds[SENDER_DELETE] = NULL;
+ s->client_cmds[SENDER_on] = http_com_on;
+ s->client_cmds[SENDER_off] = http_com_off;
+ s->client_cmds[SENDER_deny] = http_com_deny;
+ s->client_cmds[SENDER_allow] = http_com_allow;
+ s->client_cmds[SENDER_add] = NULL;
+ s->client_cmds[SENDER_delete] = NULL;
init_sender_status(hss, conf.http_access_arg, conf.http_access_given,
conf.http_port_arg, conf.http_max_clients_arg,
struct task *task;
struct btr_node *stdout_btrn;
bool last_write_was_status;
- bool line_handler_running;
bool input_eof;
bool caught_sigint;
bool caught_sigterm;
i9ep->empty_line[i9ep->num_columns] = '\0';
}
-/**
- * Defined key sequences are mapped to keys starting with this offset. I.e.
- * pressing the first defined key sequence yields the key number \p KEY_OFFSET.
- */
static int dispatch_key(__a_unused int count, __a_unused int key)
{
int i, ret;
if (ici->producer) {
rl_callback_handler_install("", i9e_line_handler);
i9e_attach_to_stdout(ici->producer);
- rl_set_keymap(i9ep->bare_km);
} else
rl_callback_handler_install(i9ep->ici->prompt, i9e_line_handler);
return 1;
* Tell i9e that the caller received a signal.
*
* \param sig_num The number of the signal received.
- *
- * Currently the function only cares about \p SIGINT, but this may change.
*/
void i9e_signal_dispatch(int sig_num)
{
return mm_compare_num_score_function(afhi->channels, private);
}
+static int mm_image_id_score_function(__a_unused const char *path,
+ const struct afs_info *afsi,
+ __a_unused const struct afh_info *afhi,
+ const void *private)
+{
+ return mm_compare_num_score_function(afsi->image_id, private);
+}
+
+static int mm_lyrics_id_score_function(__a_unused const char *path,
+ const struct afs_info *afsi,
+ __a_unused const struct afh_info *afhi,
+ const void *private)
+{
+ return mm_compare_num_score_function(afsi->lyrics_id, private);
+}
+
static int mm_num_played_score_function(__a_unused const char *path,
const struct afs_info *afsi,
__a_unused const struct afh_info *afhi,
{DEFINE_COMPARE_NUM_MOOD_METHOD(frequency)},
{DEFINE_COMPARE_NUM_MOOD_METHOD(channels)},
{DEFINE_COMPARE_NUM_MOOD_METHOD(num_played)},
+ {DEFINE_COMPARE_NUM_MOOD_METHOD(image_id)},
+ {DEFINE_COMPARE_NUM_MOOD_METHOD(lyrics_id)},
{.parser = NULL}
};
{
if (!n || !qd)
return 0;
- return 100 * (n * x - sum) / (int64_t)int_sqrt(n * qd);
+ return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
}
static long compute_score(struct afs_info *afsi, long mood_score)
static int add_afs_statistics(const struct osl_row *row)
{
- uint64_t n, x, s;
+ uint64_t n, x, s, q;
struct afs_info afsi;
int ret;
n = statistics.num;
x = afsi.last_played;
s = statistics.last_played_sum;
- if (n > 0)
- statistics.last_played_qd += (x - s / n) * (x - s / n) * n / (n + 1);
+ if (n > 0) {
+ q = (x > s / n)? x - s / n : s / n - x;
+ statistics.last_played_qd += q * q * n / (n + 1);
+ }
statistics.last_played_sum += x;
x = afsi.num_played;
s = statistics.num_played_sum;
- if (n > 0)
- statistics.num_played_qd += (x - s / n) * (x - s / n) * n / (n + 1);
+ if (n > 0) {
+ q = (x > s / n)? x - s / n : s / n - x;
+ statistics.num_played_qd += q * q * n / (n + 1);
+ }
statistics.num_played_sum += x;
statistics.num++;
return 1;
* the last number a_n was replaced by b) may be computed in O(1) time in terms
* of n, q, a_n, b, and S as
*
- * q' = q + d * s - (2 * S + d) * d / n,
+ * q' = q + d * s - (2 * S + d) * d / n
+ * = q + d * (s - 2 * S / n - d /n),
*
* where d = b - a_n, and s = b + a_n.
*
{
int64_t delta = new_val - old_val;
int64_t sigma = new_val + old_val;
- return old_qd + delta * sigma - (2 * old_sum + delta) * delta / n;
+ return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
}
static int update_afs_statistics(struct afs_info *old_afsi, struct afs_info *new_afsi)
*/
int recv_cred_buffer(int fd, char *buf, size_t size)
{
- char control[255];
+ char control[255] __a_aligned(8);
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
if (ret < 0)
return ret;
afhi->channels = oh->channels;
- afhi->techinfo = make_message("header version %d, input sample rate: %dHz",
+ afhi->techinfo = make_message(
+ "header version %d, input sample rate: %" PRIu32 "Hz",
oh->version, oh->input_sample_rate);
/*
* The input sample rate is irrelevant for afhi->frequency as
if (!read_chars(&p, &ch, 1))
return -E_OPUS_HEADER;
h->version = ch;
- if((h->version & 240) != 0) /* Only major version 0 supported. */
+ if ((h->version & 240) != 0) /* Only major version 0 supported. */
return -E_OPUS_HEADER;
if (!read_chars(&p, &ch, 1))
char *new_path;
const struct osl_row *row = data;
- if (!current_playlist.name)
- return 1;
if (event == AUDIO_FILE_RENAME) {
ret = row_belongs_to_score_table(row, NULL);
if (ret < 0)
int ret;
struct afsi_change_event_data *aced = data;
- switch(event) {
+ if (!current_playlist.name)
+ return 1;
+ switch (event) {
case AFSI_CHANGE:
return playlist_update_audio_file(aced->aft_row);
case AUDIO_FILE_RENAME:
/** \file send.h Sender-related defines and structures. */
-/** The sender subcommands. */
+#define SENDER_SUBCOMMANDS \
+ SENDER_SUBCOMMAND(add) /**< Add a target (udp only). */ \
+ SENDER_SUBCOMMAND(delete) /**< Delete a target (udp only). */ \
+ SENDER_SUBCOMMAND(allow) /**< Allow connections from given IP address(es). */ \
+ SENDER_SUBCOMMAND(deny) /**< Deny connections from given IP address(es). */ \
+ SENDER_SUBCOMMAND(on) /**< Activate the sender. */ \
+ SENDER_SUBCOMMAND(off) /**< Deactivate the sender. */ \
+
+#define SENDER_SUBCOMMAND(_name) SENDER_ ## _name,
enum sender_subcommand {
- SENDER_ADD, /**< Add a target (udp only). */
- SENDER_DELETE, /**< Delete a target (udp only). */
- SENDER_ALLOW, /**< Allow connections from given IP address(es). */
- SENDER_DENY, /**< Deny connections from given IP address(es). */
- SENDER_ON, /**< Activate the sender. */
- SENDER_OFF, /**< Deactivate the sender. */
+ SENDER_SUBCOMMANDS
NUM_SENDER_CMDS /**< Used as array size in struct \ref sender. */
};
+#undef SENDER_SUBCOMMAND
+#define SENDER_SUBCOMMAND(_name) #_name,
/**
* Describes one supported sender of para_server.
return -ERRNO_TO_PARA_ERROR(errno);
if (num_wchars == 0)
return 0;
- dest = para_malloc(num_wchars * sizeof(*dest));
+ dest = para_malloc((num_wchars + 1) * sizeof(*dest));
src = s;
memset(&state, 0, sizeof(state));
num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
struct fec_client *fc;
/** The FEC parameters for this target. */
struct fec_client_parms fcp;
+ /** Whether we already sent the FEC eof packet to this target. */
+ bool sent_fec_eof;
};
static struct list_head targets;
static void udp_close_target(struct sender_client *sc)
{
const char *buf;
- size_t len = vss_get_fec_eof_packet(&buf);
-
- /*
- * Ignore the return value of write() since we are closing the target
- * anyway. The sole purpose of the "do_nothing" statement is to silence
- * gcc.
- */
- if (write(sc->fd, buf, len))
- do_nothing;
+ size_t len;
+ struct udp_target *ut = sc->private_data;
+
+ if (ut->sent_fec_eof)
+ return;
+ PARA_NOTICE_LOG("sending FEC EOF\n");
+ len = vss_get_fec_eof_packet(&buf);
+ /* Ignore write() errors since we are closing the target anyway. */
+ if (write(sc->fd, buf, len) == len)
+ ut->sent_fec_eof = true;
}
static void udp_delete_target(struct sender_client *sc, const char *msg)
static int udp_com_on(__a_unused struct sender_command_data *scd)
{
- sender_status = SENDER_ON;
+ sender_status = SENDER_on;
return 1;
}
static int udp_com_off(__a_unused struct sender_command_data *scd)
{
udp_shutdown_targets();
- sender_status = SENDER_OFF;
+ sender_status = SENDER_off;
return 1;
}
/** Initialize UDP session and set maximum payload size. */
static int udp_init_fec(struct sender_client *sc)
{
+ struct udp_target *ut = sc->private_data;
int mps;
PARA_NOTICE_LOG("sending to udp %s\n", sc->name);
+ ut->sent_fec_eof = false;
mps = generic_max_transport_msg_size(sc->fd) - sizeof(struct udphdr);
PARA_INFO_LOG("current MPS = %d bytes\n", mps);
return mps;
{
int ret;
- if (sender_status == SENDER_OFF)
+ if (sender_status == SENDER_off)
return;
if (len == 0)
return;
"status: %s\n"
"port: %s\n"
"targets: %s\n",
- (sender_status == SENDER_ON)? "on" : "off",
+ (sender_status == SENDER_on)? "on" : "off",
stringify_port(conf.udp_default_port_arg, "udp"),
tgts? tgts : "(none)"
);
s->post_select = NULL;
s->shutdown_clients = udp_shutdown_targets;
s->resolve_target = udp_resolve_target;
- s->client_cmds[SENDER_ON] = udp_com_on;
- s->client_cmds[SENDER_OFF] = udp_com_off;
- s->client_cmds[SENDER_DENY] = NULL;
- s->client_cmds[SENDER_ALLOW] = NULL;
- s->client_cmds[SENDER_ADD] = udp_com_add;
- s->client_cmds[SENDER_DELETE] = udp_com_delete;
- sender_status = SENDER_OFF;
+ s->client_cmds[SENDER_on] = udp_com_on;
+ s->client_cmds[SENDER_off] = udp_com_off;
+ s->client_cmds[SENDER_deny] = NULL;
+ s->client_cmds[SENDER_allow] = NULL;
+ s->client_cmds[SENDER_add] = udp_com_add;
+ s->client_cmds[SENDER_delete] = udp_com_delete;
+ sender_status = SENDER_off;
udp_init_target_list();
if (!conf.udp_no_autostart_given)
- sender_status = SENDER_ON;
+ sender_status = SENDER_on;
PARA_DEBUG_LOG("udp sender init complete\n");
}
static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data)
{
- char control[255], buf[8];
+ char control[255] __a_aligned(8), buf[8];
struct msghdr msg = {.msg_iov = NULL};
struct cmsghdr *cmsg;
struct iovec iov;
frequency ~ <num>
channels ~ <num>
num_played ~ <num>
+ image_id ~ <num>
+ lyrics_id ~ <num>
Takes a comparator ~ of the set {<, =, <=, >, >=, !=} and a number
<num>. Matches an audio file iff the condition <val> ~ <num> is
satisfied where val is the corresponding value of the audio file
-(value of the year tag, bitrate in kbit/s, frequency in Hz, channel
-count, play count).
+(value of the year tag, bitrate in kbit/s, etc.).
The year tag is special as its value is undefined if the audio file
has no year tag or the content of the year tag is not a number. Such