]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - mood.c
Clean up and rename change_current_mood().
[paraslash.git] / mood.c
diff --git a/mood.c b/mood.c
index b3f007b68fe07c46f500d1aaa7dffdfe9503f451..db0dfaeec53602eb31fa3e58991fa078d451f73b 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -47,18 +47,27 @@ struct afs_statistics {
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics = {.normalization_divisor = 1};
 
+/**
+ * Stores an instance of an open mood (parser and statistics).
+ *
+ * A structure of this type is allocated and initialized at mood open time.
+ */
 struct mood {
-       /** The name of this mood. */
+       /** NULL means that this is the "dummy" mood. */
        char *name;
-       /** Info for the bison parser. */
+       /** Bison's abstract syntax tree, used to determine admissibility. */
        struct mp_context *parser_context;
+       /** To compute the score. */
+       struct afs_statistics stats;
 };
 
 /*
  * If current_mood is NULL then no mood is currently open. If
  * current_mood->name is NULL, the dummy mood is currently open.
+ *
+ * The statistics are adjusted dynamically through this pointer as files are
+ * added, removed or played.
  */
 static struct mood *current_mood;
 
@@ -119,14 +128,6 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
-/* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
-{
-       if (!m)
-               return -E_NO_MOOD;
-       return mp_eval_row(aft_row, m->parser_context);
-}
-
 static void destroy_mood(struct mood *m)
 {
        if (!m)
@@ -138,30 +139,32 @@ static void destroy_mood(struct mood *m)
 
 static struct mood *alloc_new_mood(const char *name)
 {
-       struct mood *m = para_calloc(sizeof(struct mood));
+       struct mood *m = zalloc(sizeof(struct mood));
        if (name)
                m->name = para_strdup(name);
+       m->stats.normalization_divisor = 1;
        return m;
 }
 
-static int load_mood(const struct osl_row *mood_row, struct mood **m,
-               char **errmsg)
+static int init_mood_parser(const char *mood_name, struct mood **m, char **err)
 {
-       char *mood_name;
        struct osl_object mood_def;
        int ret;
 
-       ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
+       if (!*mood_name) {
+               if (err)
+                       *err = make_message("empty mood name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       }
+       ret = mood_get_def_by_name(mood_name, &mood_def);
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message(
-                               "could not read mood definition");
+               if (err)
+                       *err = make_message("could not read mood definition\n");
                return ret;
        }
-       assert(*mood_name);
        *m = alloc_new_mood(mood_name);
        PARA_INFO_LOG("opening mood %s\n", mood_name);
-       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, err);
        osl_close_disk_object(&mood_def);
        if (ret < 0)
                destroy_mood(*m);
@@ -256,16 +259,18 @@ static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
        return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
 }
 
-static long compute_score(struct afs_info *afsi)
+static long compute_score(struct afs_info *afsi,
+               const struct afs_statistics *stats)
 {
-       long score = -normalized_value(afsi->num_played, statistics.num,
-               statistics.num_played_sum, statistics.num_played_qd);
-       score -= normalized_value(afsi->last_played, statistics.num,
-               statistics.last_played_sum, statistics.last_played_qd);
+       long score = -normalized_value(afsi->num_played, stats->num,
+               stats->num_played_sum, stats->num_played_qd);
+       score -= normalized_value(afsi->last_played, stats->num,
+               stats->last_played_sum, stats->last_played_qd);
        return score / 2;
 }
 
-static int add_afs_statistics(const struct osl_row *row)
+static int add_afs_statistics(const struct osl_row *row,
+               struct afs_statistics *stats)
 {
        uint64_t n, x, s, q;
        struct afs_info afsi;
@@ -274,59 +279,60 @@ static int add_afs_statistics(const struct osl_row *row)
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        x = afsi.last_played;
-       s = statistics.last_played_sum;
+       s = stats->last_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.last_played_qd += q * q * n / (n + 1);
+               stats->last_played_qd += q * q * n / (n + 1);
        }
-       statistics.last_played_sum += x;
+       stats->last_played_sum += x;
 
        x = afsi.num_played;
-       s = statistics.num_played_sum;
+       s = stats->num_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.num_played_qd += q * q * n / (n + 1);
+               stats->num_played_qd += q * q * n / (n + 1);
        }
-       statistics.num_played_sum += x;
-       statistics.num++;
+       stats->num_played_sum += x;
+       stats->num++;
        return 1;
 }
 
 static int del_afs_statistics(const struct osl_row *row)
 {
+       struct afs_statistics *stats = &current_mood->stats;
        uint64_t n, s, q, a, new_s;
        struct afs_info afsi;
        int ret;
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        assert(n);
        if (n == 1) {
-               memset(&statistics, 0, sizeof(statistics));
-               statistics.normalization_divisor = 1;
+               memset(stats, 0, sizeof(*stats));
+               stats->normalization_divisor = 1;
                return 1;
        }
 
-       s = statistics.last_played_sum;
-       q = statistics.last_played_qd;
+       s = stats->last_played_sum;
+       q = stats->last_played_qd;
        a = afsi.last_played;
        new_s = s - a;
-       statistics.last_played_sum = new_s;
-       statistics.last_played_qd = q + s * s / n - a * a
+       stats->last_played_sum = new_s;
+       stats->last_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
-       s = statistics.num_played_sum;
-       q = statistics.num_played_qd;
+       s = stats->num_played_sum;
+       q = stats->num_played_qd;
        a = afsi.num_played;
        new_s = s - a;
-       statistics.num_played_sum = new_s;
-       statistics.num_played_qd = q + s * s / n - a * a
+       stats->num_played_sum = new_s;
+       stats->num_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
-       statistics.num--;
+       stats->num--;
        return 1;
 }
 
@@ -355,19 +361,18 @@ struct admissible_array {
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
-       int ret;
+       struct afs_statistics *stats = &aa->m->stats;
 
-       ret = row_is_admissible(aft_row, aa->m);
-       if (ret <= 0)
-               return ret;
-       if (statistics.num >= aa->size) {
+       if (!mp_eval_row(aft_row, aa->m->parser_context))
+               return 0;
+       if (stats->num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
-               aa->array = para_realloc(aa->array,
-                       aa->size * sizeof(struct osl_row *));
+               aa->array = arr_realloc(aa->array, aa->size,
+                       sizeof(struct osl_row *));
        }
-       aa->array[statistics.num] = aft_row;
-       return add_afs_statistics(aft_row);
+       aa->array[stats->num] = aft_row;
+       return add_afs_statistics(aft_row, stats);
 }
 
 /**
@@ -412,29 +417,25 @@ _static_inline_ int64_t update_quadratic_deviation(int64_t n, int64_t old_qd,
        return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
 }
 
-static int update_afs_statistics(struct afs_info *old_afsi,
+static void update_afs_statistics(struct afs_info *old_afsi,
                struct afs_info *new_afsi)
 {
-       unsigned n;
-       int ret = get_num_admissible_files(&n);
-
-       if (ret < 0)
-               return ret;
-       assert(n);
-
-       statistics.last_played_qd = update_quadratic_deviation(n,
-               statistics.last_played_qd, old_afsi->last_played,
-               new_afsi->last_played, statistics.last_played_sum);
-       statistics.last_played_sum += new_afsi->last_played - old_afsi->last_played;
-
-       statistics.num_played_qd = update_quadratic_deviation(n,
-               statistics.num_played_qd, old_afsi->num_played,
-               new_afsi->num_played, statistics.num_played_sum);
-       statistics.num_played_sum += new_afsi->num_played - old_afsi->num_played;
-       return 1;
+       struct afs_statistics *stats = &current_mood->stats;
+
+       assert(stats->num > 0);
+       stats->last_played_qd = update_quadratic_deviation(stats->num,
+               stats->last_played_qd, old_afsi->last_played,
+               new_afsi->last_played, stats->last_played_sum);
+       stats->last_played_sum += new_afsi->last_played - old_afsi->last_played;
+
+       stats->num_played_qd = update_quadratic_deviation(stats->num,
+               stats->num_played_qd, old_afsi->num_played,
+               new_afsi->num_played, stats->num_played_sum);
+       stats->num_played_sum += new_afsi->num_played - old_afsi->num_played;
 }
 
-static int add_to_score_table(const struct osl_row *aft_row)
+static int add_to_score_table(const struct osl_row *aft_row,
+               const struct afs_statistics *stats)
 {
        long score;
        struct afs_info afsi;
@@ -442,7 +443,7 @@ static int add_to_score_table(const struct osl_row *aft_row)
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi);
+       score = compute_score(&afsi, stats);
        return score_add(aft_row, score);
 }
 
@@ -490,7 +491,8 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                struct afs_info *old_afsi)
 {
        long score, percent;
-       int ret, is_admissible, was_admissible = 0;
+       int ret;
+       bool is_admissible, was_admissible;
        struct afs_info afsi;
        unsigned rank;
 
@@ -500,30 +502,24 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
        if (ret < 0)
                return ret;
        was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood);
-       if (ret < 0)
-               return ret;
-       is_admissible = (ret > 0);
+       is_admissible = mp_eval_row(aft_row, current_mood->parser_context);
        if (!was_admissible && !is_admissible)
                return 1;
        if (was_admissible && !is_admissible)
                return delete_from_statistics_and_score_table(aft_row);
        if (!was_admissible && is_admissible) {
-               ret = add_afs_statistics(aft_row);
+               ret = add_afs_statistics(aft_row, &current_mood->stats);
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row);
+               return add_to_score_table(aft_row, &current_mood->stats);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
        if (ret < 0)
                return ret;
-       if (old_afsi) {
-               ret = update_afs_statistics(old_afsi, &afsi);
-               if (ret < 0)
-                       return ret;
-       }
-       score = compute_score(&afsi);
+       if (old_afsi)
+               update_afs_statistics(old_afsi, &afsi);
+       score = compute_score(&afsi, &current_mood->stats);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
@@ -535,31 +531,28 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
 }
 
 /* sse: seconds since epoch. */
-static void log_statistics(int64_t sse)
+static void log_statistics(struct afs_statistics *stats, int64_t sse)
 {
-       unsigned n = statistics.num;
+       unsigned n = stats->num;
        int mean_days, sigma_days;
 
-       assert(current_mood);
-       PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
-               current_mood->name : "(dummy)");
        if (!n) {
                PARA_WARNING_LOG("no admissible files\n");
                return;
        }
-       PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
-       sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
+       PARA_NOTICE_LOG("%u admissible files\n", stats->num);
+       mean_days = (sse - stats->last_played_sum / n) / 3600 / 24;
+       sigma_days = int_sqrt(stats->last_played_qd / n) / 3600 / 24;
        PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
        PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
-               statistics.num_played_sum / n,
-               int_sqrt(statistics.num_played_qd / n));
+               stats->num_played_sum / n,
+               int_sqrt(stats->num_played_qd / n));
        PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
-               statistics.num_played_correction);
+               stats->num_played_correction);
        PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
-               statistics.last_played_correction);
+               stats->last_played_correction);
        PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
-               statistics.normalization_divisor);
+               stats->normalization_divisor);
 }
 
 /**
@@ -571,14 +564,10 @@ void close_current_mood(void)
 {
        destroy_mood(current_mood);
        current_mood = NULL;
-       memset(&statistics, 0, sizeof(statistics));
-       statistics.normalization_divisor = 1;
 }
 
-static void compute_correction_factors(int64_t sse)
+static void compute_correction_factors(int64_t sse, struct afs_statistics *s)
 {
-       struct afs_statistics *s = &statistics;
-
        if (s->num > 0) {
                s->normalization_divisor = int_sqrt(s->last_played_qd)
                        * int_sqrt(s->num_played_qd) / s->num / 100;
@@ -611,13 +600,10 @@ static void compute_correction_factors(int64_t sse)
  *
  * \sa struct \ref afs_info::last_played, \ref mp_eval_row().
  */
-int change_current_mood(const char *mood_name, char **errmsg)
+int mood_switch(const char *mood_name, char **errmsg)
 {
        int i, ret;
-       struct admissible_array aa = {
-               .size = 0,
-               .array = NULL
-       };
+       struct admissible_array aa = {.size = 0};
        /*
         * We can not use the "now" pointer from sched.c here because we are
         * called before schedule(), which initializes "now".
@@ -625,33 +611,11 @@ int change_current_mood(const char *mood_name, char **errmsg)
        struct timeval rnow;
 
        if (mood_name) {
-               struct mood *m;
-               struct osl_row *row;
-               struct osl_object obj;
-
-               if (!*mood_name) {
-                       *errmsg = make_message("empty mood name");
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               obj.data = (char *)mood_name;
-               obj.size = strlen(mood_name) + 1;
-               ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
-               if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message("no such mood: %s",
-                                       mood_name);
-                       return ret;
-               }
-               ret = load_mood(row, &m, errmsg);
+               ret = init_mood_parser(mood_name, &aa.m, errmsg);
                if (ret < 0)
                        return ret;
-               close_current_mood();
-               current_mood = m;
-       } else { /* load dummy mood */
-               close_current_mood();
-               current_mood = alloc_new_mood(NULL);
-       }
-       aa.m = current_mood;
+       } else /* load dummy mood */
+               aa.m = alloc_new_mood(NULL);
        PARA_NOTICE_LOG("computing statistics of admissible files\n");
        ret = audio_file_loop(&aa, add_if_admissible);
        if (ret < 0) {
@@ -660,10 +624,10 @@ int change_current_mood(const char *mood_name, char **errmsg)
                goto out;
        }
        clock_get_realtime(&rnow);
-       compute_correction_factors(rnow.tv_sec);
-       log_statistics(rnow.tv_sec);
-       for (i = 0; i < statistics.num; i++) {
-               ret = add_to_score_table(aa.array[i]);
+       compute_correction_factors(rnow.tv_sec, &aa.m->stats);
+       log_statistics(&aa.m->stats, rnow.tv_sec);
+       for (i = 0; i < aa.m->stats.num; i++) {
+               ret = add_to_score_table(aa.array[i], &aa.m->stats);
                if (ret < 0) {
                        if (errmsg)
                                *errmsg = make_message(
@@ -671,11 +635,15 @@ int change_current_mood(const char *mood_name, char **errmsg)
                        goto out;
                }
        }
-       ret = statistics.num;
+       /* success */
+       close_current_mood();
+       current_mood = aa.m;
+       PARA_NOTICE_LOG("loaded mood %s\n", mood_name? mood_name : "(dummy)");
+       ret = aa.m->stats.num;
 out:
        free(aa.array);
        if (ret < 0)
-               close_current_mood();
+               destroy_mood(aa.m);
        return ret;
 }
 
@@ -685,25 +653,22 @@ out:
  * This function is called on events which render the current list of
  * admissible files useless, for example if an attribute is removed from the
  * attribute table.
- *
- * If no mood is currently open, the function returns success.
  */
 static int reload_current_mood(void)
 {
        int ret;
        char *mood_name = NULL;
 
+       assert(current_mood);
        ret = clear_score_table();
        if (ret < 0)
                return ret;
-       if (!current_mood)
-               return 1;
        PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
                current_mood->name : "(dummy)");
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
        close_current_mood();
-       ret = change_current_mood(mood_name, NULL);
+       ret = mood_switch(mood_name, NULL);
        free(mood_name);
        return ret;
 }