/******************************************************************** * FUNCTION read_txid_file * * Read the transaction ID file and return the current ID value found * * INPUTS: * txidfile == full filespec of the transaction ID file * curid == address of return ID * * OUTPUTS: * *curid == return current ID (if return NO_ERR * * RETURNS: * status *********************************************************************/ static status_t read_txid_file (const xmlChar *txidfile, cfg_transaction_id_t *curid) { assert( txidfile && "txidfile is NULL" ); assert( curid && "curid is NULL" ); *curid = 0; status_t res = NO_ERR; FILE *fil = fopen((const char *)txidfile, "r"); if (!fil) { res = errno_to_status(); log_error("\nError: Open txid file for read failed (%s)", get_error_string(res)); return res; } char buffer [128]; if (fgets(buffer, sizeof buffer, fil)) { /* expecting just 1 line containing the ASCII encoding of * the transaction ID */ log_debug4("\nRead transaction ID line '%s'", buffer); uint32 len = xml_strlen((const xmlChar *)buffer); if (len > 1) { /* strip ending newline */ buffer[len-1] = 0; ncx_num_t num; ncx_init_num(&num); res = ncx_convert_num((xmlChar *)buffer, NCX_NF_DEC, NCX_BT_UINT64, &num); if (res == NO_ERR) { agt_cfg_txid = num.ul; *curid = num.ul; log_debug3("\nGot transaction ID line '%llu'", (unsigned long long)num.ul); } else { log_error("\nError: txid is not valid (%s)", get_error_string(res)); } ncx_clean_num(NCX_BT_UINT64, &num); } else { res = ERR_NCX_INVALID_NUM; log_error("\nError: txid is not valid (%s)", get_error_string(res)); } } else { res = errno_to_status(); log_error("\nError: Read txid file failed (%s)", get_error_string(res)); } fclose(fil); return res; } /* read_txid_file */
static gboolean library_view_update_info(library_view_t* view) { if (view->run_timeout) { // update play buttons playlist_player_t* player = backtobasics_player(view->btb); if (view->btn_play == NULL) { view->btn_play = GTK_WIDGET(gtk_builder_get_object(view->builder, "tbtn_library_play")); } if (view->btn_pause == NULL) { view->btn_pause = GTK_WIDGET(gtk_builder_get_object(view->builder, "tbtn_library_pause")); } GtkWidget* btn_play = view->btn_play; GtkWidget* btn_pause = view->btn_pause; if (!playlist_player_is_playing(player)) { if (!gtk_widget_get_visible(btn_play)) gtk_widget_show_all(btn_play); if (gtk_widget_get_visible(btn_pause)) gtk_widget_hide(btn_pause); } else if (playlist_player_is_playing(player)) { if (gtk_widget_get_visible(btn_play)) gtk_widget_hide(btn_play); if (!gtk_widget_get_visible(btn_pause)) gtk_widget_show_all(btn_pause); } // update slider and time long tr_tm = playlist_player_get_track_position_in_ms(player); track_t* track = playlist_player_get_track(player); int index = playlist_player_get_track_index(player); int a = tr_tm / 1000; int b = view->time_in_ms / 1000; if (a != b) { view->time_in_ms = tr_tm; int min = tr_tm / 1000 / 60; int sec = (tr_tm / 1000) % 60; { char s[200]; sprintf(s,"<span size=\"x-small\"><b><i>%02d:%02d</i></b></span>", min, sec); GtkLabel* lbl = GTK_LABEL(gtk_builder_get_object(view->builder, "lbl_time")); gtk_label_set_markup(lbl, s); // update presets reflect_presets(view->btb); } GtkScale* sc_playback = GTK_SCALE(gtk_builder_get_object(view->builder, "sc_library_playback")); double perc = 0.0; if (track != NULL) { int len_in_ms = track_get_length_in_ms(track); if (len_in_ms != view->len_in_ms) { view->len_in_ms = len_in_ms; int min = len_in_ms / 1000 / 60; int sec = (len_in_ms / 1000) % 60; { char s[200]; sprintf(s,"<span size=\"x-small\"><b><i>%02d:%02d</i></b></span>", min, sec); GtkLabel* lbl = GTK_LABEL(gtk_builder_get_object(view->builder, "lbl_total")); gtk_label_set_markup(lbl, s); } } perc = (((double) tr_tm) / ((double) len_in_ms)) * 100.0; if (!view->sliding) { gtk_range_set_value(GTK_RANGE(sc_playback), perc); } } // update track info if (index != view->track_index || (track != NULL && track_get_id(track) != view->track_id)) { log_debug3("updating track info, index = %d, %p", index, track); view->track_index = index; if (track != NULL) { // fetch lyric if possible if (strcmp(track_get_lyric(track),"") == 0) { struct lyric_cb* cb = (struct lyric_cb*) mc_malloc(sizeof(struct lyric_cb)); cb->track_id = mc_strdup(track_get_id(track)); cb->view = view; fetch_lyric(track, library_view_process_lyric, cb); } else { struct lyric_cb* cb = (struct lyric_cb*) mc_malloc(sizeof(struct lyric_cb)); cb->track_id = mc_strdup(track_get_id(track)); cb->view = view; library_view_process_lyric(mc_strdup(track_get_lyric(track)), cb); } // Print artist info view->track_id = track_get_id(track); log_debug2("artid = %s", track_get_artid(track)); char s[200]; char c = ','; char c1 = ','; char *artist = text_to_html(track_get_artist(track)); char *title = text_to_html(track_get_title(track)); char *piece = text_to_html(track_get_piece(track)); if (strcmp(track_get_artist(track), "") == 0) { c = ' '; } if (strcmp(track_get_piece(track), "") == 0) { c1 = ' '; } snprintf(s, 125, "%s%c %s%c %s", artist, c, piece, c1, title ); mc_free(artist); mc_free(title); mc_free(piece); char ss[400]; log_debug2("s = %s", s); sprintf(ss,"<span size=\"x-small\"><i><b>%s</b></i></span>",s); GtkLabel* lbl = GTK_LABEL(gtk_builder_get_object(view->builder, "lbl_song_info")); gtk_label_set_markup(lbl, ss); log_debug2("artid = %s", track_get_artid(track)); file_info_t* info = file_info_new(track_get_artid(track)); if (!file_info_is_file(info)) { file_info_destroy(info); char *path = backtobasics_logo(view->btb); info = file_info_new(path); mc_free(path); //info = file_info_new(backtobasics_logo(view->btb)); } if (file_info_is_file(info)) { GError *err = NULL; GdkPixbuf* pb = gdk_pixbuf_new_from_file_at_scale(file_info_path(info), view->img_w, view->img_h, TRUE, &err ); if (pb != NULL) { GtkImage* img = GTK_IMAGE(gtk_builder_get_object(view->builder, "img_art")); gtk_image_set_from_pixbuf(img, pb); g_object_unref(pb); } else { log_error3("error loading image art: %d, %s", err->code, err->message); //g_free(err); } } file_info_destroy(info); } log_debug("track hash"); // Select the track in the librarylist if the librarylist is still // the same if (playlist_model_tracks_hash(view->playlist_model) == playlist_player_get_hash(player)) { GtkTreeView* tview = view->tview; GtkTreePath* path = gtk_tree_path_new(); gtk_tree_path_append_index(path, index); gtk_tree_view_set_cursor(tview, path, NULL, FALSE); gtk_tree_path_free(path); } log_debug("track hash 2"); } //log_debug3("lib hash = %lld, pl hash = %lld", view->library_list_hash, playlist_player_get_hash(player)); // TODO if (playlist_model_tracks_hash(view->playlist_model) == playlist_player_get_hash(player)) { //view->track_index = -1; } else { GtkTreeView* tview = view->tview; GtkTreeSelection* sel = gtk_tree_view_get_selection(tview); gtk_tree_selection_unselect_all(sel); } } // update repeat info playlist_player_repeat_t repeat = playlist_player_get_repeat(player); if (view->repeat != repeat) { log_debug3("repeat = %d, view repeat = %d", repeat, view->repeat); view->repeat = repeat; GtkWidget* r_btn = GTK_WIDGET(gtk_builder_get_object(view->builder, "tbtn_repeat")); GtkWidget* r1_btn = GTK_WIDGET(gtk_builder_get_object(view->builder, "tbtn_repeat_one")); GtkWidget* rlist_btn = GTK_WIDGET(gtk_builder_get_object(view->builder, "tbtn_repeat_all")); log_debug4("r = %p, r1 = %p, rall = %p", r_btn, r1_btn, rlist_btn); switch (repeat) { case PLP_NO_REPEAT: { gtk_widget_show_all(r_btn); gtk_widget_hide(rlist_btn); gtk_widget_hide(r1_btn); } break; case PLP_TRACK_REPEAT: { gtk_widget_hide(r_btn); gtk_widget_show_all(r1_btn); gtk_widget_hide(rlist_btn); } break; case PLP_LIST_REPEAT: { gtk_widget_hide(r1_btn); gtk_widget_show_all(rlist_btn); gtk_widget_hide(r_btn); } break; } } return TRUE; } else { return FALSE; } }
/******************************************************************** * FUNCTION consume_node * * Internal consume XML node function * see agt_xml_consume_node for details. * * EXTRA INPUTS: * eoferr == TRUE if an End of File error should be generated * == FALSE if not * nserr == TRUE if bad namespace should be checked * == FALSE if not * clean == TRUE is a string should be cleaned before returned * == FALSE if a string node should be returned as-is * * RETURNS: * status of the operation * Try to fail on fatal errors only *********************************************************************/ static status_t consume_node (ses_cb_t *scb, boolean advance, xml_node_t *node, ncx_layer_t layer, xml_msg_hdr_t *msghdr, boolean eoferr, boolean nserr, boolean clean) { int ret, nodetyp; const xmlChar *badns; xmlChar *valstr, *namestr; uint32 len; status_t res, res2; boolean done; /* init local vars */ done = FALSE; res = NO_ERR; res2 = NO_ERR; badns = NULL; /* loop past any unused xmlTextReader node types */ while (!done) { /* check if a new node should be read */ if (advance) { /* advance the node pointer */ ret = xmlTextReaderRead(scb->reader); if (ret != 1) { /* do not treat this as an internal error */ res = ERR_XML_READER_EOF; if (msghdr && eoferr) { /* generate an operation-failed error */ agt_record_error(scb, msghdr, layer, res, NULL, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } return res; } } /* get the node depth to match the end node correctly */ node->depth = xmlTextReaderDepth(scb->reader); if (node->depth == -1) { /* this never actaully happens */ SET_ERROR(ERR_XML_READER_INTERNAL); node->depth = 0; } /* get the internal nodetype, check it and convert it */ nodetyp = xmlTextReaderNodeType(scb->reader); switch (nodetyp) { case XML_ELEMENT_NODE: /* classify element as empty or start */ if (xmlTextReaderIsEmptyElement(scb->reader)) { node->nodetyp = XML_NT_EMPTY; } else { node->nodetyp = XML_NT_START; } done = TRUE; break; case XML_ELEMENT_DECL: node->nodetyp = XML_NT_END; done = TRUE; break; case XML_TEXT_NODE: /* case XML_DTD_NODE: */ node->nodetyp = XML_NT_STRING; done = TRUE; break; default: /* unused node type -- keep trying */ if (LOGDEBUG3) { log_debug3("\nxml_consume_node: skip unused node (%s)", xml_get_node_name(nodetyp)); } advance = TRUE; } } /* finish the node, depending on its type */ switch (node->nodetyp) { case XML_NT_START: case XML_NT_END: case XML_NT_EMPTY: /* get the element QName */ namestr = xml_strdup(xmlTextReaderConstName(scb->reader)); if (!namestr) { res = ERR_INTERNAL_MEM; } else { node->qname = namestr; /* check for namespace prefix in the name * only error returned is unknown-namespace */ len = 0; res = xml_check_ns(scb->reader, namestr, &node->nsid, &len, &badns); if (!nserr && res != NO_ERR) { node->nsid = xmlns_inv_id(); len = 0; res = NO_ERR; } /* set the element name to the char after the prefix, if any */ node->elname = (const xmlChar *)(namestr+len); /* get all the attributes, except for XML_NT_END */ if (res == NO_ERR && node->nodetyp != XML_NT_END) { res2 = get_all_attrs(scb, node, &node->attrs, layer, msghdr, nserr); } /* Set the node module */ if (res == NO_ERR) { if (node->nsid) { node->module = xmlns_get_module(node->nsid); } else { /* no entry, use the default module (ncx) */ node->module = NCX_DEF_MODULE; } } } break; case XML_NT_STRING: /* get the text value -- this is a malloced string */ node->simval = NULL; valstr = xmlTextReaderValue(scb->reader); if (valstr) { if (clean) { node->simfree = xml_copy_clean_string(valstr); } else { node->simfree = xml_strdup(valstr); } if (node->simfree) { node->simlen = xml_strlen(node->simfree); node->simval = (const xmlChar *)node->simfree; } /* see if this is a QName string; if so save the NSID */ xml_check_qname_content(scb->reader, node); xmlFree(valstr); } if (!node->simval) { /* prevent a NULL ptr reference */ node->simval = EMPTY_STRING; node->simlen = 0; node->simfree = NULL; } break; default: break; } if ((res != NO_ERR) && msghdr) { if (badns) { /* generate an operation-failed error */ agt_record_error(scb, msghdr, layer, res, node, NCX_NT_STRING, badns, NCX_NT_NONE, NULL); } else { agt_record_error(scb, msghdr, layer, res, node, NCX_NT_NONE, NULL, NCX_NT_NONE, NULL); } } if (LOGDEBUG4) { log_debug4("\nxml_consume_node: return (%d)", (res==NO_ERR) ? res2 : res); if (scb->state != SES_ST_INIT) { xml_dump_node(node); } } /* return general error first, then attribute error * It doesn't really matter since the caller will * assume all error reports have been queued upon return */ return (res==NO_ERR) ? res2 : res; } /* consume_node */
void* player_thread(void* _mp3_info) { log_debug("player thread started"); mp3_t* mp3_info = (mp3_t* ) _mp3_info; long current_position_in_ms = 0; long previous_position_in_ms = -1; long guard_position_in_ms = -1; el_bool playing = el_false; pthread_t thread_id; int no_count = 0; post_event(mp3_info->client_notification, AUDIO_READY, current_position_in_ms); audio_event_t *event; event = audio_event_fifo_dequeue(mp3_info->player_control); while (event->state != INTERNAL_CMD_DESTROY) { if (event->state != INTERNAL_CMD_NONE) { log_debug4("event = %s, %ld, %s", audio_event_name(event->state), event->position_in_ms, mp3_info->file_or_url); } audio_state_t event_state = event->state; long event_position = event->position_in_ms; audio_event_destroy(event); switch (event_state) { case INTERNAL_CMD_LOAD_FILE: { playing = el_false; // Stop stream, if playing if (!mp3_info->is_file) { mp3_info->continue_streaming = el_false; psem_wait(mp3_info->stream_ready); } if (mp3_info->is_open) { mpg123_close(mp3_info->handle); aodev_close(mp3_info->ao_handle); } mpg123_open(mp3_info->handle, mp3_info->file_or_url); mpg123_getformat(mp3_info->handle, &mp3_info->rate, &mp3_info->channels, &mp3_info->encoding); mp3_info->buffer_size = mpg123_outblock(mp3_info->handle); mc_free(mp3_info->buffer); mp3_info->buffer = mc_malloc(mp3_info->buffer_size * sizeof(char) ); int bytes_per_sample = get_encsize(mp3_info->encoding); aodev_set_format(mp3_info->ao_handle, bytes_per_sample * 8, mp3_info->rate, mp3_info->channels); aodev_open(mp3_info->ao_handle); mp3_info->is_open = el_true; mp3_info->is_file = el_true; mp3_info->can_seek = el_true; current_position_in_ms = 0; guard_position_in_ms = -1; { off_t l = mpg123_length(mp3_info->handle); if (l == MPG123_ERR) { mp3_info->length = -1; } else { mp3_info->length = (l * 1000) / mp3_info->rate; } psem_post(mp3_info->length_set); } } break; case INTERNAL_CMD_LOAD_URL: { playing = el_false; log_debug2("loading url %s", mp3_info->file_or_url); // Wait for feeding streams to end if (!mp3_info->is_file) { mp3_info->continue_streaming = el_false; psem_wait(mp3_info->stream_ready); } mp3_info->is_file = el_false; log_debug("current stream ended"); if (mp3_info->is_open) { mpg123_close(mp3_info->handle); aodev_close(mp3_info->ao_handle); mp3_info->is_open = el_false; } log_debug("aodev closed"); mpg123_open_feed(mp3_info->handle); log_debug("feed opened"); pthread_create(&thread_id, NULL, stream_thread, mp3_info); log_debug("stream thread started"); mp3_info->is_open = el_true; mp3_info->can_seek = el_false; current_position_in_ms = 0; guard_position_in_ms = -1; mp3_info->length = 0; mp3_info->continue_streaming = el_true; psem_post(mp3_info->length_set); } break; case INTERNAL_CMD_SEEK: { off_t pos = mpg123_timeframe(mp3_info->handle, (event_position / 1000.0)); mpg123_seek_frame(mp3_info->handle, pos, SEEK_SET); } break; case INTERNAL_CMD_PLAY: { playing = el_true; } break; case INTERNAL_CMD_PAUSE: { playing = el_false; } break; case INTERNAL_CMD_GUARD: { guard_position_in_ms = event_position; } break; case INTERNAL_CMD_SET_VOLUME: { double volume = ((double) event_position) / 1000.0; mpg123_volume(mp3_info->handle, volume); } case INTERNAL_CMD_NONE: break; default: break; } //log_debug3("guard = %d, playing = %d", guard_position_in_ms, playing); if (guard_position_in_ms >= 0 && current_position_in_ms >= guard_position_in_ms) { guard_position_in_ms = -1; post_event(mp3_info->client_notification, AUDIO_GUARD_REACHED, current_position_in_ms); } else if (playing) { if (mp3_info->is_file) { size_t bytes; int res = mpg123_read(mp3_info->handle, mp3_info->buffer, mp3_info->buffer_size, &bytes); if (res == MPG123_OK) { aodev_play_buffer(mp3_info->ao_handle, mp3_info->buffer, bytes); off_t frame = mpg123_tellframe(mp3_info->handle); double time_per_frame = (mpg123_tpf(mp3_info->handle)*1000.0); //static int prt = 1; //if (prt) { printf("tpf=%.6lf\n",time_per_frame);prt=0; } current_position_in_ms = (long) (frame * time_per_frame); // 1 frame is about 26 milliseconds if (previous_position_in_ms == -1) previous_position_in_ms = current_position_in_ms; if ((current_position_in_ms - previous_position_in_ms) >= STATE_REPORT_THRESHOLD) { post_event(mp3_info->client_notification, AUDIO_PLAYING, current_position_in_ms); } previous_position_in_ms = current_position_in_ms; } else if (res == MPG123_DONE) { post_event(mp3_info->client_notification, AUDIO_EOS, current_position_in_ms); playing = el_false; } else { post_event(mp3_info->client_notification, AUDIO_STATE_ERROR, current_position_in_ms); playing = el_false; } } else { // Stream playing if (mp3_stream_fifo_peek(mp3_info->stream_fifo) != NULL) { el_bool go_on = el_true; while (go_on && mp3_stream_fifo_peek(mp3_info->stream_fifo) != NULL) { memblock_t* blk = mp3_stream_fifo_dequeue(mp3_info->stream_fifo); mpg123_feed(mp3_info->handle, (const unsigned char*) memblock_as_str(blk), memblock_size(blk)); memblock_destroy(blk); size_t done; int err; unsigned char *audio; off_t frame_offset; do { err = mpg123_decode_frame(mp3_info->handle, &frame_offset, &audio, &done); switch(err) { case MPG123_NEW_FORMAT: mpg123_getformat(mp3_info->handle, &mp3_info->rate, &mp3_info->channels, &mp3_info->encoding); if (aodev_is_open(mp3_info->ao_handle)) { aodev_close(mp3_info->ao_handle); } aodev_set_format(mp3_info->ao_handle, get_encsize(mp3_info->encoding) * 8, mp3_info->rate, mp3_info->channels); aodev_open(mp3_info->ao_handle); break; case MPG123_OK: //log_debug2("playing buffer %d", done); aodev_play_buffer(mp3_info->ao_handle, audio, done); off_t frame = mpg123_tellframe(mp3_info->handle); double time_per_frame = (mpg123_tpf(mp3_info->handle)*1000.0); current_position_in_ms = (long) (frame * time_per_frame); // 1 frame is about 26 milliseconds if (previous_position_in_ms == -1) previous_position_in_ms = current_position_in_ms; if ((current_position_in_ms - previous_position_in_ms) >= STATE_REPORT_THRESHOLD) { post_event(mp3_info->client_notification, AUDIO_PLAYING, current_position_in_ms); } previous_position_in_ms = current_position_in_ms; go_on = el_false; break; case MPG123_NEED_MORE: break; default: break; } } while (done > 0); } } else { // no streaming data, prevent race conditions // sleep for a small time (50 ms); no_count += 1; if (no_count > 10) { post_event(mp3_info->client_notification, AUDIO_BUFFERING, current_position_in_ms); } sleep_ms(50); } } } if (playing) { if (audio_event_fifo_peek(mp3_info->player_control) != NULL) { event = audio_event_fifo_dequeue(mp3_info->player_control); } else { event = (audio_event_t*) mc_malloc(sizeof(audio_event_t)); event->state = INTERNAL_CMD_NONE; event->position_in_ms = -1; } } else { //log_debug("waiting for next event"); event = audio_event_fifo_dequeue(mp3_info->player_control); } } // destroy event received log_debug("destroy event received"); // Kill playing streams if (mp3_info->streaming) { mp3_info->continue_streaming = el_false; psem_wait(mp3_info->stream_ready); } audio_event_destroy(event); // exit thread return NULL; }