/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include #include #include #include static GstElement *ocarina_player; static bool player_playing = false; static bool track_loaded = false; static unsigned int cur_trackid = 0; static bool o_pause_enabled = false; static unsigned int o_pause_count = 0; static bool o_should_pause = false; static File f_cur_track("cur_track"); class RecentQueue : public Queue { public: RecentQueue() : Queue(Q_ENABLED | Q_REPEAT | Q_NO_SORT | Q_DISABLE_CHANGED_SIZE) {} unsigned int add(Track *track) { del(track); _cur = 0; return _add_at(track, 0); } }; static RecentQueue o_recently_played; static void parse_error(GstMessage *error) { GError *err; gchar *debug; Track *track = tagdb :: lookup(cur_trackid); gst_message_parse_error(error, &err, &debug); g_print("Error playing file: %s\n", track->path().c_str()); g_print("Error: %s\n", err->message); g_error_free(err); g_free(debug); } static void handle_pause_count() { if (o_pause_enabled == false) return; else if (o_pause_count == 0) { o_should_pause = true; o_pause_enabled = false; get_callbacks()->on_pause(); } else o_pause_count--; get_callbacks()->on_pause_count_changed(o_pause_enabled, o_pause_count); } static gboolean on_message(GstBus *bus, GstMessage *message, gpointer data) { Track *track; switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: parse_error(message); audio :: next(); audio :: play(); break; case GST_MESSAGE_EOS: handle_pause_count(); track = tagdb :: lookup(cur_trackid); if (track) { track->played(); library :: get_queue()->updated(track); } audio :: next(); audio :: seek_to(0); default: break; } return TRUE; } static bool change_state(GstState state) { GstStateChangeReturn ret; ret = gst_element_set_state(GST_ELEMENT(ocarina_player), state); switch (ret) { case GST_STATE_CHANGE_SUCCESS: case GST_STATE_CHANGE_ASYNC: player_playing = (state == GST_STATE_PLAYING); return true; default: player_playing = false; return false; } } static void save_state() { f_cur_track.open(OPEN_WRITE); f_cur_track << cur_trackid << std::endl; f_cur_track.close(); } static bool load_song(Track *track) { GstState state; gchar *uri; std::string filepath = track->path(); if (o_should_pause == true) { state = GST_STATE_PAUSED; o_should_pause = false; } else { gst_element_get_state(GST_ELEMENT(ocarina_player), &state, NULL, GST_CLOCK_TIME_NONE); } change_state(GST_STATE_NULL); uri = gst_filename_to_uri(filepath.c_str(), NULL); g_object_set(G_OBJECT(ocarina_player), "uri", uri, NULL); g_free(uri); get_callbacks()->on_track_loaded(track); return change_state(state); } void audio :: init(int *argc, char ***argv) { GstBus *bus; gst_init(argc, argv); ocarina_player = gst_element_factory_make("playbin", "ocarina_player"); bus = gst_pipeline_get_bus(GST_PIPELINE(ocarina_player)); gst_bus_add_watch(bus, on_message, NULL); } void audio :: load_state() { unsigned int id; if (f_cur_track.exists()) { f_cur_track.open(OPEN_READ); f_cur_track >> id; f_cur_track.close(); audio :: load_trackid(id); } } void audio :: quit() { change_state(GST_STATE_NULL); gst_deinit(); } void audio :: play() { if (track_loaded == false) return; if (change_state(GST_STATE_PLAYING)) get_callbacks()->on_play(); } void audio :: pause() { if (track_loaded == false) return; if (change_state(GST_STATE_PAUSED)) get_callbacks()->on_pause(); } void audio :: toggle_play() { if (player_playing == true) pause(); else play(); } void audio :: stop() { pause(); seek_to(0); } void audio :: seek_to(long pos) { bool ret; if (track_loaded == false) return; ret = gst_element_seek_simple(GST_ELEMENT(ocarina_player), GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, pos); if (!ret) throw -E_AUDIO; } void audio :: next() { Track *track; track_loaded = false; track = deck :: next(); load_song(track); track_loaded = true; cur_trackid = track->id; save_state(); o_recently_played.add(track); } void audio :: previous() { Track *track = o_recently_played.next(); if (track->id == cur_trackid) return; load_song(track); cur_trackid = track->id; save_state(); } void audio :: load_trackid(unsigned int track_id) { Track *track; if ((track_id == cur_trackid) && (track_loaded == true)) return; track_loaded = false; try { track = tagdb :: lookup(track_id); } catch (int err) { return; } load_song(track); track_loaded = true; cur_trackid = track_id; save_state(); o_recently_played.add(track); } unsigned int audio :: current_trackid() { if (track_loaded == false) throw -E_EXIST; return cur_trackid; } Queue *audio :: get_recent_pq() { return &o_recently_played; } long audio :: position() { long position; if (track_loaded == false) return 0; if (!gst_element_query_position(GST_ELEMENT(ocarina_player), GST_FORMAT_TIME, &position)) return 0; return position; } std::string audio :: position_str() { std::stringstream ss; long cur = position() / GST_SECOND; unsigned int minutes = cur / 60; unsigned int seconds = cur % 60; ss << minutes << ":"; if (seconds < 10) ss << "0"; ss << seconds; return ss.str(); } long audio :: duration() { long duration; if (track_loaded == false) return 0; if (!gst_element_query_duration(ocarina_player, GST_FORMAT_TIME, &duration)) return 0; return duration; } void audio :: pause_after(bool enabled, unsigned int n) { if (n > o_pause_count) enabled = true; o_pause_enabled = enabled; o_pause_count = n; get_callbacks()->on_pause_count_changed(enabled, n); } bool audio :: pause_enabled() { return o_pause_enabled; } unsigned int audio :: pause_count() { return o_pause_count; }