X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=mood.c;h=398713437997ee30034f739404ee640b0e389281;hb=c2245032a47304586180cb64bfa00dc6974467a7;hp=fbc22297fd0df3a2d1fb5b7927fde5b1a7825315;hpb=c88ccb7d91ea4baab78e24922d5c1d0cbaf6dcce;p=paraslash.git diff --git a/mood.c b/mood.c index fbc22297..39871343 100644 --- 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,9 +139,10 @@ 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; } @@ -258,10 +260,11 @@ static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd) static long compute_score(struct afs_info *afsi) { - 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); + const struct afs_statistics *stats = ¤t_mood->stats; + 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; } @@ -270,63 +273,65 @@ static int add_afs_statistics(const struct osl_row *row) uint64_t n, x, s, q; struct afs_info afsi; int ret; + struct afs_statistics *stats = ¤t_mood->stats; 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 = ¤t_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; } @@ -354,19 +359,18 @@ struct admissible_array { */ static int add_if_admissible(struct osl_row *aft_row, void *data) { + const struct afs_statistics *stats = ¤t_mood->stats; struct admissible_array *aa = data; - int ret; - 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; + aa->array[stats->num] = aft_row; return add_afs_statistics(aft_row); } @@ -412,26 +416,21 @@ _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 = ¤t_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) @@ -490,7 +489,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,10 +500,7 @@ 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) @@ -518,11 +515,8 @@ static int mood_update_audio_file(const struct osl_row *aft_row, 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; - } + if (old_afsi) + update_afs_statistics(old_afsi, &afsi); score = compute_score(&afsi); PARA_DEBUG_LOG("score: %li\n", score); percent = (score + 100) / 3; @@ -537,7 +531,8 @@ static int mood_update_audio_file(const struct osl_row *aft_row, /* sse: seconds since epoch. */ static void log_statistics(int64_t sse) { - unsigned n = statistics.num; + const struct afs_statistics *stats = ¤t_mood->stats; + unsigned n = stats->num; int mean_days, sigma_days; assert(current_mood); @@ -547,19 +542,19 @@ static void log_statistics(int64_t sse) 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,13 +566,11 @@ 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) { - struct afs_statistics *s = &statistics; + struct afs_statistics *s = ¤t_mood->stats; if (s->num > 0) { s->normalization_divisor = int_sqrt(s->last_played_qd) @@ -607,8 +600,7 @@ static void compute_correction_factors(int64_t sse) * * If there is already an open mood, it will be closed first. * - * \return Positive on success, negative on errors. Loading the dummy mood - * always succeeds. + * \return Positive on success, negative on errors. * * \sa struct \ref afs_info::last_played, \ref mp_eval_row(). */ @@ -663,7 +655,7 @@ int change_current_mood(const char *mood_name, char **errmsg) clock_get_realtime(&rnow); compute_correction_factors(rnow.tv_sec); log_statistics(rnow.tv_sec); - for (i = 0; i < statistics.num; i++) { + for (i = 0; i < current_mood->stats.num; i++) { ret = add_to_score_table(aa.array[i]); if (ret < 0) { if (errmsg) @@ -672,7 +664,7 @@ int change_current_mood(const char *mood_name, char **errmsg) goto out; } } - ret = statistics.num; + ret = current_mood->stats.num; out: free(aa.array); if (ret < 0) @@ -686,19 +678,16 @@ 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) @@ -716,9 +705,17 @@ static int reload_current_mood(void) * \param pb Unused. * \param data Its type depends on the event. * - * 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. + * This function updates the score table according to the event that has + * occurred. Two actions are possible: (a) reload the current mood, or (b) + * add/remove/update the row of the score table which corresponds to the audio + * file that has been modified or whose afs info has been changed. It depends + * on the type of the event which action (if any) is performed. + * + * The callbacks of command handlers such as com_add() or com_touch() which + * modify the audio file table call this function. The virtual streaming system + * also calls this after it has updated the afs info of the file it is about to + * stream (the one with the highest score). If the file stays admissible, its + * score is recomputed so that a different file is picked next time. * * \return Standard. */