From: Andre Noll Date: Sat, 11 Jul 2009 13:55:04 +0000 (+0200) Subject: Move mood methods to a separate file. X-Git-Tag: v0.4.0~51^2~5 X-Git-Url: http://git.tue.mpg.de/?a=commitdiff_plain;h=9242741b62d726e7b673a3b10c62bbc8509a75ef;p=paraslash.git Move mood methods to a separate file. As more mood methods will be added in subsequent patches, it's good to separate moods from mood methods. This also removes the played_rarely mood method, since it never really worked: Whether or not a file was played rarely depends on the afs statistics which have not been computed at the time the mood scrore function is executed. --- diff --git a/afs.c b/afs.c index 8e3aa923..531cada8 100644 --- a/afs.c +++ b/afs.c @@ -29,6 +29,7 @@ #include "sched.h" #include "signal.h" #include "fd.h" +#include "mood.h" /** The osl tables used by afs. \sa blob.c. */ enum afs_table_num { diff --git a/afs.h b/afs.h index 9e04a7ed..ccfb7b8d 100644 --- a/afs.h +++ b/afs.h @@ -234,13 +234,6 @@ int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj); int audio_file_loop(void *private_data, osl_rbtree_loop_func *func); void aft_check_callback(int fd, __a_unused const struct osl_object *query); -/* mood */ -int change_current_mood(char *mood_name); -void close_current_mood(void); -int reload_current_mood(void); -void mood_check_callback(int fd, __a_unused const struct osl_object *query); - - /* playlist */ int playlist_open(char *name); void playlist_close(void); diff --git a/configure.ac b/configure.ac index 61421b5d..5f39c245 100644 --- a/configure.ac +++ b/configure.ac @@ -84,7 +84,7 @@ dccp_send fd user_list chunk_queue afs aft mood score attribute blob ringbuffer playlist sha1 sched audiod grab_client filter_common wav_filter compress_filter http_recv dccp_recv recv_common write_common file_write audiod_command client_common recv stdout filter stdin audioc write client exec send_common ggo -udp_recv udp_send color fec fecdec_filter prebuffer_filter" +udp_recv udp_send color fec fecdec_filter prebuffer_filter mm" all_executables="server recv filter audioc write client afh" @@ -125,7 +125,7 @@ afh_ldflags="" server_cmdline_objs="server.cmdline server_command_list afs_command_list" server_errlist_objs="server afh_common mp3_afh vss command net string signal - time daemon crypt http_send close_on_fork + time daemon crypt http_send close_on_fork mm ipc dccp_send fd user_list chunk_queue afs aft mood score attribute blob playlist sha1 sched acl send_common udp_send color fec" server_ldflags="-losl" diff --git a/error.h b/error.h index 1cc40e7f..79931bf4 100644 --- a/error.h +++ b/error.h @@ -111,12 +111,15 @@ extern const char **para_errlist[]; #define MOOD_ERRORS \ - PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \ PARA_ERROR(NO_MOOD, "no mood available"), \ PARA_ERROR(NOT_ADMISSIBLE, "file is not admissible"), \ PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \ +#define MM_ERRORS \ + PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \ + + #define ATTRIBUTE_ERRORS \ PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \ PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \ diff --git a/mm.c b/mm.c new file mode 100644 index 00000000..0130cbcb --- /dev/null +++ b/mm.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2007-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mm.c Paraslash's mood methods. */ + +#include +#include +#include +#include + +#include "para.h" +#include "error.h" +#include "string.h" +#include "afh.h" +#include "afs.h" +#include "mm.h" + +#define MOOD_COMPARATORS \ + MC(LESS, <) \ + MC(LESS_OR_EQUAL, <=) \ + MC(EQUAL, =) \ + MC(EQUAL2, ==) \ + MC(NOT_EQUAL, !=) \ + MC(NOT_EQUAL2, <>) \ + MC(GREATER, >) \ + MC(GREATER_OR_EQUAL, >=) \ + +#define MC(a, b) MC_ ## a, +enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS}; +#undef MC +#define MC(a, b) # b, +const char const *mood_comparators[] = {MOOD_COMPARATORS}; +#undef MC + +static int parse_mood_comparator(const char *word) +{ + int i; + + for (i = 0; i < NUM_MOOD_COMPARATORS; i++) + if (!strcmp(word, mood_comparators[i])) + return i; + return -E_MOOD_SYNTAX; +} + +static int compare_int32(int32_t a, int32_t b, enum mood_comparator_id id) +{ + int res; + + switch (id) { + case MC_LESS: + res = a < b; break; + case MC_LESS_OR_EQUAL: + res = a <= b; break; + case MC_EQUAL: + case MC_EQUAL2: + res = a == b; break; + case MC_NOT_EQUAL: + case MC_NOT_EQUAL2: + res = a != b; break; + case MC_GREATER: + res = a > b; break; + case MC_GREATER_OR_EQUAL: + res = a >= b; break; + default: + PARA_EMERG_LOG("BUG: invalid mood comparator\n"); + exit(EXIT_FAILURE); + } + return res? 100 : -100; +} + +struct mm_year_data { + /** The year given at the mood line. */ + int32_t year; + /** Used to detect Y2K issues. */ + int32_t current_year; + /** <, <=, =, !=, >=, or >. */ + enum mood_comparator_id id; +}; + +static int mm_year_parser(int argc, char **argv, void **private) +{ + int ret = -E_MOOD_SYNTAX; + struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd)); + time_t current_time; + struct tm *gmt; + + if (argc != 2) + goto err; + ret = parse_mood_comparator(argv[1]); + mmyd->id = ret; + if (ret < 0) + goto err; + ret = para_atoi32(argv[2], &mmyd->year); + if (ret < 0) + goto err; + current_time = time(NULL); + gmt = gmtime(¤t_time); + /* tm_year is the number of years since 1900 */ + mmyd->current_year = gmt->tm_year + 1900; + *private = mmyd; + return 1; +err: + free(mmyd); + return ret; +} + +static int mm_year_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + const struct afh_info *afhi, + const void *private) +{ + const struct mm_year_data *mmyd = private; + int32_t tag_year; + int ret = para_atoi32(afhi->tags.year, &tag_year); + + if (ret < 0) /* year tag not present or not a number */ + return -100; + if (tag_year < 0) + return -100; + /* try to work around Y2K issues */ + if (tag_year < 100) { + tag_year += 1900; + if (tag_year + 100 <= mmyd->current_year) + tag_year += 100; /* assume tag_year >= 2000 */ + } + return compare_int32(tag_year, mmyd->year, mmyd->id); +} + +static void mm_year_cleanup(void *private) +{ + free(private); +} + +static int mm_no_attributes_set_parser(int argc, __a_unused char **argv, + __a_unused void **ignored) +{ + return argc? -E_MOOD_SYNTAX : 1; +} + +static int mm_no_attributes_set_score_function(__a_unused const char *path, + const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + __a_unused const void *data) +{ + if (!afsi->attributes) + return 100; + return -100; +} + +static int mm_path_matches_score_function(const char *path, + __a_unused const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *data) +{ + if (fnmatch(data, path, 0)) + return -100; + return 100; +} + +static int mm_path_matches_parser(int argc, char **argv, void **data) +{ + if (argc != 1) + return -E_MOOD_SYNTAX; + *data = para_strdup(argv[1]); + return 1; +} + +static void mm_path_matches_cleanup(void *data) +{ + free(data); +} + +static int mm_is_set_parser(int argc, char **argv, void **bitnum) +{ + int ret; + unsigned char c, *res; + + if (argc != 1) + return -E_MOOD_SYNTAX; + ret = get_attribute_bitnum_by_name(argv[1], &c); + if (ret < 0) + return ret; + res = para_malloc(1); + *res = c; + *bitnum = res; + return 1; +} + +static int mm_is_set_score_function(__a_unused const char *path, + __a_unused const struct afs_info *afsi, + __a_unused const struct afh_info *afhi, + const void *data) +{ + const unsigned char *bn = data; + if (afsi->attributes & (1ULL << *bn)) + return 100; + return -100; +} + +#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 + +const struct mood_method mood_methods[] = { + {DEFINE_MOOD_METHOD(no_attributes_set)}, + {DEFINE_MOOD_METHOD(is_set)}, + {DEFINE_MOOD_METHOD_WITH_CLEANUP(path_matches)}, + {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)}, + {.parser = NULL} +}; + diff --git a/mm.h b/mm.h new file mode 100644 index 00000000..c1744dcb --- /dev/null +++ b/mm.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mm.h Symbols and declarations for mood methods. */ + +/** + * 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 afh_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 void** pointer. + * + * \sa mood_open(), mood_cleanup_function, mood_score_function. + */ +typedef int mood_parser(int, 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; +}; + +/** The array of available mood methods. */ +extern const struct mood_method mood_methods[]; diff --git a/mood.c b/mood.c index 239911fd..ae71dab0 100644 --- a/mood.c +++ b/mood.c @@ -6,9 +6,7 @@ /** \file mood.c Paraslash's mood handling functions. */ -#include #include -#include #include #include "para.h" @@ -18,6 +16,7 @@ #include "afs.h" #include "list.h" #include "ipc.h" +#include "mm.h" /** * Contains statistical data of the currently admissible audio files. @@ -38,66 +37,6 @@ struct afs_statistics { }; 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 afh_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 void** pointer. - * - * \sa mood_open(), mood_cleanup_function, mood_score_function. - */ -typedef int mood_parser(int, 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; -}; - /** * Each line of the current mood corresponds to a mood_item. */ @@ -168,210 +107,6 @@ static uint64_t int_sqrt(uint64_t x) return res; } -#define MOOD_COMPARATORS \ - MC(LESS, <) \ - MC(LESS_OR_EQUAL, <=) \ - MC(EQUAL, =) \ - MC(EQUAL2, ==) \ - MC(NOT_EQUAL, !=) \ - MC(NOT_EQUAL2, <>) \ - MC(GREATER, >) \ - MC(GREATER_OR_EQUAL, >=) \ - -#define MC(a, b) MC_ ## a, -enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS}; -#undef MC -#define MC(a, b) # b, -const char const *mood_comparators[] = {MOOD_COMPARATORS}; -#undef MC - -static int parse_mood_comparator(const char *word) -{ - int i; - - for (i = 0; i < NUM_MOOD_COMPARATORS; i++) - if (!strcmp(word, mood_comparators[i])) - return i; - return -E_MOOD_SYNTAX; -} - -static int compare_int32(int32_t a, int32_t b, enum mood_comparator_id id) -{ - int res; - - switch (id) { - case MC_LESS: - res = a < b; break; - case MC_LESS_OR_EQUAL: - res = a <= b; break; - case MC_EQUAL: - case MC_EQUAL2: - res = a == b; break; - case MC_NOT_EQUAL: - case MC_NOT_EQUAL2: - res = a != b; break; - case MC_GREATER: - res = a > b; break; - case MC_GREATER_OR_EQUAL: - res = a >= b; break; - default: - PARA_EMERG_LOG("BUG: invalid mood comparator\n"); - exit(EXIT_FAILURE); - } - return res? 100 : -100; -} - -struct mm_year_data { - /** The year given at the mood line. */ - int32_t year; - /** Used to detect Y2K issues. */ - int32_t current_year; - /** <, <=, =, !=, >=, or >. */ - enum mood_comparator_id id; -}; - -static int mm_year_parser(int argc, char **argv, void **private) -{ - int ret = -E_MOOD_SYNTAX; - struct mm_year_data *mmyd = para_malloc(sizeof(*mmyd)); - time_t current_time; - struct tm *gmt; - - if (argc != 2) - goto err; - ret = parse_mood_comparator(argv[1]); - mmyd->id = ret; - if (ret < 0) - goto err; - ret = para_atoi32(argv[2], &mmyd->year); - if (ret < 0) - goto err; - current_time = time(NULL); - gmt = gmtime(¤t_time); - /* tm_year is the number of years since 1900 */ - mmyd->current_year = gmt->tm_year + 1900; - *private = mmyd; - return 1; -err: - free(mmyd); - return ret; -} - -static int mm_year_score_function(__a_unused const char *path, - __a_unused const struct afs_info *afsi, - const struct afh_info *afhi, - const void *private) -{ - const struct mm_year_data *mmyd = private; - int32_t tag_year; - int ret = para_atoi32(afhi->tags.year, &tag_year); - - if (ret < 0) /* year tag not present or not a number */ - return -100; - if (tag_year < 0) - return -100; - /* try to work around Y2K issues */ - if (tag_year < 100) { - tag_year += 1900; - if (tag_year + 100 <= mmyd->current_year) - tag_year += 100; /* assume tag_year >= 2000 */ - } - return compare_int32(tag_year, mmyd->year, mmyd->id); -} - -static void mm_year_cleanup(void *private) -{ - free(private); -} - -static int mm_no_attributes_set_parser(int argc, __a_unused char **argv, - __a_unused void **ignored) -{ - return argc? -E_MOOD_SYNTAX : 1; -} - -static int mm_no_attributes_set_score_function(__a_unused const char *path, - const struct afs_info *afsi, - __a_unused const struct afh_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 afh_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(int argc, __a_unused char **argv, - __a_unused void **ignored) -{ - return argc? -E_MOOD_SYNTAX : 1; -} - -static int mm_path_matches_score_function(const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct afh_info *afhi, - const void *data) -{ - if (fnmatch(data, path, 0)) - return -100; - return 100; -} - -static int mm_path_matches_parser(int argc, char **argv, void **data) -{ - if (argc != 1) - return -E_MOOD_SYNTAX; - *data = para_strdup(argv[1]); - return 1; -} - -static void mm_path_matches_cleanup(void *data) -{ - free(data); -} - -static int mm_is_set_parser(int argc, char **argv, void **bitnum) -{ - int ret; - unsigned char c, *res; - - if (argc != 1) - return -E_MOOD_SYNTAX; - ret = get_attribute_bitnum_by_name(argv[1], &c); - if (ret < 0) - return ret; - res = para_malloc(1); - *res = c; - *bitnum = res; - return 1; -} - -static int mm_is_set_score_function(__a_unused const char *path, - __a_unused const struct afs_info *afsi, - __a_unused const struct afh_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, long *score_arg_sum) @@ -432,24 +167,6 @@ static int compute_mood_score(const struct osl_row *aft_row, struct mood *m, 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(path_matches)}, - {DEFINE_MOOD_METHOD_WITH_CLEANUP(year)}, - {.parser = NULL} -}; - static void cleanup_list_entry(struct mood_item *item) { if (item->method && item->method->cleanup) @@ -1063,7 +780,6 @@ void close_current_mood(void) memset(&statistics, 0, sizeof(statistics)); } - /** * Change the current mood. * diff --git a/mood.h b/mood.h new file mode 100644 index 00000000..51c27a36 --- /dev/null +++ b/mood.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2007-2009 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mood.h Functions exported by mood.h. */ + +int change_current_mood(char *mood_name); +void close_current_mood(void); +int reload_current_mood(void); +void mood_check_callback(int fd, __a_unused const struct osl_object *query);