From 39ef739509b1f46d0e3a4144be5fbe543e2e885d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 3 Jul 2014 15:44:26 -0500 Subject: use the libgstreamer API instead of libcanberra to play audio sounds. --- src/snap.cpp | 210 ++++++++++++++++++++++++----------------------------------- 1 file changed, 85 insertions(+), 125 deletions(-) (limited to 'src/snap.cpp') diff --git a/src/snap.cpp b/src/snap.cpp index eab7001..cea09d7 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include @@ -53,154 +53,136 @@ class Sound public: Sound(const std::shared_ptr& clock, - const std::string& filename, + const std::string& uri, unsigned int volume, unsigned int duration_minutes, bool loop): m_clock(clock), - m_filename(filename), + m_uri(uri), m_volume(volume), m_loop(loop), - m_canberra_id(get_next_canberra_id()), - m_loop_end_time(clock->localtime().add_full(0, 0, 0, 0, duration_minutes, 0.0)) + m_loop_end_time(clock->localtime().add_full(0, 0, 0, 0, (int)duration_minutes, 0.0)) { + // init GST once + static bool gst_initialized = false; + if (!gst_initialized) + { + gst_initialized = true; + + GError* error = nullptr; + gst_init_check (nullptr, nullptr, &error); + if (error) + { + g_critical("Unable to play alarm sound: %s", error->message); + g_error_free(error); + } + } + if (m_loop) { g_debug("Looping '%s' until cutoff time %s", - m_filename.c_str(), + m_uri.c_str(), m_loop_end_time.format("%F %T").c_str()); } else { - g_debug("Playing '%s' once", m_filename.c_str()); + g_debug("Playing '%s' once", m_uri.c_str()); } - const auto rv = ca_context_create(&m_context); - if (rv == CA_SUCCESS) - { - play(); - } - else - { - g_warning("Failed to create canberra context: %s", ca_strerror(rv)); - m_context = nullptr; - } + m_play = gst_element_factory_make("playbin", "play"); + + auto bus = gst_pipeline_get_bus(GST_PIPELINE(m_play)); + m_watch_source = gst_bus_add_watch(bus, bus_callback, this); + gst_object_unref(bus); + + play(); } ~Sound() { stop(); - g_clear_pointer(&m_context, ca_context_destroy); + g_source_remove(m_watch_source); + + if (m_play != nullptr) + { + gst_element_set_state (m_play, GST_STATE_NULL); + g_clear_pointer (&m_play, gst_object_unref); + } } private: void stop() { - if (m_context != nullptr) + if (m_play != nullptr) { - const auto rv = ca_context_cancel(m_context, m_canberra_id); - if (rv != CA_SUCCESS) - g_warning("Failed to cancel alarm sound: %s", ca_strerror(rv)); - } - - if (m_loop_tag != 0) - { - g_source_remove(m_loop_tag); - m_loop_tag = 0; + gst_element_set_state (m_play, GST_STATE_PAUSED); } } void play() { - auto context = m_context; - g_return_if_fail(context != nullptr); - - const auto filename = m_filename.c_str(); - const float gain = get_gain_level(m_volume); - - ca_proplist* props = nullptr; - ca_proplist_create(&props); - ca_proplist_sets(props, CA_PROP_MEDIA_FILENAME, filename); - ca_proplist_setf(props, CA_PROP_CANBERRA_VOLUME, "%f", gain); - const auto rv = ca_context_play_full(context, m_canberra_id, props, - on_done_playing, this); - if (rv != CA_SUCCESS) - g_warning("Unable to play '%s': %s", filename, ca_strerror(rv)); - - g_clear_pointer(&props, ca_proplist_destroy); + g_return_if_fail(m_play != nullptr); + + g_object_set(G_OBJECT (m_play), "uri", m_uri.c_str(), + "volume", get_volume(), + nullptr); + gst_element_set_state (m_play, GST_STATE_PLAYING); } - static float get_gain_level(unsigned int volume) + gdouble get_volume() const { - const unsigned int clamped_volume = CLAMP(volume, 1, 100); - - /* This range isn't set in stone -- - arrived at from manual tests on Nextus 4 */ - constexpr float gain_low = -10; - constexpr float gain_high = 10; - - constexpr float gain_range = gain_high - gain_low; - return gain_low + (gain_range * (clamped_volume / 100.0f)); + // input int range is [1..100]; gst playbin's range is [0...1.0] + return CLAMP(m_volume, 1, 100) / 100.0; } - static void on_done_playing(ca_context*, uint32_t, int rv, void* gself) + static gboolean bus_callback(GstBus*, GstMessage* msg, gpointer gself) { - // if we still need to loop, wait a second, then play it again + auto self = static_cast(gself); - if (rv == CA_SUCCESS) + if ((GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS) && + (self->m_loop) && + (self->m_clock->localtime() < self->m_loop_end_time)) { - auto self = static_cast(gself); - if ((self->m_loop_tag == 0) && - (self->m_loop) && - (self->m_clock->localtime() < self->m_loop_end_time)) - { - self->m_loop_tag = g_timeout_add_seconds(1, play_idle, self); - } + gst_element_seek(self->m_play, + 1.0, + GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, + 0, + GST_SEEK_TYPE_NONE, + (gint64)GST_CLOCK_TIME_NONE); } - } - static gboolean play_idle(gpointer gself) - { - auto self = static_cast(gself); - self->m_loop_tag = 0; - self->play(); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; // keep listening } /*** **** ***/ - static int32_t get_next_canberra_id() - { - static int32_t next_canberra_id = 1; - return next_canberra_id++; - } - const std::shared_ptr m_clock; - const std::string m_filename; + const std::string m_uri; const unsigned int m_volume; const bool m_loop; - const int32_t m_canberra_id; const DateTime m_loop_end_time; - ca_context* m_context = nullptr; - guint m_loop_tag = 0; + guint m_watch_source = 0; + GstElement* m_play = nullptr; }; class SoundBuilder { public: void set_clock(const std::shared_ptr& c) {m_clock = c;} - void set_filename(const std::string& s) {m_filename = s;} + void set_uri(const std::string& uri) {m_uri = uri;} void set_volume(const unsigned int v) {m_volume = v;} void set_duration_minutes(int unsigned i) {m_duration_minutes=i;} void set_looping(bool b) {m_looping=b;} Sound* operator()() { return new Sound (m_clock, - m_filename, + m_uri, m_volume, m_duration_minutes, m_looping); @@ -208,7 +190,7 @@ public: private: std::shared_ptr m_clock; - std::string m_filename; + std::string m_uri; unsigned int m_volume = 50; unsigned int m_duration_minutes = 30; bool m_looping = true; @@ -397,38 +379,8 @@ private: *** libnotify -- snap decisions **/ -std::string get_local_filename (const std::string& str) -{ - std::string ret; - - if (!str.empty()) - { - GFile* files[] = { g_file_new_for_path(str.c_str()), - g_file_new_for_uri(str.c_str()) }; - - for(auto& file : files) - { - if (g_file_is_native(file) && g_file_query_exists(file, nullptr)) - { - char* tmp = g_file_get_path(file); - if (tmp != nullptr) - { - ret = tmp; - g_free(tmp); - break; - } - } - } - - for(auto& file : files) - g_object_unref(file); - } - - return ret; -} - -std::string get_alarm_sound(const Appointment& appointment, - const std::shared_ptr& settings) +std::string get_alarm_uri(const Appointment& appointment, + const std::shared_ptr& settings) { const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; @@ -436,20 +388,28 @@ std::string get_alarm_sound(const Appointment& appointment, settings->alarm_sound.get(), FALLBACK }; - std::string alarm_sound; + std::string uri; for(const auto& candidate : candidates) { - alarm_sound = get_local_filename(candidate); - - if (!alarm_sound.empty()) + if (gst_uri_is_valid (candidate.c_str())) + { + uri = candidate; break; + } + else if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) + { + gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); + if (tmp != nullptr) + { + uri = tmp; + g_free (tmp); + break; + } + } } - g_debug("%s: Appointment \"%s\" using alarm sound \"%s\"", - G_STRFUNC, appointment.summary.c_str(), alarm_sound.c_str()); - - return alarm_sound; + return uri; } } // unnamed namespace @@ -481,7 +441,7 @@ void Snap::operator()(const Appointment& appointment, // create a popup... SoundBuilder sound_builder; - sound_builder.set_filename(get_alarm_sound(appointment, m_settings)); + sound_builder.set_uri(get_alarm_uri(appointment, m_settings)); sound_builder.set_volume(m_settings->alarm_volume.get()); sound_builder.set_clock(m_clock); sound_builder.set_duration_minutes(m_settings->alarm_duration.get()); -- cgit v1.2.3