int cmd_listall(int argc, char **argv, struct mpd_connection *conn) { const char * listall = ""; int i = 0; if (argc > 0) listall = charset_to_utf8(argv[i]); do { char *tmp = strdup(listall); strip_trailing_slash(tmp); if (options.custom_format) { if (!mpd_send_list_all_meta(conn, tmp)) printErrorAndExit(conn); print_entity_list(conn, MPD_ENTITY_TYPE_UNKNOWN); } else { if (!mpd_send_list_all(conn, tmp)) printErrorAndExit(conn); print_filenames(conn); } my_finishCommand(conn); free(tmp); } while (++i < argc && (listall = charset_to_utf8(argv[i])) != NULL); return 0; }
int cmd_ls ( int argc, char ** argv, mpd_Connection * conn ) { mpd_InfoEntity * entity; const char *ls; int i = 0; if (argc > 0) ls = charset_to_utf8(argv[i]); else ls = strdup(""); do { mpd_sendLsInfoCommand(conn,ls); printErrorAndExit(conn); while((entity = mpd_getNextInfoEntity(conn))) { if(entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) { mpd_Directory * dir = entity->info.directory; printf("%s\n", charset_from_utf8(dir->path)); } else if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) { struct mpd_song *song = entity->info.song; printf("%s\n", charset_from_utf8(song->file)); } mpd_freeInfoEntity(entity); } my_finishCommand(conn); } while (++i < argc && (ls = charset_to_utf8(argv[i])) != NULL); return 0; }
int cmd_lsplaylists ( int argc, char ** argv, mpd_Connection * conn ) { mpd_InfoEntity * entity; const char * ls = ""; int i = 0; if(argc>0) ls = charset_to_utf8(argv[i]); do { mpd_sendLsInfoCommand(conn,ls); printErrorAndExit(conn); while((entity = mpd_getNextInfoEntity(conn))) { if(entity->type== MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) { mpd_PlaylistFile * pl = entity->info.playlistFile; printf("%s\n", charset_from_utf8(pl->path)); } mpd_freeInfoEntity(entity); } my_finishCommand(conn); } while (++i < argc && (ls = charset_to_utf8(argv[i])) != NULL); return 0; }
void set_charset(Octstr *document, Octstr *charset) { long gt = 0, enc = 0; Octstr *encoding = NULL, *text = NULL, *temp = NULL; if (octstr_len(charset) == 0) return; encoding = octstr_create(" encoding"); enc = octstr_search(document, encoding, 0); gt = octstr_search_char(document, '>', 0); if (enc < 0 || enc > gt) { gt++; text = octstr_copy(document, gt, octstr_len(document) - gt); if (charset_to_utf8(text, &temp, charset) >= 0) { octstr_delete(document, gt, octstr_len(document) - gt); octstr_append_data(document, octstr_get_cstr(temp), octstr_len(temp)); } octstr_destroy(temp); octstr_destroy(text); } octstr_destroy(encoding); }
static int do_search ( int argc, char ** argv, mpd_Connection * conn, int exact ) { Constraint *constraints; int numconstraints; int i; if (argc % 2 != 0) DIE("arguments must be pairs of search types and queries\n"); numconstraints = get_constraints(argc, argv, &constraints); if (numconstraints < 0) return -1; mpd_startSearch(conn, exact); for (i = 0; i < numconstraints; i++) { mpd_addConstraintSearch(conn, constraints[i].type, charset_to_utf8(constraints[i].query)); } free(constraints); mpd_commitSearch(conn); printErrorAndExit(conn); print_filenames(conn); my_finishCommand(conn); return 0; }
static int ls_entity(int argc, char **argv, struct mpd_connection *conn, enum mpd_entity_type type) { const char *ls = ""; int i = 0; if (argc > 0) ls = charset_to_utf8(argv[i]); do { if (!mpd_send_list_meta(conn, ls)) printErrorAndExit(conn); print_entity_list(conn, type); my_finishCommand(conn); } while (++i < argc && (ls = charset_to_utf8(argv[i])) != NULL); return 0; }
int cmd_listall ( int argc, char ** argv, mpd_Connection * conn ) { const char * listall = ""; int i=0; if (argc > 0) listall = charset_to_utf8(argv[i]); do { mpd_sendListallCommand(conn,listall); printErrorAndExit(conn); print_filenames(conn); my_finishCommand(conn); } while (++i < argc && (listall = charset_to_utf8(argv[i])) != NULL); return 0; }
static void add_constraint(struct mpd_connection *conn, const struct constraint *constraint) { if (constraint->type == (enum mpd_tag_type)SEARCH_TAG_ANY) mpd_search_add_any_tag_constraint(conn, MPD_OPERATOR_DEFAULT, charset_to_utf8(constraint->query)); else if (constraint->type == (enum mpd_tag_type)SEARCH_TAG_URI) mpd_search_add_uri_constraint(conn, MPD_OPERATOR_DEFAULT, charset_to_utf8(constraint->query)); #if LIBMPDCLIENT_CHECK_VERSION(2,9,0) else if (constraint->type == (enum mpd_tag_type)SEARCH_TAG_BASE) mpd_search_add_base_constraint(conn, MPD_OPERATOR_DEFAULT, charset_to_utf8(constraint->query)); #endif else mpd_search_add_tag_constraint(conn, MPD_OPERATOR_DEFAULT, constraint->type, charset_to_utf8(constraint->query)); }
int cmd_update ( int argc, char ** argv, mpd_Connection * conn) { const char * update = ""; int i = 0; mpd_sendCommandListBegin(conn); printErrorAndExit(conn); if(argc > 0) update = charset_to_utf8(argv[i]); do { mpd_sendUpdateCommand(conn, update); } while (++i < argc && (update = charset_to_utf8(argv[i])) != NULL); mpd_sendCommandListEnd(conn); printErrorAndExit(conn); mpd_finishCommand(conn); printErrorAndExit(conn); return 1; }
static void test_charset_iconv_utf7_state(void) { struct charset_translation *trans; string_t *str = t_str_new(32); unsigned char nextbuf[5+CHARSET_MAX_PENDING_BUF_SIZE+1]; size_t size; test_begin("charset iconv utf7 state"); test_assert(charset_to_utf8_begin("UTF-7", NULL, &trans) == 0); size = 2; test_assert(charset_to_utf8(trans, (const void *)"a+", &size, str) == CHARSET_RET_INCOMPLETE_INPUT); test_assert(strcmp(str_c(str), "a") == 0); test_assert(size == 1); memset(nextbuf, '?', sizeof(nextbuf)); memcpy(nextbuf, "+AOQ-", 5); size = sizeof(nextbuf); test_assert(charset_to_utf8(trans, nextbuf, &size, str) == CHARSET_RET_OK); test_assert(strcmp(str_c(str), "a\xC3\xA4???????????") == 0); charset_to_utf8_end(&trans); test_end(); }
int cmd_load ( int argc, char ** argv, struct mpd_connection *conn ) { if (!mpd_command_list_begin(conn, false)) printErrorAndExit(conn); for (int i = 0; i < argc; ++i) { printf("loading: %s\n",argv[i]); mpd_send_load(conn, charset_to_utf8(argv[i])); } mpd_command_list_end(conn); my_finishCommand(conn); return 0; }
int cmd_add (int argc, char ** argv, struct mpd_connection *conn ) { if (contains_absolute_path(argc, argv) && !path_prepare(conn)) printErrorAndExit(conn); int i; if (!mpd_command_list_begin(conn, false)) printErrorAndExit(conn); for(i=0;i<argc;i++) { strip_trailing_slash(argv[i]); const char *path = argv[i]; const char *relative_path = to_relative_path(path); if (relative_path != NULL) path = relative_path; if (options.verbosity >= V_VERBOSE) printf("adding: %s\n", path); mpd_send_add(conn, charset_to_utf8(path)); } if (!mpd_command_list_end(conn)) printErrorAndExit(conn); if (!mpd_response_finish(conn)) { #if LIBMPDCLIENT_CHECK_VERSION(2,4,0) if (mpd_connection_get_error(conn) == MPD_ERROR_SERVER) { /* check which of the arguments has failed */ unsigned location = mpd_connection_get_server_error_location(conn); if (location < (unsigned)argc) { /* we've got a valid location from the server */ const char *message = mpd_connection_get_error_message(conn); message = charset_from_utf8(message); fprintf(stderr, "error adding %s: %s\n", argv[location], message); exit(EXIT_FAILURE); } } #endif printErrorAndExit(conn); } return 0; }
int cmd_list ( int argc, char ** argv, mpd_Connection * conn ) { Constraint *constraints; int numconstraints = 0; int type; int i; char *tag; type = get_search_type(argv[0]); if (type < 0) return -1; argc -= 1; argv += 1; if (argc > 0) { if (argc % 2 != 0) { DIE("arguments must be a tag type and " "optional pairs of search types and queries\n"); } numconstraints = get_constraints(argc, argv, &constraints); if (numconstraints < 0) return -1; } mpd_startFieldSearch(conn, type); if (argc > 0) { for (i = 0; i < numconstraints; i++) { mpd_addConstraintSearch(conn, constraints[i].type, charset_to_utf8(constraints[i].query)); } free(constraints); } mpd_commitSearch(conn); printErrorAndExit(conn); while ((tag = mpd_getNextTag(conn, type))) { printErrorAndExit(conn); printf("%s\n", charset_from_utf8(tag)); free(tag); } my_finishCommand(conn); return 0; }
static void test_charset_iconv(void) { struct { const char *charset; const char *input; const char *output; enum charset_result result; } tests[] = { { "ISO-8859-1", "p\xE4\xE4", "p\xC3\xA4\xC3\xA4", CHARSET_RET_OK }, { "UTF-7", "+AOQA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDkAPYA5AD2AOQA9gDk", "\xC3\xA4\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4" "\xC3\xB6\xC3\xA4\xC3\xB6\xC3\xA4", CHARSET_RET_OK } }; string_t *str = t_str_new(128); struct charset_translation *trans; enum charset_result result; size_t pos, left, limit, len; unsigned int i; test_begin("charset iconv"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); test_assert_idx(charset_to_utf8_str(tests[i].charset, NULL, tests[i].input, str, &result) == 0, i); test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); test_assert_idx(result == tests[i].result, i); str_truncate(str, 0); test_assert_idx(charset_to_utf8_begin(tests[i].charset, NULL, &trans) == 0, i); len = strlen(tests[i].input); for (pos = 0, limit = 1; limit <= len; pos += left, limit++) { left = limit - pos; result = charset_to_utf8(trans, (const void *)(tests[i].input + pos), &left, str); if (result != CHARSET_RET_INCOMPLETE_INPUT && result != CHARSET_RET_OK) break; } test_assert_idx(strcmp(tests[i].output, str_c(str)) == 0, i); test_assert_idx(result == tests[i].result, i); charset_to_utf8_end(&trans); } /* Use //IGNORE just to force handling to be done by iconv instead of our own UTF-8 routines. */ test_charset_utf8_common("UTF-8//IGNORE"); test_end(); }
int charset_to_utf8_str(const char *charset, normalizer_func_t *normalizer, const char *input, string_t *output, enum charset_result *result_r) { struct charset_translation *t; size_t len = strlen(input); if (charset_to_utf8_begin(charset, normalizer, &t) < 0) return -1; *result_r = charset_to_utf8(t, (const unsigned char *)input, &len, output); charset_to_utf8_end(&t); return 0; }
int cmd_add (int argc, char ** argv, mpd_Connection * conn ) { int i; mpd_sendCommandListBegin(conn); printErrorAndExit(conn); for(i=0;i<argc;i++) { printf("adding: %s\n", argv[i]); mpd_sendAddCommand(conn, charset_to_utf8(argv[i])); printErrorAndExit(conn); } mpd_sendCommandListEnd(conn); my_finishCommand(conn); return 0; }
int cmd_load ( int argc, char ** argv, mpd_Connection * conn ) { int i; char * sp; char * dp; mpd_InfoEntity * entity; mpd_PlaylistFile * pl; for(i=0;i<argc;i++) { sp = argv[i]; while((sp = strchr(sp,' '))) *sp = '_'; } mpd_sendLsInfoCommand(conn,""); printErrorAndExit(conn); while((entity = mpd_getNextInfoEntity(conn))) { if(entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) { pl = entity->info.playlistFile; dp = sp = strdup(charset_from_utf8(pl->path)); while((sp = strchr(sp,' '))) *sp = '_'; for(i=0;i<argc;i++) { if(strcmp(dp,argv[i])==0) strcpy(argv[i], charset_from_utf8(pl->path)); } free(dp); mpd_freeInfoEntity(entity); } } my_finishCommand(conn); mpd_sendCommandListBegin(conn); printErrorAndExit(conn); for(i=0;i<argc;i++) { printf("loading: %s\n",argv[i]); mpd_sendLoadCommand(conn,charset_to_utf8(argv[i])); printErrorAndExit(conn); } mpd_sendCommandListEnd(conn); my_finishCommand(conn); return 0; }
static int find_songname_id(struct mpd_connection *conn, const char *s) { int res = -1; mpd_search_queue_songs(conn, false); const char *pattern = charset_to_utf8(s); mpd_search_add_any_tag_constraint(conn, MPD_OPERATOR_DEFAULT, pattern); mpd_search_commit(conn); struct mpd_song *song = mpd_recv_song(conn); if (song != NULL) { res = mpd_song_get_id(song); mpd_song_free(song); } my_finishCommand(conn); return res; }
static void translation_buf_decode(struct message_decoder_context *ctx, const unsigned char **data, size_t *size) { unsigned char trans_buf[CHARSET_MAX_PENDING_BUF_SIZE+1]; unsigned int data_wanted, skip; size_t trans_size, orig_size; /* @UNSAFE: move the previously untranslated bytes to trans_buf and see if we have now enough data to get the next character translated */ memcpy(trans_buf, ctx->translation_buf, ctx->translation_size); data_wanted = sizeof(trans_buf) - ctx->translation_size; if (data_wanted > *size) data_wanted = *size; memcpy(trans_buf + ctx->translation_size, *data, data_wanted); orig_size = trans_size = ctx->translation_size + data_wanted; (void)charset_to_utf8(ctx->charset_trans, trans_buf, &trans_size, ctx->buf2); if (trans_size <= ctx->translation_size) { /* need more data to finish the translation. */ i_assert(orig_size < CHARSET_MAX_PENDING_BUF_SIZE); memcpy(ctx->translation_buf, trans_buf, orig_size); ctx->translation_size = orig_size; *data += *size; *size = 0; return; } skip = trans_size - ctx->translation_size; i_assert(*size >= skip); *data += skip; *size -= skip; ctx->translation_size = 0; }
static bool message_decode_body(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output) { const unsigned char *data = NULL; size_t pos = 0, size = 0; int ret; if (ctx->encoding_buf->used != 0) { /* @UNSAFE */ buffer_append(ctx->encoding_buf, input->data, input->size); } switch (ctx->message_cte) { case MESSAGE_CTE_UNKNOWN: /* just skip this body */ return FALSE; case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: data = input->data; size = pos = input->size; break; case MESSAGE_CTE_QP: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { (void)quoted_printable_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { (void)quoted_printable_decode(input->data, input->size, &pos, ctx->buf); } data = ctx->buf->data; size = ctx->buf->used; break; case MESSAGE_CTE_BASE64: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { ret = base64_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { ret = base64_decode(input->data, input->size, &pos, ctx->buf); } if (ret < 0) { /* corrupted base64 data, don't bother with the rest of it */ return FALSE; } if (ret == 0) { /* end of base64 input */ pos = input->size; buffer_set_used_size(ctx->encoding_buf, 0); } data = ctx->buf->data; size = ctx->buf->used; break; } if (ctx->encoding_buf->used != 0) buffer_delete(ctx->encoding_buf, 0, pos); else if (pos != input->size) { buffer_append(ctx->encoding_buf, input->data + pos, input->size - pos); } if (ctx->binary_input) { output->data = data; output->size = size; } else if (ctx->charset_utf8 || ctx->charset_trans == NULL) { /* handle unknown charsets the same as UTF-8. it might find usable ASCII text. */ buffer_set_used_size(ctx->buf2, 0); if (ctx->normalizer != NULL) { (void)ctx->normalizer(data, size, ctx->buf2); output->data = ctx->buf2->data; output->size = ctx->buf2->used; } else if (uni_utf8_get_valid_data(data, size, ctx->buf2)) { output->data = data; output->size = size; } else { output->data = ctx->buf2->data; output->size = ctx->buf2->used; } } else { buffer_set_used_size(ctx->buf2, 0); if (ctx->translation_size != 0) translation_buf_decode(ctx, &data, &size); pos = size; (void)charset_to_utf8(ctx->charset_trans, data, &pos, ctx->buf2); if (pos != size) { ctx->translation_size = size - pos; i_assert(ctx->translation_size <= sizeof(ctx->translation_buf)); memcpy(ctx->translation_buf, data + pos, ctx->translation_size); } output->data = ctx->buf2->data; output->size = ctx->buf2->used; } output->hdr = NULL; return TRUE; }
static bool message_decode_body(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output) { const unsigned char *data = NULL; size_t pos = 0, size = 0; const char *error; int ret; if (ctx->encoding_buf->used != 0) buffer_append(ctx->encoding_buf, input->data, input->size); switch (ctx->message_cte) { case MESSAGE_CTE_UNKNOWN: /* just skip this body */ return FALSE; case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: i_assert(ctx->encoding_buf->used == 0); data = input->data; size = pos = input->size; break; case MESSAGE_CTE_QP: { i_assert(ctx->encoding_buf->used == 0); buffer_set_used_size(ctx->buf, 0); if (ctx->qp == NULL) ctx->qp = qp_decoder_init(ctx->buf); (void)qp_decoder_more(ctx->qp, input->data, input->size, &pos, &error); data = ctx->buf->data; size = ctx->buf->used; /* eat away all input. qp-decoder buffers it internally. */ pos = input->size; break; } case MESSAGE_CTE_BASE64: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { ret = base64_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { ret = base64_decode(input->data, input->size, &pos, ctx->buf); } if (ret < 0) { /* corrupted base64 data, don't bother with the rest of it */ return FALSE; } if (ret == 0) { /* end of base64 input */ pos = input->size; buffer_set_used_size(ctx->encoding_buf, 0); } data = ctx->buf->data; size = ctx->buf->used; break; } if (ctx->encoding_buf->used != 0) buffer_delete(ctx->encoding_buf, 0, pos); else if (pos != input->size) { buffer_append(ctx->encoding_buf, input->data + pos, input->size - pos); } if (ctx->binary_input) { output->data = data; output->size = size; } else { buffer_set_used_size(ctx->buf2, 0); if (ctx->translation_size != 0) translation_buf_decode(ctx, &data, &size); pos = size; (void)charset_to_utf8(ctx->charset_trans, data, &pos, ctx->buf2); if (pos != size) { ctx->translation_size = size - pos; i_assert(ctx->translation_size <= sizeof(ctx->translation_buf)); memcpy(ctx->translation_buf, data + pos, ctx->translation_size); } output->data = ctx->buf2->data; output->size = ctx->buf2->used; } output->hdr = NULL; return TRUE; }
static gchar * getFrameText (struct id3_tag *tag, char *frame_name) { const id3_ucs4_t *string; struct id3_frame *frame; union id3_field *field; gchar *utf8 = NULL; enum id3_field_textencoding encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1; frame = id3_tag_findframe (tag, frame_name, 0); if (!frame) return NULL; /* Find the encoding used for the field */ field = id3_frame_field (frame, 0); //printf ("field: %p\n", field); if (field && (id3_field_type (field) == ID3_FIELD_TYPE_TEXTENCODING)) { encoding = field->number.value; //printf ("encoding: %d\n", encoding); } if (strcmp (frame_name, ID3_FRAME_COMMENT) == 0) field = id3_frame_field (frame, 3); else field = id3_frame_field (frame, 1); //printf ("field: %p\n", field); if (!field) return NULL; if (strcmp (frame_name, ID3_FRAME_COMMENT) == 0) string = id3_field_getfullstring (field); else string = id3_field_getstrings (field, 0); // g_debug("string: %s\n", string); if (!string) return NULL; if (strcmp (frame_name, ID3_FRAME_GENRE) == 0) string = id3_genre_name (string); if (encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1) { /* ISO_8859_1 is just a "marker" -- most people just drop whatever coding system they are using into it, so we use charset_to_utf8() to convert to utf8 */ id3_latin1_t *raw = id3_ucs4_latin1duplicate (string); utf8 = (gchar *) charset_to_utf8 (raw); g_free (raw); } else { /* Standard unicode is being used -- we won't have to worry about charsets then. */ // g_debug("This frame is a Unicode frame!\n"); utf8 = (gchar *) id3_ucs4_utf8duplicate (string); } // g_debug("Found tag: %s, value: %s\n", frame_name, utf8); return utf8; }
/** * Open and scan the metadata of the m4a/mp4/... file * and populate the given track. */ void AP_read_metadata(const char *filePath, Track *track) { FILE *mp4File; Trackage *trackage; uint8_t track_cur; uint8_t txttrack_cur; gboolean audio_or_video_found = FALSE; gboolean has_quicktime_chaps = FALSE; uint32_t timescale = 0; APar_ScanAtoms(filePath, true); mp4File = openSomeFile(filePath, true); trackage = APar_ExtractDetails(mp4File, SHOW_TRACK_INFO); for (track_cur = 0; track_cur < trackage->total_tracks; ++track_cur) { TrackInfo *info = trackage->infos[track_cur]; if ((info->type_of_track & AUDIO_TRACK) || (info->type_of_track & VIDEO_TRACK) || (info->type_of_track & DRM_PROTECTED_TRACK)) { /* * the info->duration is in the track's timescale units so must be divided by that * value to get seconds, while track->tracklen in gtkpod is in ms */ float duration = ((float) info->duration / (float) info->parent->movie_info->timescale) * 1000; track->tracklen = (gint32) duration; track->bitrate = APar_calculate_bitrate(info); track->samplerate = info->media_sample_rate; audio_or_video_found = TRUE; break; } } for (txttrack_cur = 0; audio_or_video_found && txttrack_cur < trackage->total_tracks; ++txttrack_cur) { TrackInfo *txtinfo = trackage->infos[txttrack_cur]; char buf[128]; // search for chapter track if (!(txtinfo->type_of_track & TEXT_TRACK)) continue; // see if the AV track's chap refers to this text track // chap: 0: atom size 4: 'chap' 8,12,...,8+(N-1)*4: (0: referenced track ID) snprintf(buf, sizeof(buf), "moov.trak[%u].tref.chap", track_cur + 1); AtomicInfo* chapAtom = APar_FindAtom(buf, false, SIMPLE_ATOM, 0); if (!chapAtom) continue; int entry_count = (chapAtom->AtomicLength - 8) / 4; for (int i = 0; i < entry_count; ++i) { if (APar_read32(buf, mp4File, chapAtom->AtomicStart + 8 + i * 4) == txtinfo->track_id) { has_quicktime_chaps = TRUE; timescale = txtinfo->media_sample_rate; break; } } if (has_quicktime_chaps) break; } if (has_quicktime_chaps) { // found a chapter... now get the chapter data from the text track char buf[128]; // stts: 0: atom size 4: 'stts' 8: version 12: entry count 16,24,...,16+(N-1)*8: (0: frame count 4: duration) snprintf(buf, sizeof(buf), "moov.trak[%u].mdia.minf.stbl.stts", txttrack_cur + 1); AtomicInfo* sampleAtom = APar_FindAtom(buf, false, VERSIONED_ATOM, 0); // stsz: 0: atom size 4: 'stsz' 8: version 12: size of all (or 0) 16: entry count 20,24,...,20+(N-1)*4: (0: sample size) snprintf(buf, sizeof(buf), "moov.trak[%u].mdia.minf.stbl.stsz", txttrack_cur + 1); AtomicInfo* sampleSizeAtom = APar_FindAtom(buf, false, VERSIONED_ATOM, 0); // stco: 0: atom size 4: 'stco' 8: version 12: entry count 16,20,...,16+(N-1)*4: (0: sample byte offset) snprintf(buf, sizeof(buf), "moov.trak[%u].mdia.minf.stbl.stco", txttrack_cur + 1); AtomicInfo* sampleOffsetAtom = APar_FindAtom(buf, false, VERSIONED_ATOM, 0); // We must have a valid sampleAtom to know chapter times. If sampleSizeAtom or sampleOffsetAtom is invalid, // we can do without them (and instead create a default chapter name). if (sampleAtom && sampleAtom->AtomicLength >= 16) { Itdb_Chapterdata *chapterdata = itdb_chapterdata_new(); uint32_t stts_entry_count = APar_read32(buf, mp4File, sampleAtom->AtomicStart + 12); uint32_t stsz_entry_count = !sampleSizeAtom || sampleSizeAtom->AtomicLength < 20 ? 0 : APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 16); uint32_t stco_entry_count = !sampleOffsetAtom || sampleOffsetAtom->AtomicLength < 16 ? 0 : APar_read32(buf, mp4File, sampleOffsetAtom->AtomicStart + 12); uint32_t stsz_all_size = !sampleSizeAtom || sampleSizeAtom->AtomicLength < 16 ? 0 : APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 12); uint32_t start_time = 0; u_int32_t max_frame_size = stsz_all_size; // if stsz_all_size specified, use only that size for (int i = 0; !stsz_all_size && i < stsz_entry_count; ++i) { uint32_t chap_name_len = APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 20 + i * 4); if (chap_name_len > max_frame_size) max_frame_size = chap_name_len; } max_frame_size += 1; // for trailing '\0' (unneeded?), and to make sure that malloc() gets passed at least 1 char * namebuf = (char *)malloc(max_frame_size * sizeof(char)); for (int i = 0; i < stts_entry_count; ++i) { gchar *title = NULL; uint32_t chap_name_len = stsz_all_size; uint32_t chap_offset = 0; if (stsz_all_size == 0 && i < stsz_entry_count) chap_name_len = APar_read32(buf, mp4File, sampleSizeAtom->AtomicStart + 20 + i * 4); if (i < stco_entry_count) chap_offset = APar_read32(buf, mp4File, sampleOffsetAtom->AtomicStart + 16 + i * 4); if (chap_offset != 0) APar_readX(namebuf, mp4File, chap_offset, chap_name_len); else // If the location of the chapter name is unknown, trigger default chapter naming chap_name_len = 0; if (chap_name_len > 2) { int titlelength = (namebuf[0] << 8) + namebuf[1]; // if the stsz atom and the title value disagree, use the smaller one for safety titlelength = (titlelength > chap_name_len) ? chap_name_len : titlelength; // If a title begins with 0xFFFE, it's a UTF-16 title if (titlelength >= 2 && namebuf[2] == 0xff && namebuf[3] == 0xfe) title = g_utf16_to_utf8((const gunichar2 *) &namebuf[4], titlelength - 2, NULL, NULL, NULL); else title = g_strndup(&namebuf[2], titlelength); } else { // chapter title couldn't be found; create our own titles (and some ipods don't display them anyway). // Translators: this string is used to create a chapter title when no chapter title could be found title = g_strdup_printf(_("Chapter %3d"), i); } if (!timescale) // assume 1000, also, don't divide by 0 timescale = 1000; double duration_ms = (double)start_time * 1000.0 / (double)timescale; itdb_chapterdata_add_chapter(chapterdata, duration_ms, title); g_free(title); if (i < (stts_entry_count - 1)) // skip this stage after the last chapter has been added { uint32_t frame_count = APar_read32(buf, mp4File, sampleAtom->AtomicStart + 16 + i * 8); uint32_t duration = APar_read32(buf, mp4File, sampleAtom->AtomicStart + 20 + i * 8); start_time += frame_count * duration; } } if (namebuf) free(namebuf); if (track->chapterdata) // if there was already chapter data, don't leak it itdb_chapterdata_free(track->chapterdata); track->chapterdata = itdb_chapterdata_duplicate(chapterdata); itdb_chapterdata_free(chapterdata); } } // TODO: add support for Nero-style mp4 chapters if (prefs_get_int("readtags")) { char* value = NULL; // MP4 Title value = find_atom_value(TITLE); if (value) { track->title = g_strdup(value); free(value); } // MP4 Artist value = find_atom_value(ARTIST); if (value) { track->artist = g_strdup(value); free(value); } // MP4 Album Artist value = find_atom_value(ALBUM_ARTIST); if (value) { track->albumartist = g_strdup(value); free(value); } // MP4 Composer value = find_atom_value(COMPOSER); if (value) { track->composer = g_strdup(value); free(value); } // MP4 Comment value = find_atom_value(COMMENT); if (value) { track->comment = g_strdup(value); free(value); } // MP4 Description value = find_atom_value(DESCRIPTION); if (value) { track->description = g_strdup(value); free(value); } // MP4 Keywords value = find_atom_value(KEYWORD); if (value) { track->keywords = g_strdup(value); free(value); } // MP4 Year value = find_atom_value(YEAR); if (value) { track->year = atoi(value); free(value); } // MP4 Album value = find_atom_value(ALBUM); if (value) { track->album = g_strdup(value); free(value); } // MP4 Track No. and Total value = find_atom_value(TRACK_NUM_AND_TOTAL); if (value) { const char* delimiter = " of "; char *result = NULL; result = strtok(value, delimiter); if (result) track->track_nr = atoi(result); result = strtok(NULL, delimiter); if (result) track->tracks = atoi(result); free(value); } // MP4 Disk No. and Total value = find_atom_value(DISK_NUM_AND_TOTAL); if (value) { const char* delimiter = " of "; char *result = NULL; result = strtok(value, delimiter); if (result) track->cd_nr = atoi(result); result = strtok(NULL, delimiter); if (result) track->cds = atoi(result); free(value); } // MP4 Grouping value = find_atom_value(GROUPING); if (value) { track->grouping = g_strdup(value); free(value); } // MP4 Genre - note: can be either a standard or custom genre // standard genre value = find_atom_value(STANDARD_GENRE); if (value) { track->genre = charset_to_utf8(value); // Should not free standard genres } else { // custom genre value = find_atom_value(CUSTOM_GENRE); if (value) { track->genre = g_strdup(value); free(value); } } // MP4 Tempo / BPM value = find_atom_value(TEMPO); if (value) { track->BPM = atoi(value); free(value); } // MP4 Lyrics value = find_atom_value(LYRICS); if (value) { track->lyrics_flag = 0x01; free(value); } // MP4 TV Show value = find_atom_value(TV_SHOW); if (value) { track->tvshow = g_strdup(value); free(value); } // MP4 TV Episode value = find_atom_value(TV_EPISODE); if (value) { track->tvepisode = g_strdup(value); free(value); } // MP4 TV Episode No. value = find_atom_value(TV_EPISODE_NO); if (value) { track->episode_nr = atoi(value); free(value); } // MP4 TV Network value = find_atom_value(TV_NETWORK_NAME); if (value) { track->tvnetwork = g_strdup(value); free(value); } // MP4 TV Season No. value = find_atom_value(TV_SEASON_NO); if (value) { track->season_nr = atoi(value); free(value); } // MP4 Media Type value = find_atom_value(MEDIA_TYPE); if (value) { stiks * stik = MatchStikString(value); if (stik) { track->mediatype = mediaTypeTagToMediaType(stik->stik_number); } // Should not free standard media types } // MP4 Compilation flag value = find_atom_value(COMPILATION); if (value) { track->compilation = !g_strcmp0("true", value); free(value); } // MP4 Category value = find_atom_value(CATEGORY); if (value) { track->category = g_strdup(value); free(value); } // MP4 Podcast URL value = find_atom_value(PODCAST_URL); if (value) { track->podcasturl = g_strdup(value); free(value); } value = find_atom_value(GAPLESS_FLAG); if (value) { track->gapless_track_flag = atoi(value); free(value); } // MP4 Sort Title value = find_atom_value(SORT_TITLE); if (value) { track->sort_title = g_strdup(value); free(value); } // MP4 Sort Artist value = find_atom_value(SORT_ARTIST); if (value) { track->sort_artist = g_strdup(value); free(value); } // MP4 Sort Album Artist value = find_atom_value(SORT_ALBUM_ARTIST); if (value) { track->sort_albumartist = g_strdup(value); free(value); } // MP4 Sort Composer value = find_atom_value(SORT_COMPOSER); if (value) { track->sort_composer = g_strdup(value); free(value); } // MP4 Sort Album value = find_atom_value(SORT_ALBUM); if (value) { track->sort_album = g_strdup(value); free(value); } // MP4 Sort TV Show value = find_atom_value(SORT_TV_SHOW); if (value) { track->sort_tvshow = g_strdup(value); free(value); } if (prefs_get_int("coverart_apic")) { gchar *tmp_file_prefix = g_build_filename(g_get_tmp_dir(), "ttt", NULL); gchar *tmp_file; AtomicInfo *info = find_atom("covr"); if (info) { // Extract the data to a temporary file tmp_file = APar_ExtractAAC_Artwork(info->AtomicNumber, tmp_file_prefix, 1); g_free(tmp_file_prefix); if (tmp_file && g_file_test(tmp_file, G_FILE_TEST_EXISTS)) { // Set the thumbnail using the tmp file GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(tmp_file, NULL); if (pixbuf) { itdb_track_set_thumbnails_from_pixbuf(track, pixbuf); g_object_unref(pixbuf); } g_remove(tmp_file); } if (tmp_file) g_free(tmp_file); } } } APar_FreeMemory(); }
int cmd_update(int argc, char **argv, struct mpd_connection *conn) { if (contains_absolute_path(argc, argv) && !path_prepare(conn)) printErrorAndExit(conn); if (!mpd_command_list_begin(conn, false)) printErrorAndExit(conn); int i = 0; const char *update = ""; if (argc > 0) update = charset_to_utf8(argv[i]); do { char *tmp = strdup(update); strip_trailing_slash(tmp); const char *path = tmp; const char *relative_path = to_relative_path(path); if (relative_path != NULL) path = relative_path; mpd_send_update(conn, path); free(tmp); } while (++i < argc && (update = charset_to_utf8(argv[i])) != NULL); if (!mpd_command_list_end(conn)) printErrorAndExit(conn); /* obtain the last "update id" response */ unsigned id = 0; while (true) { unsigned next_id = mpd_recv_update_id(conn); if (next_id == 0) break; id = next_id; } my_finishCommand(conn); while (options.wait) { /* idle until an update finishes */ enum mpd_idle idle = mpd_run_idle_mask(conn, MPD_IDLE_UPDATE); struct mpd_status *status; unsigned current_id; if (idle == 0) printErrorAndExit(conn); /* determine the current "update id" */ status = getStatus(conn); current_id = mpd_status_get_update_id(status); mpd_status_free(status); /* is our last queued update finished now? */ if (current_id == 0 || current_id > id || (id > 1 << 30 && id < 1000)) /* wraparound */ break; } return 1; }
/** * Using the given track, set the metadata of the target * file */ void AP_write_metadata(Track *track, const char *filePath, GError **error) { ExtraTrackData *etr; gchar *atom; gchar *value; g_return_if_fail(track); APar_ScanAtoms(filePath); if (metadata_style != ITUNES_STYLE) { gchar *fbuf = charset_to_utf8(filePath); gtkpod_log_error(error, g_strdup_printf(_("ERROR %s is not itunes style."), fbuf)); g_free(fbuf); return; } // Title set_limited_text_atom_value(TITLE, track->title); // Artist set_limited_text_atom_value(ARTIST, track->artist); // Album Artist set_limited_text_atom_value(ALBUM_ARTIST, track->albumartist); // Album set_limited_text_atom_value(ALBUM, track->album); // Genre APar_MetaData_atomGenre_Set(track->genre); // Track Number and Total atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TRACK_NUM_AND_TOTAL, DATA); if (track->track_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { gchar *track_info = g_strdup_printf("%d / %d", track->track_nr, track->tracks); short tracknumData_atom = APar_MetaData_atom_Init(atom, track_info, AtomFlags_Data_Binary); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->track_nr, 16); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->tracks, 16); APar_Unified_atom_Put(tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); g_free(track_info); } g_free(atom); // Disk Number and Total atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, DISK_NUM_AND_TOTAL, DATA); if (track->cd_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { gchar *disk_info = g_strdup_printf("%d / %d", track->cd_nr, track->cds); short disknumData_atom = APar_MetaData_atom_Init(atom, disk_info, AtomFlags_Data_Binary); APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->cd_nr, 16); APar_Unified_atom_Put(disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->cds, 16); g_free(disk_info); } g_free(atom); // Comment set_limited_text_atom_value(COMMENT, track->comment); // Year gchar *yr = NULL; if (track->year > 0) yr = g_strdup_printf("%d", track->year); set_limited_text_atom_value(YEAR, yr); if (yr) g_free(yr); // Lyrics etr = (ExtraTrackData *) track->userdata; if (etr) write_lyrics_internal(etr->lyrics, filePath, error); // Composer set_limited_text_atom_value(COMPOSER, track->composer); // Grouping set_limited_text_atom_value(GROUPING, track->grouping); // Description set_limited_text_atom_value(DESCRIPTION, track->description); // TV Network set_limited_text_atom_value(TV_NETWORK_NAME, track->tvnetwork); // TV Show Name set_limited_text_atom_value(TV_SHOW, track->tvshow); // TV Episode set_limited_text_atom_value(TV_EPISODE, track->tvepisode); // Compilation atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, COMPILATION, DATA); if (!track->compilation) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { //compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init value = g_strdup_printf("%d", track->compilation); short compilationData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(compilationData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is compilation g_free(value); } g_free(atom); // Tempo / BPM atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TEMPO, DATA); if (!track->BPM) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { //bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is already accounted for in APar_MetaData_atom_Init value = g_strdup_printf("%d", track->BPM); short bpmData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(bpmData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->BPM, 16); g_free(value); } g_free(atom); // Media Type atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, MEDIA_TYPE, DATA); guint8 mediaTypeTag = mediaTypeToMediaTypeTag(track->mediatype); value = g_strdup_printf("%d", track->season_nr); short stikData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(stikData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, mediaTypeTag, 8); g_free(value); g_free(atom); // TV Season No. atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TV_SEASON_NO, DATA); if (track->season_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { value = g_strdup_printf("%d", track->season_nr); short tvseasonData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->season_nr, 16); g_free(value); } g_free(atom); // TV Episode No. atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, TV_EPISODE_NO, DATA); if (track->episode_nr == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { value = g_strdup_printf("%d", track->episode_nr); short tvepisodenumData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16); APar_Unified_atom_Put(tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, track->episode_nr, 16); g_free(value); } g_free(atom); // Keywords set_limited_text_atom_value(KEYWORD, track->keywords); // Podcast Category set_limited_text_atom_value(CATEGORY, track->category); // Podcast URL atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, PODCAST_URL, DATA); if (!track->podcasturl || strlen(track->podcasturl) == 0) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { short podcasturlData_atom = APar_MetaData_atom_Init(atom, track->podcasturl, AtomFlags_Data_Binary); APar_Unified_atom_Put(podcasturlData_atom, track->podcasturl, UTF8_iTunesStyle_Binary, 0, 0); } g_free(atom); // Gapless Playback atom = g_strdup_printf("%s.%s.%s", ILST_FULL_ATOM, GAPLESS_FLAG, DATA); if (!track->gapless_track_flag) { APar_RemoveAtom(atom, VERSIONED_ATOM, 0); } else { value = g_strdup_printf("%d", track->gapless_track_flag); short gaplessData_atom = APar_MetaData_atom_Init(atom, value, AtomFlags_Data_UInt); APar_Unified_atom_Put(gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is gapl g_free(value); } g_free(atom); // Sort Title set_limited_text_atom_value(SORT_TITLE, track->sort_title); // Sort Artist set_limited_text_atom_value(SORT_ARTIST, track->sort_artist); // Sort Album Artist set_limited_text_atom_value(SORT_ALBUM_ARTIST, track->sort_albumartist); // Sort Composer set_limited_text_atom_value(SORT_COMPOSER, track->sort_composer); // Sort Album set_limited_text_atom_value(SORT_ALBUM, track->sort_album); // Sort TV Show set_limited_text_atom_value(SORT_TV_SHOW, track->sort_tvshow); if (prefs_get_int("coverart_apic")) { GdkPixbuf *pixbuf = (GdkPixbuf*) itdb_artwork_get_pixbuf(track->itdb->device, track->artwork, -1, -1); if (!pixbuf) { // Destroy any existing artwork if any APar_MetaData_atomArtwork_Set("REMOVE_ALL", NULL); } else { gchar *tmp_file = g_build_filename(g_get_tmp_dir(), "ttt.jpg", NULL); GError *pixbuf_err = NULL; gdk_pixbuf_save(pixbuf, tmp_file, "jpeg", &pixbuf_err, "quality", "100", NULL); if (!pixbuf_err) { APar_MetaData_atomArtwork_Set(tmp_file, NULL); g_remove(tmp_file); } else { gtkpod_log_error(error, g_strdup_printf(_("ERROR failed to change track file's artwork.") )); g_error_free(pixbuf_err); return; } g_free(tmp_file); g_object_unref(pixbuf); } } // after all the modifications are enacted on the tree in memory // then write out the changes APar_DetermineAtomLengths(); openSomeFile(filePath, true); APar_WriteFile(filePath, NULL, true); APar_FreeMemory(); }
Track *wav_get_file_info(const gchar *filename, GError **error) { Track *track = NULL; gchar *fn; gchar magic[4]; gulong len; WaveFile *wav_file; wav_file = g_malloc(sizeof(WaveFile)); memset(wav_file, 0, sizeof(WaveFile)); if (!(wav_file->file = fopen(filename, "rb"))) { gchar *fn = charset_to_utf8(filename); gtkpod_log_error(error, g_strdup_printf(_("Could not open '%s' for reading.\n"), fn)); g_free(fn); g_free(wav_file); wav_file = NULL; return NULL; } fread(magic, 1, 4, wav_file->file); if (strncmp(magic, "RIFF", 4) != 0) goto file_error; read_le_long(wav_file->file, &len); fread(magic, 1, 4, wav_file->file); if (strncmp(magic, "WAVE", 4) != 0) goto file_error; for (;;) { fread(magic, 1, 4, wav_file->file); if (!read_le_long(wav_file->file, &len)) goto file_error; if (!strncmp("fmt ", magic, 4)) break; fseek(wav_file->file, len, SEEK_CUR); } if (len < 16) goto file_error; read_le_short(wav_file->file, &wav_file->format_tag); switch (wav_file->format_tag) { case WAVE_FORMAT_UNKNOWN: case WAVE_FORMAT_ALAW: case WAVE_FORMAT_MULAW: case WAVE_FORMAT_ADPCM: case WAVE_FORMAT_OKI_ADPCM: case WAVE_FORMAT_DIGISTD: case WAVE_FORMAT_DIGIFIX: case IBM_FORMAT_MULAW: case IBM_FORMAT_ALAW: case IBM_FORMAT_ADPCM: goto file_error; } read_le_short(wav_file->file, &wav_file->channels); read_le_long(wav_file->file, &wav_file->samples_per_sec); read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec); read_le_short(wav_file->file, &wav_file->block_align); read_le_short(wav_file->file, &wav_file->bits_per_sample); /* if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) goto file_error;*/ len -= 16; if (len) fseek(wav_file->file, len, SEEK_CUR); for (;;) { fread(magic, 4, 1, wav_file->file); if (!read_le_long(wav_file->file, &len)) goto file_error; if (!strncmp("data", magic, 4)) break; fseek(wav_file->file, len, SEEK_CUR); } track = gp_track_new(); track->mediatype = ITDB_MEDIATYPE_AUDIO; track->bitrate = wav_file->samples_per_sec * wav_file->channels * wav_file->bits_per_sample; track->samplerate = wav_file->samples_per_sec; track->tracklen = 1000 * ((double) 8 * len / track->bitrate); track->bitrate /= 1000; /* change to kbps */ track->filetype = g_strdup(_("WAV audio file")); fclose(wav_file->file); g_free(wav_file); wav_file = NULL; return track; file_error: fclose(wav_file->file); g_free(wav_file); wav_file = NULL; fn = charset_to_utf8(filename); gtkpod_log_error(error, g_strdup_printf(_("%s does not appear to be a supported wav file.\n"), fn)); g_free(fn); return NULL; }