X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=mood.c;h=fe8ee8ad9915cdc3db089a8f8584883537963111;hp=08c2c222e84f69bd794ea7c4598b83fe0051ac06;hb=3010ef96e10cb15d423eef8f9802fbed78744393;hpb=8ee99fd28639d1b2aeee1312cc15388565a40280 diff --git a/mood.c b/mood.c index 08c2c222..fe8ee8ad 100644 --- a/mood.c +++ b/mood.c @@ -1,18 +1,22 @@ /* - * Copyright (C) 2007 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file mood.c Paraslash's mood handling functions. */ -#include +#include +#include + #include "para.h" #include "error.h" #include "string.h" #include "afh.h" #include "afs.h" #include "list.h" +#include "ipc.h" +#include "mm.h" /** * Contains statistical data of the currently admissible audio files. @@ -31,67 +35,7 @@ struct afs_statistics { /** Number of admissible files */ unsigned num; }; -struct afs_statistics statistics; - -/** - * Assign scores according to a mood_method. - * - * Each mood_method has its own mood_score_function. The first three parameters - * passed to that function are informations about the audio file whose score is - * to be computed. The data argument depends on the mood method this function - * is used for. It usually is the argument given at the end of a mood line. - * - * Mood score functions must return values between -100 and +100 inclusively. - * Boolean score functions should always return either -100 or +100. - * - * \sa struct mood_method, mood_parser. - */ -typedef int mood_score_function(const char *path, const struct afs_info *afsi, - const struct audio_format_info *afhi, const void *data); - -/** - * Pre-process a mood line. - * - * The mood_parser of a mood_method is called once at mood open time for each - * line of the current mood definition that contains the mood_method's name as - * a keyword. The line is passed to the mood_parser as the first argument. The - * mood_parser must determine whether the line is syntactically correct and - * return a positive value if so and a negative value otherwise. - * - * Some mood parsers pre-process the data given in the mood line to compute a - * structure which depends of the particular mood_method and which is used - * later in the mood_score_function of the mood_method. The mood_parser may - * store a pointer to its structure via the second argument. - * - * \sa mood_open(), mood_cleanup_function, mood_score_function. - */ -typedef int mood_parser(const char *, void **); - -/** - * Deallocate resources which were allocated by the mood_parser. - * - * This optional function of a mood_method is used to free any resources - * allocated in mood_open() by the mood_parser. The argument passed is a - * pointer to the mood_method specific data structure that was returned by the - * mood_parser. - * - * \sa mood_parser. - */ -typedef void mood_cleanup_function(void *); - -/** - * Used for scoring and to determine whether a file is admissible. - */ -struct mood_method { - /* The name of the method. */ - const char *name; - /** Pointer to the mood parser. */ - mood_parser *parser; - /** Pointer to the score function */ - mood_score_function *score_function; - /** Optional cleanup function. */ - mood_cleanup_function *cleanup; -}; +static struct afs_statistics statistics; /** * Each line of the current mood corresponds to a mood_item. @@ -163,167 +107,80 @@ static uint64_t int_sqrt(uint64_t x) return res; } -static int mm_no_attributes_set_parser(const char *arg, __a_unused void **ignored) -{ - if (arg && *arg) - PARA_WARNING_LOG("ignored junk at eol: %s\n", arg); - return 1; -} - -static int mm_no_attributes_set_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct audio_format_info *afhi, - __a_unused const void *data) -{ - if (!afsi->attributes) - return 100; - return -100; -} - -static int mm_played_rarely_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct audio_format_info *afhi, - __a_unused const void *data) -{ - unsigned num; - int ret = get_num_admissible_files(&num); - - if (ret < 0) - return 0; - if (statistics.num_played_sum - num * afsi->num_played - > int_sqrt(statistics.num_played_qd * num)) - return 100; - return -100; -} - -static int mm_played_rarely_parser(const char *arg, __a_unused void **ignored) -{ - if (arg && *arg) - PARA_WARNING_LOG("ignored junk at eol: %s\n", arg); - return 1; -} - -static int mm_name_like_score_function(const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct audio_format_info *afhi, - const void *data) -{ - if (fnmatch(data, path, 0)) - return -100; - return 100; -} - -static int mm_name_like_parser(const char *arg, void **data) -{ - *data = para_strdup(arg); - return 1; -} - -static void mm_name_like_cleanup(void *data) -{ - free(data); -} - -static int mm_is_set_parser(const char *arg, void **bitnum) -{ - unsigned char *c = para_malloc(1); - int ret = get_attribute_bitnum_by_name(arg, c); - - if (ret >= 0) - *bitnum = c; - else - free(c); - return ret; -} - -static int mm_is_set_score_function(__a_unused const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct audio_format_info *afhi, - const void *data) -{ - const unsigned char *bn = data; - if (afsi->attributes & (1ULL << *bn)) - return 100; - return -100; -} - -/* returns 1 if row matches score item, negative otherwise */ -static int add_item_score(const struct osl_row *row, struct mood_item *item, long *score, +/* returns 1 if row matches score item, 0 if not. */ +static int 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) { - struct afs_info afsi; - struct audio_format_info afhi; - char *path; - int ret; + int ret, match = 1; *score_arg_sum += item->random_score? 100 : PARA_ABS(item->score_arg); ret = 100; if (item->method) { - ret = get_afsi_of_row(row, &afsi); - if (ret< 0) - return ret; - ret = get_afhi_of_row(row, &afhi); - if (ret< 0) - return ret; - ret = get_audio_file_path_of_row(row, &path); - if (ret< 0) - return ret; - ret = item->method->score_function(path, &afsi, &afhi, + ret = item->method->score_function(path, afsi, afhi, item->parser_data); if ((ret < 0 && !item->logical_not) || (ret >= 0 && item->logical_not)) - return -1; /* no match */ + match = 0; /* no match */ } if (item->random_score) - *score += PARA_ABS(ret) * para_random(100); + *score = PARA_ABS(ret) * para_random(100); else - *score += PARA_ABS(ret) * item->score_arg; - return 1; + *score = PARA_ABS(ret) * item->score_arg; + return match; } +/* returns 1 if row admissible, 0 if not, negative on errors */ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, long *result) { struct mood_item *item; - int match = 0; - long score_arg_sum = 0, score = 0; + int ret, match = 0; + long score_arg_sum = 0, score = 0, item_score; + struct afs_info afsi; + struct afh_info afhi; + char *path; if (!m) return -E_NO_MOOD; + ret = get_afsi_of_row(aft_row, &afsi); + if (ret< 0) + return ret; + ret = get_afhi_of_row(aft_row, &afhi); + if (ret< 0) + return ret; + ret = get_audio_file_path_of_row(aft_row, &path); + if (ret< 0) + 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) - if (add_item_score(aft_row, item, &score, &score_arg_sum) > 0) - return -E_NOT_ADMISSIBLE; - list_for_each_entry(item, &m->accept_list, mood_item_node) - if (add_item_score(aft_row, item, &score, &score_arg_sum) > 0) - match = 1; + list_for_each_entry(item, &m->deny_list, mood_item_node) { + ret = get_item_score(item, &afsi, &afhi, path, &item_score, + &score_arg_sum); + if (ret > 0) /* not admissible */ + return 0; + score += item_score; + } + 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; + score += item_score; + } /* reject if there is no matching entry in the accept list */ if (!match && !list_empty(&m->accept_list)) - return -E_NOT_ADMISSIBLE; - list_for_each_entry(item, &m->score_list, mood_item_node) - add_item_score(aft_row, item, &score, &score_arg_sum); + return 0; + list_for_each_entry(item, &m->score_list, mood_item_node) { + ret = get_item_score(item, &afsi, &afhi, path, &item_score, + &score_arg_sum); + score += item_score; + } if (score_arg_sum) score /= score_arg_sum; *result = score; return 1; } -#define DEFINE_MOOD_METHOD(_name) \ -.parser = mm_ ## _name ## _parser, \ -.score_function = mm_ ## _name ## _score_function, \ -.name = #_name - -#define DEFINE_MOOD_METHOD_WITH_CLEANUP(_name) \ - DEFINE_MOOD_METHOD(_name), \ - .cleanup = mm_ ## _name ## _cleanup - -static const struct mood_method mood_methods[] = { - {DEFINE_MOOD_METHOD(no_attributes_set)}, - {DEFINE_MOOD_METHOD(played_rarely)}, - {DEFINE_MOOD_METHOD(is_set)}, - {DEFINE_MOOD_METHOD_WITH_CLEANUP(name_like)}, - {.parser = NULL} -}; - static void cleanup_list_entry(struct mood_item *item) { if (item->method && item->method->cleanup) @@ -372,8 +229,11 @@ enum mood_line_type { ML_SCORE }; +/** Data passed to the parser of a mood line. */ struct mood_line_parser_data { + /** The mood this mood line belongs to. */ struct mood *m; + /** The line number in the mood definition. */ unsigned line_num; }; @@ -388,17 +248,17 @@ static int parse_mood_line(char *mood_line, void *data) { struct mood_line_parser_data *mlpd = data; char **argv; - char *delim = " \t"; unsigned num_words; char **w; int i, ret; enum mood_line_type mlt = ML_INVALID; struct mood_item *mi = NULL; - char *buf = para_strdup(mood_line); mlpd->line_num++; - num_words = split_args(buf, &argv, delim); - ret = 1; + ret = create_argv(mood_line, " \t", &argv); + if (ret < 0) + return ret; + num_words = ret; if (!num_words) /* empty line */ goto out; w = argv; @@ -419,11 +279,13 @@ static int parse_mood_line(char *mood_line, void *data) w++; if (!*w) goto out; - if (!strcmp(*w, "with")) { - w++; - if (!*w) - goto out; - } + if (strcmp(*w, "with")) + goto check_for_if; + w++; + if (!*w) + goto out; + if (strcmp(*w, "score")) + goto out; } if (mlt == ML_SCORE || !strcmp(*w, "score")) { ret = -E_MOOD_SYNTAX; @@ -446,6 +308,7 @@ static int parse_mood_line(char *mood_line, void *data) w++; if (!*w) goto out; +check_for_if: if (!strcmp(*w, "if")) { ret = -E_MOOD_SYNTAX; w++; @@ -468,8 +331,8 @@ static int parse_mood_line(char *mood_line, void *data) ret = -E_MOOD_SYNTAX; if (!mood_methods[i].parser) goto out; - w++; - ret = mood_methods[i].parser(*w, &mi->parser_data); + ret = mood_methods[i].parser(num_words - 1 - (w - argv), w, + &mi->parser_data); if (ret < 0) goto out; mi->method = &mood_methods[i]; @@ -486,8 +349,7 @@ success: (mlt == ML_DENY? "deny" : "score"), mi->method); ret = 1; out: - free(argv); - free(buf); + free_argv(argv); if (ret >= 0) return ret; if (mi) { @@ -514,11 +376,10 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m) osl_close_disk_object(&mood_def); if (ret < 0) { PARA_ERROR_LOG("unable to load mood %s: %s\n", mlpd.m->name, - PARA_STRERROR(-ret)); + para_strerror(-ret)); destroy_mood(mlpd.m); return ret; } - PARA_INFO_LOG("loaded mood %s\n", mlpd.m->name); *m = mlpd.m; return 1; } @@ -533,39 +394,47 @@ 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\n"); + para_printf(pb, "failed to get mood definition: %s\n", + para_strerror(-ret)); return ret; } if (!*mood_name) /* ignore dummy row */ goto out; - para_printf(pb, "checking mood %s...\n", mood_name); + 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, parse_mood_line, &mlpd); if (ret < 0) para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num, - PARA_STRERROR(-ret)); + para_strerror(-ret)); out: osl_close_disk_object(&mood_def); - return 1; + return ret; } /** * Check all moods for syntax errors. * + * \param fd The afs socket. * \param query Unused. - * \param result: Contains check messages. */ -int mood_check_callback(__a_unused const struct osl_object *query, - struct osl_object *result) +void mood_check_callback(int fd, __a_unused const struct osl_object *query) { - struct para_buffer pb = {.buf = NULL}; + struct para_buffer pb = { + .max_size = SHMMAX, + .private_data = &fd, + .max_size_handler = pass_buffer_as_shm + }; - para_printf(&pb, "checking moods...\n"); + int ret = para_printf(&pb, "checking moods...\n"); + if (ret < 0) + return; osl_rbtree_loop(moods_table, BLOBCOL_ID, &pb, check_mood); - result->data = pb.buf; - result->size = pb.size; - return 1; + if (pb.offset) + pass_buffer_as_shm(pb.buf, pb.offset, &fd); + free(pb.buf); } #if 0 @@ -715,7 +584,7 @@ struct admissible_array { * \param aft_row The audio file to be added. * \param private_data Pointer to a struct admissible_file_info. * - * \return Negative on errors, positive on success. + * \return 1 if row admissible, 0 if not, negative on errors. */ static int add_if_admissible(struct osl_row *aft_row, void *data) { @@ -724,8 +593,8 @@ static int add_if_admissible(struct osl_row *aft_row, void *data) long score = 0; ret = compute_mood_score(aft_row, aa->m, &score); - if (ret < 0) - return (ret == -E_NOT_ADMISSIBLE)? 1 : ret; + if (ret <= 0) + return ret; if (statistics.num >= aa->size) { aa->size *= 2; aa->size += 100; @@ -863,6 +732,8 @@ static int mood_update_audio_file(const struct osl_row *aft_row, return ret; was_admissible = ret; ret = compute_mood_score(aft_row, current_mood, &score); + if (ret < 0) + return ret; is_admissible = (ret > 0); if (!was_admissible && !is_admissible) return 1; @@ -904,14 +775,27 @@ static void log_statistics(void) PARA_NOTICE_LOG("no admissible files\n"); return; } - PARA_NOTICE_LOG("last_played mean: %lli, last_played sigma: %llu\n", + PARA_INFO_LOG("last_played mean: %lli, last_played sigma: %llu\n", (long long int)(statistics.last_played_sum / n), (long long unsigned)int_sqrt(statistics.last_played_qd / n)); - PARA_NOTICE_LOG("num_played mean: %lli, num_played sigma: %llu\n", + PARA_INFO_LOG("num_played mean: %lli, num_played sigma: %llu\n", (long long int)statistics.num_played_sum / n, (long long unsigned)int_sqrt(statistics.num_played_qd / n)); } +/** + * Close the current mood. + * + * Free all resources of the current mood which were allocated during + * mood_open(). + */ +void close_current_mood(void) +{ + destroy_mood(current_mood); + current_mood = NULL; + memset(&statistics, 0, sizeof(statistics)); +} + /** * Change the current mood. * @@ -943,7 +827,7 @@ int change_current_mood(char *mood_name) .data = mood_name, .size = strlen(mood_name) + 1 }; - ret = osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row); + ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row)); if (ret < 0) { PARA_NOTICE_LOG("no such mood: %s\n", mood_name); return ret; @@ -951,46 +835,31 @@ int change_current_mood(char *mood_name) ret = load_mood(row, &m); if (ret < 0) return ret; - destroy_mood(current_mood); + close_current_mood(); current_mood = m; } else { - destroy_mood(current_mood); + close_current_mood(); current_mood = alloc_new_mood("dummy"); } aa.m = current_mood; - PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name); - PARA_INFO_LOG("%s\n", "computing statistics of admissible files"); + PARA_NOTICE_LOG("computing statistics of admissible files\n"); ret = audio_file_loop(&aa, add_if_admissible); if (ret < 0) return ret; log_statistics(); - PARA_NOTICE_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("score add complete\n"); - ret = 1; + PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name); + ret = statistics.num; out: free(aa.array); return ret; } - -/** - * Close the current mood. - * - * Free all resources of the current mood which were allocated during - * mood_open(). - */ -static void close_current_mood(void) -{ - destroy_mood(current_mood); - current_mood = NULL; - memset(&statistics, 0, sizeof(statistics)); -} - /** * Close and re-open the current mood. * @@ -1007,26 +876,26 @@ static void close_current_mood(void) int reload_current_mood(void) { int ret; - char *mood_name; + char *mood_name = NULL; - PARA_NOTICE_LOG("reloading current mood\n"); 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 = clear_score_table(); - if (ret < 0) - return ret; ret = change_current_mood(mood_name); free(mood_name); return ret; } -int moods_event_handler(enum afs_events event, struct para_buffer *pb, +int moods_event_handler(enum afs_events event, __a_unused struct para_buffer *pb, void *data) { - switch(event) { + if (!current_mood) + return 0; + switch (event) { /* * The three blob events might change the set of admissible files, * so we must reload the score list. @@ -1056,6 +925,5 @@ int moods_event_handler(enum afs_events event, struct para_buffer *pb, default: return 1; } - return 1; }