X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=20fa3a96a4ccf1139d6d369ae8ff5764103e7822;hp=3d10ced25949d61e370eae617950dd7ff316b7e6;hb=20ad4f0f93da79e2ec0a9699dff58b9922556438;hpb=753b26859be2150d3f4e7840a86412f96f60627a diff --git a/aft.c b/aft.c index 3d10ced2..20fa3a96 100644 --- a/aft.c +++ b/aft.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Andre Noll + * Copyright (C) 2007-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -20,6 +20,7 @@ #include "vss.h" #include "fd.h" #include "ipc.h" +#include "portable_io.h" static struct osl_table *audio_file_table; @@ -73,6 +74,8 @@ enum ls_flags { LS_FLAG_ADMISSIBLE_ONLY = 2, /** -r */ LS_FLAG_REVERSE = 4, + /** -d */ + LS_FLAG_UNIXDATE = 8, }; /** @@ -98,6 +101,8 @@ struct ls_widths { unsigned short duration_width; /** size of the num played field. */ unsigned short num_played_width; + /** size of the amp field. */ + unsigned short amp_width; }; /** Data passed from the ls command handler to its callback function. */ @@ -142,8 +147,10 @@ enum afsi_offsets { AFSI_LYRICS_ID_OFFSET = 24, /** Storage position of the .audio_format_id field. */ AFSI_AUDIO_FORMAT_ID_OFFSET = 28, - /** 3 bytes reserved space for future usage. */ - AFSI_AUDIO_FORMAT_UNUSED_OFFSET = 29, + /** Storage position of the amplification field. */ + AFSI_AMP_OFFSET = 29, + /** 2 bytes reserved space for future usage. */ + AFSI_AUDIO_FORMAT_UNUSED_OFFSET = 30, /** On-disk storage space needed. */ AFSI_SIZE = 32 }; @@ -167,7 +174,8 @@ void save_afsi(struct afs_info *afsi, struct osl_object *obj) write_u32(buf + AFSI_LYRICS_ID_OFFSET, afsi->lyrics_id); write_u8(buf + AFSI_AUDIO_FORMAT_ID_OFFSET, afsi->audio_format_id); - memset(buf + AFSI_AUDIO_FORMAT_UNUSED_OFFSET, 0, 3); + write_u8(buf + AFSI_AMP_OFFSET, afsi->amp); + memset(buf + AFSI_AUDIO_FORMAT_UNUSED_OFFSET, 0, 2); } /** @@ -192,6 +200,7 @@ int load_afsi(struct afs_info *afsi, struct osl_object *obj) afsi->lyrics_id = read_u32(buf + AFSI_LYRICS_ID_OFFSET); afsi->audio_format_id = read_u8(buf + AFSI_AUDIO_FORMAT_ID_OFFSET); + afsi->amp = read_u8(buf + AFSI_AMP_OFFSET); return 1; } @@ -582,8 +591,6 @@ int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi) static int save_afd(struct audio_file_data *afd) { size_t size = sizeof(*afd) + sizeof_chunk_table(&afd->afhi); - - PARA_DEBUG_LOG("size: %zu\n", size); int shmid, ret = shm_new(size); void *shm_afd; char *buf; @@ -622,94 +629,6 @@ int load_afd(int shmid, struct audio_file_data *afd) return 1; } -/** - * Mmap the given audio file and update statistics. - * - * \param aft_row Determines the audio file to be opened and updated. - * \param score The score of the audio file. - * \param afd Result pointer. - * - * On success, the numplayed field of the audio file selector info is increased - * and the lastplayed time is set to the current time. Finally, the score of - * the audio file is updated. - * - * \return Positive shmid on success, negative on errors. - */ -int open_and_update_audio_file(struct osl_row *aft_row, long score, - struct audio_file_data *afd) -{ - HASH_TYPE *aft_hash, file_hash[HASH_SIZE]; - struct osl_object afsi_obj; - struct afs_info old_afsi, new_afsi; - int ret = get_hash_of_row(aft_row, &aft_hash); - struct afsi_change_event_data aced; - struct osl_object map, chunk_table_obj; - char *path; - - if (ret < 0) - return ret; - ret = get_audio_file_path_of_row(aft_row, &path); - if (ret < 0) - return ret; - ret = get_afsi_object_of_row(aft_row, &afsi_obj); - if (ret < 0) - return ret; - ret = load_afsi(&old_afsi, &afsi_obj); - if (ret < 0) - return ret; - ret = get_afhi_of_row(aft_row, &afd->afhi); - if (ret < 0) - return ret; - afd->afhi.chunk_table = NULL; - ret = osl_open_disk_object(audio_file_table, aft_row, - AFTCOL_CHUNKS, &chunk_table_obj); - if (ret < 0) - goto err; - ret = mmap_full_file(path, O_RDONLY, &map.data, - &map.size, &afd->fd); - if (ret < 0) - goto err; - hash_function(map.data, map.size, file_hash); - ret = hash_compare(file_hash, aft_hash); - para_munmap(map.data, map.size); - if (ret) { - ret = -E_HASH_MISMATCH; - goto err; - } - new_afsi = old_afsi; - new_afsi.num_played++; - new_afsi.last_played = time(NULL); - save_afsi(&new_afsi, &afsi_obj); /* in-place update */ - - load_chunk_table(&afd->afhi, chunk_table_obj.data); - { - struct ls_data d = { - .afhi = afd->afhi, /* struct copy */ - .afsi = old_afsi, - .path = path, - .score = score, - .hash = file_hash - }; - struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1}; - ret = make_status_items(&d, &pb); /* frees info string */ - afd->afhi.info_string = NULL; - if (ret < 0) - goto err; - strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE); - afd->verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE - 1] = '\0'; - free(pb.buf); - } - aced.aft_row = aft_row; - aced.old_afsi = &old_afsi; - afs_event(AFSI_CHANGE, NULL, &aced); - ret = save_afd(afd); -err: - free(afd->afhi.chunk_table); - free(afd->afhi.info_string); - osl_close_disk_object(&chunk_table_obj); - return ret; -} - static int get_local_time(uint64_t *seconds, char *buf, size_t size, time_t current_time, enum ls_listing_mode lm) { @@ -736,7 +655,7 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size, #define GET_NUM_DIGITS(x, num) { \ typeof((x)) _tmp = PARA_ABS(x); \ *num = 1; \ - if ((x)) \ + if ((_tmp)) \ while ((_tmp) > 9) { \ (_tmp) /= 10; \ (*num)++; \ @@ -883,10 +802,15 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, goto out; } get_attribute_bitmap(&afsi->attributes, att_buf); - ret = get_local_time(&afsi->last_played, last_played_time, - sizeof(last_played_time), current_time, opts->mode); - if (ret < 0) - goto out; + if (opts->flags & LS_FLAG_UNIXDATE) + sprintf(last_played_time, "%llu", + (long long unsigned)afsi->last_played); + else { + ret = get_local_time(&afsi->last_played, last_played_time, + sizeof(last_played_time), current_time, opts->mode); + if (ret < 0) + goto out; + } get_duration_buf(afhi->seconds_total, duration_buf, opts); if (have_score) { if (opts->mode == LS_MODE_LONG) @@ -899,6 +823,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, ret = para_printf(b, "%s" /* score */ "%s " /* attributes */ + "%*u " /* amp */ "%*d " /* image_id */ "%*d " /* lyrics_id */ "%*d " /* bitrate */ @@ -911,6 +836,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, "%s\n", /* path */ score_buf, att_buf, + w->amp_width, afsi->amp, w->image_id_width, afsi->image_id, w->lyrics_id_width, afsi->lyrics_id, w->bitrate_width, afhi->bitrate, @@ -955,6 +881,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, "%s: %lu\n" /* seconds total */ "%s: %s\n" /* last played time */ "%s: %d\n" /* num_played */ + "%s: %u\n" /* ampplification */ "%s" /* tag info */ "%s: %lu\n" /* chunk time */ "%s: %lu\n", /* num chunks */ @@ -975,6 +902,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total, status_item_list[SI_LAST_PLAYED], last_played_time, status_item_list[SI_NUM_PLAYED], afsi->num_played, + status_item_list[SI_AMPLIFICATION], afsi->amp, afhi->info_string, status_item_list[SI_CHUNK_TIME], tv2ms(&afhi->chunk_tv), status_item_list[SI_NUM_CHUNKS], afhi->chunks_total @@ -999,6 +927,13 @@ out: return ret; } +/** + * Write a list of audio-file related status items with empty values. + * + * \param buf Result pointer. + * + * This is used by vss when currently no audio file is open. + */ void make_empty_status_items(char *buf) { sprintf(buf, @@ -1021,6 +956,10 @@ void make_empty_status_items(char *buf) "%s: \n" /* seconds total */ "%s: \n" /* num played */ "%s: \n" /* last played */ + "%s: \n" /* audio file info */ + "%s: \n" /* taginfo1 */ + "%s: \n" /* taginfo2 */ + "%s: \n" /* amplification */ , status_item_list[SI_PATH], status_item_list[SI_DIRECTORY], @@ -1040,22 +979,178 @@ void make_empty_status_items(char *buf) status_item_list[SI_DURATION], status_item_list[SI_SECONDS_TOTAL], status_item_list[SI_NUM_PLAYED], - status_item_list[SI_LAST_PLAYED] + status_item_list[SI_LAST_PLAYED], + status_item_list[SI_AUDIO_FILE_INFO], + status_item_list[SI_TAGINFO1], + status_item_list[SI_TAGINFO2], + status_item_list[SI_AMPLIFICATION] ); } -int make_status_items(struct ls_data *d, struct para_buffer *pb) +static void fixup_taginfo(char *begin, char *end) { + char *p = begin; + + for (;;) { + p = strchr(p, '\n'); + if (!p) + break; + if (p >= end - 1) + break; + *p = ' '; + p++; + } +} + +/* crap, remove this ASAP. */ +static int fixup_info_string(char *info_string) +{ + char *t1, *t2, *end; + + if (strncmp(info_string, "audio_file_info:", 16)) + return -ERRNO_TO_PARA_ERROR(EINVAL); + t1 = strstr(info_string, "\ntaginfo1:"); + if (!t1) + return -ERRNO_TO_PARA_ERROR(EINVAL); + t2 = strstr(info_string, "\ntaginfo2: "); + if (!t2) + return -ERRNO_TO_PARA_ERROR(EINVAL); + + end = t2 + strlen(t2) + 1; + fixup_taginfo(info_string + 16, t1); + fixup_taginfo(t1 + 10, t2); + fixup_taginfo(t2 + 10, end); + + if (t1 - info_string < 80 && t2 - t1 < 80 && end - t2 < 80) + return 0; + if (t1 - info_string >= 80) { + memmove(info_string + 80, t1, end - t1); + t1 = info_string + 80; + t2 -= t1 - info_string - 80; + end -= t1 - info_string - 80; + } + if (t2 - t1 >= 80) { + memmove(t1 + 80, t2, end - t2); + end -= t2 - t1 - 80; + t2 = t1 + 80; + } + if (end - t2 >= 80) { + t2[78] = '\n'; + t2[79] = '\0'; + } + return 1; +} + +static int make_status_items(struct audio_file_data *afd, + struct afs_info *afsi, char *path, long score, + HASH_TYPE *hash) +{ + struct ls_data d = { + .afhi = afd->afhi, + .afsi = *afsi, + .path = path, + .score = score, + .hash = hash + }; struct ls_options opts = { .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY, .mode = LS_MODE_VERBOSE, }; + struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1}; time_t current_time; + int ret; + ret = fixup_info_string(afd->afhi.info_string); + if (ret < 0) { + PARA_WARNING_LOG("ignoring invalid tag info\n"); + afd->afhi.info_string[0] = '\0'; + } else if (ret) + PARA_NOTICE_LOG("truncated overlong tag info\n"); time(¤t_time); - return print_list_item(d, &opts, pb, current_time); + ret = print_list_item(&d, &opts, &pb, current_time); /* frees info string */ + afd->afhi.info_string = NULL; + if (ret < 0) + goto out; + strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE); + afd->verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE - 1] = '\0'; +out: + free(pb.buf); + return ret; } +/** + * Mmap the given audio file and update statistics. + * + * \param aft_row Determines the audio file to be opened and updated. + * \param score The score of the audio file. + * \param afd Result pointer. + * + * On success, the numplayed field of the audio file selector info is increased + * and the lastplayed time is set to the current time. Finally, the score of + * the audio file is updated. + * + * \return Positive shmid on success, negative on errors. + */ +int open_and_update_audio_file(struct osl_row *aft_row, long score, + struct audio_file_data *afd) +{ + HASH_TYPE *aft_hash, file_hash[HASH_SIZE]; + struct osl_object afsi_obj; + struct afs_info old_afsi, new_afsi; + int ret = get_hash_of_row(aft_row, &aft_hash); + struct afsi_change_event_data aced; + struct osl_object map, chunk_table_obj; + char *path; + + if (ret < 0) + return ret; + ret = get_audio_file_path_of_row(aft_row, &path); + if (ret < 0) + return ret; + ret = get_afsi_object_of_row(aft_row, &afsi_obj); + if (ret < 0) + return ret; + ret = load_afsi(&old_afsi, &afsi_obj); + if (ret < 0) + return ret; + ret = get_afhi_of_row(aft_row, &afd->afhi); + if (ret < 0) + return ret; + afd->afhi.chunk_table = NULL; + ret = osl_open_disk_object(audio_file_table, aft_row, + AFTCOL_CHUNKS, &chunk_table_obj); + if (ret < 0) + goto err; + ret = mmap_full_file(path, O_RDONLY, &map.data, + &map.size, &afd->fd); + if (ret < 0) + goto err; + hash_function(map.data, map.size, file_hash); + ret = hash_compare(file_hash, aft_hash); + para_munmap(map.data, map.size); + if (ret) { + ret = -E_HASH_MISMATCH; + goto err; + } + new_afsi = old_afsi; + new_afsi.num_played++; + new_afsi.last_played = time(NULL); + save_afsi(&new_afsi, &afsi_obj); /* in-place update */ + + load_chunk_table(&afd->afhi, chunk_table_obj.data); + ret = make_status_items(afd, &old_afsi, path, score, file_hash); + if (ret < 0) + goto err; + aced.aft_row = aft_row; + aced.old_afsi = &old_afsi; + afs_event(AFSI_CHANGE, NULL, &aced); + ret = save_afd(afd); +err: + free(afd->afhi.chunk_table); + free(afd->afhi.info_string); + osl_close_disk_object(&chunk_table_obj); + return ret; +} static int ls_audio_format_compare(const void *a, const void *b) { @@ -1243,8 +1338,10 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts) GET_NUM_DIGITS(d->afsi.num_played, &num_digits); w->num_played_width = PARA_MAX(w->num_played_width, num_digits); /* get the number of chars to print this amount of time */ - tmp = get_duration_width(d->afhi.seconds_total); - w->duration_width = PARA_MAX(w->duration_width, tmp); + num_digits = get_duration_width(d->afhi.seconds_total); + w->duration_width = PARA_MAX(w->duration_width, num_digits); + GET_NUM_DIGITS(d->afsi.amp, &num_digits); + w->amp_width = PARA_MAX(w->amp_width, num_digits); if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) { GET_NUM_DIGITS(score, &num_digits); num_digits++; /* add one for the sign (space or "-") */ @@ -1282,7 +1379,6 @@ static void com_ls_callback(int fd, const struct osl_object *query) prepare_ls_row); if (ret < 0) goto out; - ret = opts->num_patterns? -E_NO_MATCH : 0; if (!opts->num_matching_paths) goto out; ret = sort_matching_paths(opts); @@ -1369,6 +1465,10 @@ int com_ls(int fd, int argc, char * const * const argv) flags |= LS_FLAG_REVERSE; continue; } + if (!strcmp(arg, "-d")) { + flags |= LS_FLAG_UNIXDATE; + continue; + } if (!strncmp(arg, "-s", 2)) { if (!*(arg + 2) || *(arg + 3)) return -E_AFT_SYNTAX; @@ -1579,6 +1679,7 @@ static void com_add_callback(int fd, const struct osl_object *query) struct afs_info default_afsi = {.last_played = 0}; struct para_buffer msg = {.max_size = SHMMAX, .max_size_handler = pass_buffer_as_shm, .private_data = &fd}; + uint16_t afhi_offset, chunks_offset; hash = (HASH_TYPE *)buf + CAB_HASH_OFFSET; hash_to_asc(hash, asc);; @@ -1633,8 +1734,8 @@ static void com_add_callback(int fd, const struct osl_object *query) goto out; } /* no hs or force mode, child must have sent afhi */ - uint16_t afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS); - uint16_t chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS); + afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS); + chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS); objs[AFTCOL_AFHI].data = buf + afhi_offset; objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset; @@ -1696,7 +1797,7 @@ static void com_add_callback(int fd, const struct osl_object *query) afs_event(AUDIO_FILE_ADD, &msg, aft_row); out: if (ret < 0) - ret = para_printf(&msg, "%s\n", para_strerror(-ret)); + para_printf(&msg, "%s\n", para_strerror(-ret)); if (msg.offset) pass_buffer_as_shm(msg.buf, msg.offset, &fd); free(msg.buf); @@ -1796,6 +1897,7 @@ static int add_one_audio_file(const char *path, void *private_data) afhi_ptr = &afhi; } munmap(map.data, map.size); + close(fd); if (pad->flags & ADD_FLAG_VERBOSE) { send_ret = send_va_buffer(pad->fd, "adding %s\n", path); if (send_ret < 0) @@ -1908,11 +2010,13 @@ struct com_touch_options { int32_t num_played; /** New last played count. */ int64_t last_played; - /** new lyrics id. */ + /** New lyrics id. */ int32_t lyrics_id; - /** new image id. */ + /** New image id. */ int32_t image_id; - /** command line flags (see \ref touch_flags). */ + /** New amplification value. */ + int32_t amp; + /** Command line flags (see \ref touch_flags). */ unsigned flags; }; @@ -1933,7 +2037,7 @@ static int touch_audio_file(__a_unused struct osl_table *table, struct osl_object obj; struct afs_info old_afsi, new_afsi; int ret, no_options = tad->cto->num_played < 0 && tad->cto->last_played < 0 && - tad->cto->lyrics_id < 0 && tad->cto->image_id < 0; + tad->cto->lyrics_id < 0 && tad->cto->image_id < 0 && tad->cto->amp < 0; struct afsi_change_event_data aced; ret = get_afsi_object_of_row(row, &obj); @@ -1967,6 +2071,8 @@ static int touch_audio_file(__a_unused struct osl_table *table, new_afsi.num_played = tad->cto->num_played; if (tad->cto->last_played >= 0) new_afsi.last_played = tad->cto->last_played; + if (tad->cto->amp >= 0) + new_afsi.amp = tad->cto->amp; } tad->num_matches++; save_afsi(&new_afsi, &obj); /* in-place update */ @@ -2014,7 +2120,8 @@ int com_touch(int fd, int argc, char * const * const argv) .num_played = -1, .last_played = -1, .lyrics_id = -1, - .image_id = -1 + .image_id = -1, + .amp = -1, }; struct osl_object query = {.data = &cto, .size = sizeof(cto)}; int i, ret; @@ -2052,6 +2159,16 @@ int com_touch(int fd, int argc, char * const * const argv) return ret; continue; } + if (!strncmp(arg, "-a", 2)) { + int32_t val; + ret = para_atoi32(arg + 2, &val); + if (ret < 0) + return ret; + if (val < 0 || val > 255) + return -ERRNO_TO_PARA_ERROR(EINVAL); + cto.amp = val; + continue; + } if (!strcmp(arg, "-p")) { cto.flags |= TOUCH_FLAG_FNM_PATHNAME; continue; @@ -2095,7 +2212,7 @@ static int remove_audio_file(__a_unused struct osl_table *table, struct osl_row *row, const char *name, void *data) { struct com_rm_action_data *crd = data; - int ret, ret2; + int ret; if (crd->flags & RM_FLAG_VERBOSE) { ret = para_printf(&crd->pb, "removing %s\n", name); @@ -2105,8 +2222,7 @@ static int remove_audio_file(__a_unused struct osl_table *table, afs_event(AUDIO_FILE_REMOVE, &crd->pb, row); ret = osl_del_row(audio_file_table, row); if (ret < 0) - ret2 = para_printf(&crd->pb, "%s: %s\n", name, - para_strerror(-ret)); + para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret)); else crd->num_removed++; return ret; @@ -2135,7 +2251,7 @@ static void com_rm_callback(int fd, const struct osl_object *query) pmd.fnmatch_flags |= FNM_PATHNAME; ret = for_each_matching_row(&pmd); if (ret < 0) { - ret = para_printf(&crd.pb, "%s\n", para_strerror(-ret)); + para_printf(&crd.pb, "%s\n", para_strerror(-ret)); return; } if (!crd.num_removed && !(crd.flags & RM_FLAG_FORCE)) @@ -2268,10 +2384,6 @@ static void com_cpsi_callback(int fd, const struct osl_object *query) }; int ret; char *source_path = (char *)query->data + sizeof(cad.flags); - - ret = get_afsi_of_path(source_path, &cad.source_afsi); - if (ret < 0) - goto out; struct pattern_match_data pmd = { .table = audio_file_table, .loop_col_num = AFTCOL_HASH, @@ -2282,17 +2394,20 @@ static void com_cpsi_callback(int fd, const struct osl_object *query) .data = &cad, .action = copy_selector_info }; + + ret = get_afsi_of_path(source_path, &cad.source_afsi); + if (ret < 0) + goto out; ret = for_each_matching_row(&pmd); out: if (ret < 0) - ret = para_printf(&cad.pb, "%s\n", para_strerror(-ret)); + para_printf(&cad.pb, "%s\n", para_strerror(-ret)); else if (cad.flags & CPSI_FLAG_VERBOSE) { if (cad.num_copied) - ret = para_printf(&cad.pb, "copied requested afsi from %s " - "to %u files\n", - source_path, cad.num_copied); + para_printf(&cad.pb, "copied requested afsi from %s " + "to %u files\n", source_path, cad.num_copied); else - ret = para_printf(&cad.pb, "nothing copied\n"); + para_printf(&cad.pb, "nothing copied\n"); } if (cad.pb.offset) pass_buffer_as_shm(cad.pb.buf, cad.pb.offset, &fd);