X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=mood.c;h=83c3a574a53c5658afe43a296ae2de9f2c368873;hp=d9ae48bf967e61b0c4c0ca499d75f0b47eb47b96;hb=5ff80e887a99a192bab3339c4339f8202733f031;hpb=c5670af5c1f784fe6c3d46d0281f2ba7c47af75e diff --git a/mood.c b/mood.c index d9ae48bf..83c3a574 100644 --- a/mood.c +++ b/mood.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2011 Andre Noll + * Copyright (C) 2007 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -17,6 +17,8 @@ #include "list.h" #include "ipc.h" #include "mm.h" +#include "sideband.h" +#include "mood.h" /** * Contains statistical data of the currently admissible audio files. @@ -28,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; @@ -76,6 +78,10 @@ struct mood { struct list_head score_list; }; +/* + * If current_mood is NULL then no mood is currently open. If + * current_mood->name is NULL, the dummy mood is currently open + */ static struct mood *current_mood; /** @@ -107,12 +113,16 @@ __a_const static uint64_t int_sqrt(uint64_t x) return res; } -/* returns 1 if row matches score item, 0 if not. */ -static int get_item_score(struct mood_item *item, const struct afs_info *afsi, +/* + * Returns true if row matches, false if it does not match. In any case score + * and score_arg_sum are set/increased accordingly. + */ +static bool get_item_score(struct mood_item *item, const struct afs_info *afsi, const struct afh_info *afhi, const char *path, long *score, long *score_arg_sum) { - int ret, match = 1; + int ret; + bool match = true; *score_arg_sum += item->random_score? 100 : PARA_ABS(item->score_arg); ret = 100; @@ -120,7 +130,7 @@ static int get_item_score(struct mood_item *item, const struct afs_info *afsi, ret = item->method->score_function(path, afsi, afhi, item->parser_data); if ((ret < 0 && !item->logical_not) || (ret >= 0 && item->logical_not)) - match = 0; /* no match */ + match = false; } if (item->random_score) *score = PARA_ABS(ret) * para_random(100); @@ -134,7 +144,8 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, long *result) { struct mood_item *item; - int ret, match = 0; + int ret; + bool match; long score_arg_sum = 0, score = 0, item_score; struct afs_info afsi; struct afh_info afhi; @@ -153,27 +164,29 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, return ret; /* reject audio file if it matches any entry in the deny list */ list_for_each_entry(item, &m->deny_list, mood_item_node) { - ret = get_item_score(item, &afsi, &afhi, path, &item_score, + match = get_item_score(item, &afsi, &afhi, path, &item_score, &score_arg_sum); - if (ret > 0) /* not admissible */ + if (match) /* not admissible */ return 0; score += item_score; } + match = false; list_for_each_entry(item, &m->accept_list, mood_item_node) { ret = get_item_score(item, &afsi, &afhi, path, &item_score, &score_arg_sum); if (ret == 0) continue; - match = 1; + match = true; score += item_score; } /* reject if there is no matching entry in the accept list */ if (!match && !list_empty(&m->accept_list)) return 0; list_for_each_entry(item, &m->score_list, mood_item_node) { - ret = get_item_score(item, &afsi, &afhi, path, &item_score, + match = get_item_score(item, &afsi, &afhi, path, &item_score, &score_arg_sum); - score += item_score; + if (match) + score += item_score; } if (score_arg_sum) score /= score_arg_sum; @@ -210,7 +223,8 @@ static void destroy_mood(struct mood *m) static struct mood *alloc_new_mood(const char *name) { struct mood *m = para_calloc(sizeof(struct mood)); - m->name = para_strdup(name); + if (name) + m->name = para_strdup(name); INIT_LIST_HEAD(&m->accept_list); INIT_LIST_HEAD(&m->deny_list); INIT_LIST_HEAD(&m->score_list); @@ -364,14 +378,16 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m) char *mood_name; struct osl_object mood_def; struct mood_line_parser_data mlpd = {.line_num = 0}; - int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def); + int ret; + *m = NULL; + ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def); if (ret < 0) return ret; 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) { @@ -394,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; @@ -416,77 +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 = &fd, - .max_size_handler = pass_buffer_as_shm - }; - - 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(pb.buf, pb.offset, &fd); - 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; @@ -496,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; @@ -547,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. */ @@ -630,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. * @@ -647,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) @@ -673,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); } @@ -754,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) @@ -812,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 = { @@ -824,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)); @@ -837,9 +820,9 @@ int change_current_mood(char *mood_name) return ret; close_current_mood(); current_mood = m; - } else { + } else { /* load dummy mood */ close_current_mood(); - current_mood = alloc_new_mood("dummy"); + current_mood = alloc_new_mood(NULL); } aa.m = current_mood; PARA_NOTICE_LOG("computing statistics of admissible files\n"); @@ -847,14 +830,14 @@ 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); if (ret < 0) goto out; } - PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name); + PARA_NOTICE_LOG("loaded mood %s\n", mood_name? mood_name : "(dummy)"); ret = statistics.num; out: free(aa.array); @@ -878,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? @@ -900,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) { @@ -918,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: