Fix documentation of check_filter_arg().
[paraslash.git] / mood.c
diff --git a/mood.c b/mood.c
index 74b00d35b2ddc4efd07f2e8783d1a37fcc3117b4..83c3a574a53c5658afe43a296ae2de9f2c368873 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2012 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -18,6 +18,7 @@
 #include "ipc.h"
 #include "mm.h"
 #include "sideband.h"
+#include "mood.h"
 
 /**
  * Contains statistical data of the currently admissible audio files.
@@ -29,7 +30,7 @@ struct afs_statistics {
        int64_t num_played_sum;
        /** Sum of last played times over all admissible files. */
        int64_t last_played_sum;
-       /** Quadratic deviation of num played time. */
+       /** Quadratic deviation of num played count. */
        int64_t num_played_qd;
        /** Quadratic deviation of last played time. */
        int64_t last_played_qd;
@@ -386,7 +387,7 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m)
        if (!*mood_name)
                return -E_DUMMY_ROW;
        mlpd.m = alloc_new_mood(mood_name);
-       ret = for_each_line_ro(mood_def.data, mood_def.size,
+       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        osl_close_disk_object(&mood_def);
        if (ret < 0) {
@@ -409,20 +410,18 @@ static int check_mood(struct osl_row *mood_row, void *data)
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
-               para_printf(pb, "failed to get mood definition: %s\n",
-                       para_strerror(-ret));
+               para_printf(pb, "cannot read mood\n");
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       ret = para_printf(pb, "checking mood %s...\n", mood_name);
-       if (ret < 0)
-               goto out;
-       ret = for_each_line_ro(mood_def.data, mood_def.size,
+       para_printf(pb, "checking mood %s...\n", mood_name);
+       ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
        if (ret < 0)
-               para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
-                       para_strerror(-ret));
+               para_printf(pb, "mood %s: error in line %u: %s\n", mood_name,
+                       mlpd.line_num, para_strerror(-ret));
+       ret = 1; /* don't fail the loop on invalid mood definitions */
 out:
        osl_close_disk_object(&mood_def);
        return ret;
@@ -431,80 +430,37 @@ out:
 /**
  * Check all moods for syntax errors.
  *
- * \param fd The afs socket.
- * \param query Unused.
+ * \param aca Only ->pbout is used for diagnostics.
+ *
+ * \return Negative on fatal errors. Inconsistent mood definitions are not
+ * considered an error.
  */
-void mood_check_callback(int fd, __a_unused const struct osl_object *query)
-{
-       struct para_buffer pb = {
-               .max_size = shm_get_shmmax(),
-               .private_data = &(struct afs_max_size_handler_data) {
-                       .fd = fd,
-                       .band = SBD_OUTPUT
-               },
-               .max_size_handler = afs_max_size_handler
-       };
-
-       int ret = para_printf(&pb, "checking moods...\n");
-       if (ret < 0)
-               return;
-       osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb,
-               check_mood);
-       if (pb.offset)
-               pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
-       free(pb.buf);
-}
-
-#if 0
-static unsigned int_log2(uint64_t x)
+int mood_check_callback(struct afs_callback_arg *aca)
 {
-       unsigned res = 0;
-
-       while (x) {
-               x /= 2;
-               res++;
-       }
-       return res;
+       para_printf(&aca->pbout, "checking moods...\n");
+       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, &aca->pbout,
+               check_mood));
 }
-#endif
 
 static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
 {
        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_num_played_score(struct afs_info *afsi)
+static long compute_score(struct afs_info *afsi, long mood_score)
 {
-       return -normalized_value(afsi->num_played, statistics.num,
+       mood_score -= normalized_value(afsi->num_played, statistics.num,
                statistics.num_played_sum, statistics.num_played_qd);
-}
-
-static long compute_last_played_score(struct afs_info *afsi)
-{
-       return -normalized_value(afsi->last_played, statistics.num,
+       mood_score -= normalized_value(afsi->last_played, statistics.num,
                statistics.last_played_sum, statistics.last_played_qd);
-}
-
-static long compute_dynamic_score(const struct osl_row *aft_row)
-{
-       struct afs_info afsi;
-       int64_t score, nscore = 0, lscore = 0;
-       int ret;
-
-       ret = get_afsi_of_row(aft_row, &afsi);
-       if (ret < 0)
-               return -100;
-       nscore = compute_num_played_score(&afsi);
-       lscore = compute_last_played_score(&afsi);
-       score = nscore + lscore;
-       return score;
+       return mood_score / 3;
 }
 
 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;
 
@@ -514,14 +470,18 @@ static int add_afs_statistics(const struct osl_row *row)
        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;
@@ -565,16 +525,16 @@ static int del_afs_statistics(const struct osl_row *row)
 /**
  * Structure used during mood_open().
  *
- * At mood open time, we look at each file in the audio file table in order to
- * determine whether it is admissible. If a file happens to be admissible, its
- * mood score is computed by calling each relevant mood_score_function. Next,
- * we update the afs_statistics and add a struct admissible_file_info to a
- * temporary array.
+ * At mood open time we determine the set of admissible files for the given
+ * mood. The mood score of each admissible file is computed by adding up all
+ * mood item scores. Next, we update the afs statistics and append a struct
+ * admissible_file_info to a temporary array.
  *
- * If all files have been processed that way, the final score of each
+ * When all files have been processed in this way, the final score of each
  * admissible file is computed by adding the dynamic score (which depends on
- * the afs_statistics) to the mood score.  Finally, all audio files in the
- * array are added to the score table and the admissible array is freed.
+ * the afs_statistics and the current time) to the mood score. Finally, all
+ * audio files in the temporary array are added to the score table and the
+ * array is freed.
  *
  * \sa mood_method, admissible_array.
  */
@@ -648,7 +608,8 @@ static int add_if_admissible(struct osl_row *aft_row, void *data)
  * 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.
  *
@@ -665,7 +626,7 @@ _static_inline_ int64_t update_quadratic_deviation(int64_t n, int64_t old_qd,
 {
        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)
@@ -691,7 +652,13 @@ static int update_afs_statistics(struct afs_info *old_afsi, struct afs_info *new
 
 static int add_to_score_table(const struct osl_row *aft_row, long mood_score)
 {
-       long score = (compute_dynamic_score(aft_row) + mood_score) / 3;
+       long score;
+       struct afs_info afsi;
+       int ret = get_afsi_of_row(aft_row, &afsi);
+
+       if (ret < 0)
+               return ret;
+       score = compute_score(&afsi, mood_score);
        return score_add(aft_row, score);
 }
 
@@ -772,9 +739,7 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                if (ret < 0)
                        return ret;
        }
-       score += compute_num_played_score(&afsi);
-       score += compute_last_played_score(&afsi);
-       score /= 3;
+       score = compute_score(&afsi, score);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@ -830,7 +795,7 @@ void close_current_mood(void)
  * \sa struct admissible_file_info, struct admissible_array, struct
  * afs_info::last_played, mood_close().
  */
-int change_current_mood(char *mood_name)
+int change_current_mood(const char *mood_name)
 {
        int i, ret;
        struct admissible_array aa = {
@@ -842,7 +807,7 @@ int change_current_mood(char *mood_name)
                struct mood *m;
                struct osl_row *row;
                struct osl_object obj = {
-                       .data = mood_name,
+                       .data = (char *)mood_name,
                        .size = strlen(mood_name) + 1
                };
                ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
@@ -865,7 +830,7 @@ int change_current_mood(char *mood_name)
        if (ret < 0)
                return ret;
        log_statistics();
-       PARA_INFO_LOG("%d admissible files \n", statistics.num);
+       PARA_INFO_LOG("%d admissible files\n", statistics.num);
        for (i = 0; i < statistics.num; i++) {
                struct admissible_file_info *a = aa.array + i;
                ret = add_to_score_table(a->aft_row, a->score);
@@ -896,6 +861,9 @@ static int reload_current_mood(void)
        int ret;
        char *mood_name = NULL;
 
+       ret = clear_score_table();
+       if (ret < 0)
+               return ret;
        if (!current_mood)
                return 1;
        PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
@@ -918,12 +886,12 @@ static int reload_current_mood(void)
  * This function performs actions required due to the occurrence of the given
  * event. Possible actions include reload of the current mood and update of the
  * score of an audio file.
+ *
+ * \return Standard.
  */
 int moods_event_handler(enum afs_events event, __a_unused struct para_buffer *pb,
                void *data)
 {
-       int ret;
-
        if (!current_mood)
                return 0;
        switch (event) {
@@ -936,10 +904,6 @@ int moods_event_handler(enum afs_events event, __a_unused struct para_buffer *pb
        case BLOB_ADD:
                if (data == moods_table || data == playlists_table)
                        return 1; /* no reload necessary for these */
-               ret = clear_score_table();
-               if (ret < 0)
-                       PARA_CRIT_LOG("clear score table returned %s\n",
-                               para_strerror(-ret));
                return reload_current_mood();
        /* these also require reload of the score table */
        case ATTRIBUTE_ADD: