summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xCMakeLists.txt34
-rw-r--r--cmake/Findlibspotify.cmake57
-rw-r--r--config.h.in2
-rw-r--r--navit/CMakeLists.txt3
-rw-r--r--navit/attr.h1
-rw-r--r--navit/attr_def.h11
-rw-r--r--navit/audio.c298
-rw-r--r--navit/audio.h117
-rw-r--r--navit/audio/output-alsa/CMakeLists.txt2
-rw-r--r--navit/audio/output-alsa/alsa.c294
-rw-r--r--navit/audio/output-alsa/alsa.h16
-rw-r--r--navit/audio/player-mpd/CMakeLists.txt2
-rw-r--r--navit/audio/player-mpd/alsa-audio.c237
-rw-r--r--navit/audio/player-mpd/audio.c61
-rw-r--r--navit/audio/player-mpd/mpd.c1975
-rw-r--r--navit/audio/player-mpd/mpd.h60
-rw-r--r--navit/audio/player-spotify/CMakeLists.txt2
-rw-r--r--navit/audio/player-spotify/alsa-audio.c269
-rw-r--r--navit/audio/player-spotify/api_keys.c37
-rw-r--r--navit/audio/player-spotify/audio.c63
-rw-r--r--navit/audio/player-spotify/spotify.c1119
-rw-r--r--navit/audio/player-spotify/spotify.h58
-rw-r--r--navit/audio/player-stub/CMakeLists.txt1
-rw-r--r--navit/audio/player-stub/stub.c1580
-rw-r--r--navit/audio/queue.h557
-rw-r--r--navit/graphics/qt5/event_qt5.cpp13
-rw-r--r--navit/gui/internal/CMakeLists.txt8
-rw-r--r--navit/gui/internal/gui_internal_command.c7
-rw-r--r--navit/gui/internal/gui_internal_media.c332
-rw-r--r--navit/gui/internal/gui_internal_media.h2
-rw-r--r--navit/icons/media_audio.svg10
-rw-r--r--navit/icons/media_category.svg10
-rw-r--r--navit/icons/media_close.svg11
-rw-r--r--navit/icons/media_document.svg7
-rw-r--r--navit/icons/media_hierarchy.svg7
-rw-r--r--navit/icons/media_minus.svg7
-rw-r--r--navit/icons/media_next.svg24
-rw-r--r--navit/icons/media_next_artist.svg7
-rw-r--r--navit/icons/media_next_artist_bk.svg7
-rw-r--r--navit/icons/media_next_artist_wh.svg7
-rw-r--r--navit/icons/media_next_bk.svg24
-rw-r--r--navit/icons/media_next_wh.svg24
-rw-r--r--navit/icons/media_pause.svg7
-rw-r--r--navit/icons/media_pause_bk.svg7
-rw-r--r--navit/icons/media_pause_wh.svg7
-rw-r--r--navit/icons/media_play.svg7
-rw-r--r--navit/icons/media_play_bk.svg7
-rw-r--r--navit/icons/media_play_wh.svg7
-rw-r--r--navit/icons/media_playlist_downloading.svg5
-rw-r--r--navit/icons/media_playlist_no_offline.svg5
-rw-r--r--navit/icons/media_playlist_offline.svg5
-rw-r--r--navit/icons/media_playlist_pending.svg5
-rw-r--r--navit/icons/media_playlists.svg26
-rw-r--r--navit/icons/media_plus.svg7
-rw-r--r--navit/icons/media_prev.svg24
-rw-r--r--navit/icons/media_prev_artist.svg7
-rw-r--r--navit/icons/media_prev_artist_bk.svg7
-rw-r--r--navit/icons/media_prev_artist_wh.svg7
-rw-r--r--navit/icons/media_prev_bk.svg24
-rw-r--r--navit/icons/media_prev_wh.svg24
-rw-r--r--navit/icons/media_rating_empty.svg8
-rw-r--r--navit/icons/media_rating_full.svg7
-rw-r--r--navit/icons/media_rating_half.svg8
-rw-r--r--navit/icons/media_refresh.svg9
-rw-r--r--navit/icons/media_repeat_off.svg9
-rw-r--r--navit/icons/media_repeat_playlist.svg9
-rw-r--r--navit/icons/media_repeat_track.svg9
-rw-r--r--navit/icons/media_rewind.svg7
-rw-r--r--navit/icons/media_search.svg11
-rw-r--r--navit/icons/media_shuffle_off.svg10
-rw-r--r--navit/icons/media_shuffle_playlists.svg10
-rw-r--r--navit/icons/media_shuffle_tracks.svg10
-rw-r--r--navit/icons/media_shuffle_tracks_playlists.svg10
-rw-r--r--navit/icons/media_spotify.svg12
-rw-r--r--navit/icons/media_standard.svg9
-rw-r--r--navit/icons/media_stop.svg7
-rw-r--r--navit/icons/media_track_downloading.svg5
-rw-r--r--navit/icons/media_track_offline.svg5
-rw-r--r--navit/icons/media_track_offline_done.svg5
-rw-r--r--navit/icons/media_track_pending.svg5
-rw-r--r--navit/icons/media_trash.svg11
-rw-r--r--navit/icons/media_warning.svg9
-rw-r--r--navit/navit.c375
-rw-r--r--navit/plugin.h2
-rw-r--r--navit/plugin/j1850/j1850.c3
-rw-r--r--navit/plugin_def.h1
-rw-r--r--navit/xmlconfig.c9
-rw-r--r--navit/xmlconfig.h2
88 files changed, 8118 insertions, 10 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3e6d4c683..644f43e18 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,10 @@ add_module(binding/dbus "dbus-glib-1 not found" FALSE)
add_module(binding/python "python libraries not found" FALSE)
add_module(speech/dbus "dbus-glib-1 not found" FALSE)
add_module(speech/cmdline "neither system() nor CreateProcess() found" FALSE)
+add_module(audio/output-alsa "alsalib not found" FALSE)
+add_module(audio/player-mpd "mpd not found" FALSE)
+add_module(audio/player-stub "default" TRUE)
+add_module(audio/player-spotify "libspotify not found" FALSE)
add_module(speech/qt5_espeak "Qt5 multimedia not found" FALSE)
add_module(vehicle/gpsd_dbus "dbus-glib-1 not found" FALSE)
add_module(vehicle/qt5 "Qt5 libraries not found" FALSE)
@@ -212,6 +216,8 @@ find_package(Gettext)
find_package(PNG)
find_package(PythonLibs)
find_package(Threads)
+find_package(ALSA)
+find_package(libspotify)
#Qt detection
if (NOT DISABLE_QT)
@@ -538,6 +544,34 @@ if (GUI_INTERNAL_VISUAL_DBG)
endif()
endif(GUI_INTERNAL_VISUAL_DBG)
+add_feature(USE_AUDIO_FRAMEWORK "default" FALSE)
+
+find_program(MPD NAMES mpd)
+find_program(MPC NAMES mpc)
+
+
+if (ALSA_FOUND)
+ if(UNIX AND NOT ANDROID AND NOT APPLE)
+ #list(APPEND NAVIT_LIBS ${ALSA_LIBRARY})
+ set_with_reason(audio/output-alsa "alsa found: ${ALSA_VERSION_STRING}" TRUE)
+ #include_directories(${ALSA_INCLUDE_DIR})
+ if(MPD)
+ if(MPC)
+ set_with_reason(audio/player-mpd "mpd and mpc found" TRUE)
+ endif(MPC)
+ endif(MPD)
+ set_with_reason(USE_AUDIO_FRAMEWORK "alsa found" TRUE)
+ endif(UNIX AND NOT ANDROID AND NOT APPLE)
+endif (ALSA_FOUND)
+
+if (LIBSPOTIFY_FOUND)
+ if (ALSA_FOUND)#
+ set_with_reason(audio/player-spotify "libspotify and alsa found" TRUE)
+ else (ALSA_FOUND)
+ set_with_reason(audio/player-spotify "libspotify found but alsalib is missing" FALSE)
+ endif (ALSA_FOUND)
+endif(LIBSPOTIFY_FOUND)
+
IF(NOT svg2png_scaling)
IF(NOT ANDROID)
set(svg2png_scaling 0 16 32 48 64 96)
diff --git a/cmake/Findlibspotify.cmake b/cmake/Findlibspotify.cmake
new file mode 100644
index 000000000..c4a2c905a
--- /dev/null
+++ b/cmake/Findlibspotify.cmake
@@ -0,0 +1,57 @@
+# - find Find the official Spotify library
+# LIBSPOTIFY_INCLUDE_DIR - Where to find Find the official Spotify library header files (directory)
+# LIBSPOTIFY_LIBRARIES - Find the official Spotify library libraries
+# LIBSPOTIFY_LIBRARY_RELEASE - Where the release library is
+# LIBSPOTIFY_LIBRARY_DEBUG - Where the debug library is
+# LIBSPOTIFY_FOUND - Set to TRUE if we found everything (library, includes and executable)
+
+# Copyright (c) 2010 Pau Garcia i Quiles, <pgquiles@elpauer.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+#
+# Generated by CModuler, a CMake Module Generator - http://gitorious.org/cmoduler
+
+IF( LIBSPOTIFY_INCLUDE_DIR AND LIBSPOTIFY_LIBRARY_RELEASE AND LIBSPOTIFY_LIBRARY_DEBUG )
+ SET(LIBSPOTIFY_FIND_QUIETLY TRUE)
+ENDIF( LIBSPOTIFY_INCLUDE_DIR AND LIBSPOTIFY_LIBRARY_RELEASE AND LIBSPOTIFY_LIBRARY_DEBUG )
+
+FIND_PATH( LIBSPOTIFY_INCLUDE_DIR libspotify/api.h )
+
+FIND_LIBRARY(LIBSPOTIFY_LIBRARY_RELEASE NAMES spotify openspotify )
+
+FIND_LIBRARY(LIBSPOTIFY_LIBRARY_DEBUG NAMES spotify openspotify HINTS /usr/lib/debug/usr/lib/ )
+
+IF( LIBSPOTIFY_LIBRARY_RELEASE OR LIBSPOTIFY_LIBRARY_DEBUG AND LIBSPOTIFY_INCLUDE_DIR )
+ SET( LIBSPOTIFY_FOUND TRUE )
+ENDIF( LIBSPOTIFY_LIBRARY_RELEASE OR LIBSPOTIFY_LIBRARY_DEBUG AND LIBSPOTIFY_INCLUDE_DIR )
+
+IF( LIBSPOTIFY_LIBRARY_DEBUG AND LIBSPOTIFY_LIBRARY_RELEASE )
+ # if the generator supports configuration types then set
+ # optimized and debug libraries, or if the CMAKE_BUILD_TYPE has a value
+ IF( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+ SET( LIBSPOTIFY_LIBRARIES optimized ${LIBSPOTIFY_LIBRARY_RELEASE} debug
+${LIBSPOTIFY_LIBRARY_DEBUG} )
+ ELSE( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+ # if there are no configuration types and CMAKE_BUILD_TYPE has no value
+ # then just use the release libraries
+ SET( LIBSPOTIFY_LIBRARIES ${LIBSPOTIFY_LIBRARY_RELEASE} )
+ ENDIF( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+ELSEIF( LIBSPOTIFY_LIBRARY_RELEASE )
+ SET( LIBSPOTIFY_LIBRARIES ${LIBSPOTIFY_LIBRARY_RELEASE} )
+ELSE( LIBSPOTIFY_LIBRARY_DEBUG AND LIBSPOTIFY_LIBRARY_RELEASE )
+ SET( LIBSPOTIFY_LIBRARIES ${LIBSPOTIFY_LIBRARY_DEBUG} )
+ENDIF( LIBSPOTIFY_LIBRARY_DEBUG AND LIBSPOTIFY_LIBRARY_RELEASE )
+
+IF( LIBSPOTIFY_FOUND )
+ IF( NOT LIBSPOTIFY_FIND_QUIETLY )
+ MESSAGE( STATUS "Found LibSpotify header file in ${LIBSPOTIFY_INCLUDE_DIR}")
+ MESSAGE( STATUS "Found LibSpotify libraries: ${LIBSPOTIFY_LIBRARIES}")
+ ENDIF( NOT LIBSPOTIFY_FIND_QUIETLY )
+ELSE(LIBSPOTIFY_FOUND)
+ IF( LIBSPOTIFY_FIND_REQUIRED)
+ MESSAGE( FATAL_ERROR "Could not find LibSpotify" )
+ ELSE( LIBSPOTIFY_FIND_REQUIRED)
+ MESSAGE( STATUS "Optional package LibSpotify was not found" )
+ ENDIF( LIBSPOTIFY_FIND_REQUIRED)
+ENDIF(LIBSPOTIFY_FOUND)
diff --git a/config.h.in b/config.h.in
index 1bbbf782a..5ac8b8406 100644
--- a/config.h.in
+++ b/config.h.in
@@ -102,3 +102,5 @@
#cmakedefine HAS_IFADDRS 1
#cmakedefine HAVE_POSTGRESQL 1
+
+#cmakedefine USE_AUDIO_FRAMEWORK 1
diff --git a/navit/CMakeLists.txt b/navit/CMakeLists.txt
index 0c4d4243d..a52eb09b2 100644
--- a/navit/CMakeLists.txt
+++ b/navit/CMakeLists.txt
@@ -6,7 +6,8 @@ include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/support")
# navit core
set(NAVIT_SRC announcement.c atom.c attr.c cache.c callback.c command.c config_.c coord.c country.c data_window.c debug.c
- event.c file.c geom.c graphics.c gui.c item.c layout.c log.c main.c map.c maps.c
+ event.c file.c geom.c graphics.c gui.c item.c layout.c log.c main.c
+ map.c maps.c audio.c
linguistics.c mapset.c maptype.c menu.c messages.c bookmarks.c navit.c navit_nls.c navigation.c osd.c param.c phrase.c plugin.c popup.c
profile.c profile_option.c projection.c roadprofile.c route.c script.c search.c speech.c start_real.c sunriset.c transform.c track.c
search_houseno_interpol.c traffic.c util.c vehicle.c vehicleprofile.c xmlconfig.c )
diff --git a/navit/attr.h b/navit/attr.h
index dca3a6107..a315400b7 100644
--- a/navit/attr.h
+++ b/navit/attr.h
@@ -194,6 +194,7 @@ struct attr {
struct arrows *arrows;
struct element *element;
struct speech *speech;
+ struct audio *audio;
struct cursor *cursor;
struct displaylist *displaylist;
struct transformation *transformation;
diff --git a/navit/attr_def.h b/navit/attr_def.h
index 2b51f11eb..1f1ce88a7 100644
--- a/navit/attr_def.h
+++ b/navit/attr_def.h
@@ -194,6 +194,9 @@ ATTR(turn_around_penalty)
ATTR(turn_around_penalty2)
ATTR(autozoom_max)
ATTR(nav_status)
+ATTR(shuffle)
+ATTR(repeat)
+ATTR(playing)
ATTR2(0x00027500,type_rel_abs_begin)
/* These attributes are int that can either hold relative or absolute values. See the
* documentation of ATTR_REL_RELSHIFT for details.
@@ -394,6 +397,13 @@ ATTR(street_destination)
ATTR(exit_to)
ATTR(street_destination_forward)
ATTR(street_destination_backward)
+ATTR(music_dir)
+ATTR(spotify_login)
+ATTR(spotify_password)
+ATTR(spotify_playlist)
+ATTR(audio_device)
+ATTR(audio_device_mixer)
+ATTR(audio_playback_pcm)
ATTR2(0x0003ffff,type_string_end)
ATTR2(0x00040000,type_special_begin)
ATTR(order)
@@ -479,6 +489,7 @@ ATTR(layout)
ATTR(profile_option)
ATTR(script)
ATTR(traffic)
+ATTR(audio)
ATTR2(0x0008ffff,type_object_end)
ATTR2(0x00090000,type_coord_begin)
ATTR2(0x0009ffff,type_coord_end)
diff --git a/navit/audio.c b/navit/audio.c
new file mode 100644
index 000000000..ad1ad1cae
--- /dev/null
+++ b/navit/audio.c
@@ -0,0 +1,298 @@
+#include "debug.h"
+#include "glib_slice.h"
+
+#include "item.h"
+#include "audio.h"
+#include "plugin.h"
+#include "xmlconfig.h"
+
+#include "attr.h"
+#include <callback.h>
+struct object_func audio_func;
+struct audio_priv;
+
+// FIXME : we can't name it active because it conflicts with speech.active. Odd!
+struct attr audio_active = ATTR_INT(active, 0);
+struct attr *audio_default_attrs[] = {
+ &audio_active,
+ NULL,
+};
+
+/**
+ * @brief audio_player_get_name
+ *
+ * @return the name of the audio plugin instance
+ *
+ */
+char *
+audio_player_get_name(void)
+{
+ dbg(lvl_error,
+ "//fixme: get the name of the music player... where is the name set?\n");
+ return "Music Player";
+}
+
+/**
+ * Generic get function
+ *
+ * @param this_ Pointer to a audio structure
+ * @param type The attribute type to look for
+ * @param attr Pointer to a {@code struct attr} to store the attribute
+ * @param iter A audio attr_iter. This is only used for generic attributes; for attributes specific to the audio object it is ignored.
+ * @return True for success, false for failure
+ */
+int
+audio_get_attr(struct audio *this_, enum attr_type type, struct attr *attr,
+ struct attr_iter *iter)
+{
+ int ret = 1;
+ dbg(lvl_debug, "Get attr: %p (%s), attrs: %p\n", attr,
+ attr_to_name(type), this_->attrs);
+
+ ret = this_->meth.attr_get(this_->priv, type, attr);
+ if (ret)
+ return ret;
+ return attr_generic_get_attr(this_->attrs, NULL, type, attr, iter);
+}
+
+/**
+ * Generic set function
+ *
+ * @param this_ A audio
+ * @param attr The attribute to set
+ * @return False on success, true on failure
+ */
+int
+audio_set_attr(struct audio *this_, struct attr *attr)
+{
+ int ret = 1;
+ dbg(lvl_debug, "Set attr: %p (%s), attrs: %p\n", attr,
+ attr_to_name(attr->type), this_->attrs);
+
+ switch (attr->type) {
+ case attr_callback:
+ callback_list_add(this_->cbl, attr->u.callback);
+ case attr_name:
+ case attr_playing:
+ case attr_shuffle:
+ case attr_repeat:
+ ret = this_->meth.attr_set(this_->priv, attr);
+ break;
+ default:
+ break;
+ }
+ if (ret == 1 && attr->type != attr_navit)
+ this_->attrs = attr_generic_set_attr(this_->attrs, attr);
+ return ret != 0;
+}
+
+/*
+ * @brief instantiates an audio plugin
+ *
+ * This function initializes an audio plugin according to the settings
+ * from navit.xml. Plugins can have different capabilities:
+ * - they can manage the global audio volume
+ * - they can manage the playback (previous/next, play/pause)
+ * - they can support a list of tracks
+ * - they can support a list of playlists
+ *
+ * A plugin does not need to have all capabilities. Usually,
+ * an output plugin will support setting up the volume, and
+ * a playback plugin should not need to care about the volume.
+ *
+ *
+ * @param parent The parent attribute
+ * @param attrs a list of attributes from navit.xml
+ *
+ * @return The created audio plugin instance
+ */
+struct audio *
+audio_new(struct attr *parent, struct attr **attrs)
+{
+ dbg(lvl_debug,
+ "Initializing audio plugin\nParent: %p, Attrs: %p\n", parent,
+ attrs);
+ struct audio *this_;
+ struct attr *attr;
+ struct audio_priv *(*audiotype_new) (struct audio_methods * meth,
+ struct callback_list * cbl,
+ struct attr ** attrs,
+ struct attr * parent);
+
+ attr = attr_search(attrs, NULL, attr_type);
+ if (!attr) {
+ dbg(lvl_error, "type missing\n");
+ return NULL;
+ }
+ dbg(lvl_debug, "type='%s'\n", attr->u.str);
+ audiotype_new = plugin_get_category_audio(attr->u.str);
+ dbg(lvl_debug, "new=%p\n", audio_new);
+ if (!audiotype_new) {
+ dbg(lvl_error, "wrong type '%s'\n", attr->u.str);
+ return NULL;
+ }
+
+ this_ = g_new0(struct audio, 1);
+ this_->func = &audio_func;
+
+ navit_object_ref((struct navit_object *) this_);
+
+ attr = attr_search(attrs, NULL, attr_name);
+ if (attr) {
+ this_->name = g_strdup(attr->u.str);
+ dbg(lvl_debug, "audio name: %s \n", this_->name);
+ }
+
+ this_->cbl = callback_list_new();
+ this_->priv =
+ audiotype_new(&this_->meth, this_->cbl, attrs, parent);
+
+ if (!this_->priv) {
+ dbg(lvl_error, "audio_new failed\n");
+ callback_list_destroy(this_->cbl);
+ g_free(this_);
+ return NULL;
+ }
+ dbg(lvl_debug, "Attrs: %p\n", attrs);
+ dbg(lvl_debug, "Attrs: %p\n", this_->attrs);
+
+ if (this_->meth.volume) {
+ dbg(lvl_debug, "%s.volume=%p\n", this_->name,
+ this_->meth.volume);
+ } else {
+ dbg(lvl_debug, "The plugin %s cannot manage the volume\n",
+ this_->name);
+ }
+ if (this_->meth.playback) {
+ dbg(lvl_debug, "%s.playback=%p\n", this_->name,
+ this_->meth.playback);
+ } else {
+ dbg(lvl_debug, "The plugin %s cannot handle playback\n",
+ this_->name);
+ }
+ if (this_->meth.action_do) {
+ dbg(lvl_debug, "%s.action_do=%p\n", this_->name,
+ this_->meth.playback);
+ } else {
+ dbg(lvl_debug, "The plugin %s cannot handle action_do's\n",
+ this_->name);
+ }
+ if (this_->meth.tracks) {
+ dbg(lvl_debug, "%s.tracks=%p\n", this_->name,
+ this_->meth.tracks);
+ } else {
+ dbg(lvl_debug, "The plugin %s cannot handle tracks\n",
+ this_->name);
+ }
+ if (this_->meth.playlists) {
+ dbg(lvl_debug, "%s.playlists=%p\n", this_->name,
+ this_->meth.playlists);
+ } else {
+ dbg(lvl_debug, "The plugin %s cannot handle playlists\n",
+ this_->name);
+ }
+ if (this_->meth.actions) {
+ dbg(lvl_debug, "%s.actions=%p\n", this_->name,
+ this_->meth.playlists);
+ } else {
+ dbg(lvl_debug, "The plugin %s cannot handle actions\n",
+ this_->name);
+ }
+
+ dbg(lvl_debug, "return %p\n", this_);
+
+ return this_;
+}
+
+
+/**
+ * Creates an attribute iterator to be used with audio plugins
+ */
+struct attr_iter *
+audio_attr_iter_new(void)
+{
+ return (struct attr_iter *) g_new0(void *, 1);
+}
+
+/**
+ * Destroys a audio attribute iterator
+ *
+ * @param iter a audio attr_iter
+ */
+void
+audio_attr_iter_destroy(struct attr_iter *iter)
+{
+ g_free(iter);
+}
+
+
+/**
+ * Generic add function
+ *
+ * @param this_ The audio audio instance that holds the list of attributes
+ * @param attr The attribute to add
+ *
+ * @return true if the attribute was added, false if not.
+ */
+int
+audio_add_attr(struct audio *this_, struct attr *attr)
+{
+ dbg(lvl_debug, "Add attr: %p (%s), attrs: %p\n", attr,
+ attr_to_name(attr->type), this_->attrs);
+ int ret = 1;
+ switch (attr->type) {
+ case attr_callback:
+ dbg(lvl_debug,
+ "Add attr: %p (%s), attrs: %p, to cbl: %p\n", attr,
+ attr_to_name(attr->type), this_->attrs, this_->cbl);
+ callback_list_add(this_->cbl, attr->u.callback);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ this_->attrs = attr_generic_add_attr(this_->attrs, attr);
+ return ret;
+}
+
+/**
+ * @brief Generic remove function.
+ *
+ * Used to remove a callback from the audio.
+ * @param this_ The audio audio instance that holds the list of attributes
+ * @param attr the attribute to be removed
+ * @return True on success false on failure
+ */
+int
+audio_remove_attr(struct audio *this_, struct attr *attr)
+{
+ dbg(lvl_debug, "Remove attr: %p (%s), attrs: %p\n", attr,
+ attr_to_name(attr->type), this_->attrs);
+ switch (attr->type) {
+ case attr_callback:
+ callback_list_remove(this_->cbl, attr->u.callback);
+ break;
+ default:
+ this_->attrs =
+ attr_generic_remove_attr(this_->attrs, attr);
+ return 0;
+ }
+ return 1;
+}
+
+
+struct object_func audio_func = {
+ attr_audio,
+ (object_func_new) audio_new,
+ (object_func_get_attr) audio_get_attr,
+ (object_func_iter_new) audio_attr_iter_new,
+ (object_func_iter_destroy) audio_attr_iter_destroy,
+ (object_func_set_attr) audio_set_attr,
+ (object_func_add_attr) audio_add_attr,
+ (object_func_remove_attr) audio_remove_attr,
+ (object_func_init) NULL,
+ (object_func_destroy) NULL,
+ (object_func_dup) NULL,
+ (object_func_ref) navit_object_ref,
+ (object_func_unref) navit_object_unref,
+};
diff --git a/navit/audio.h b/navit/audio.h
new file mode 100644
index 000000000..39d4bd86e
--- /dev/null
+++ b/navit/audio.h
@@ -0,0 +1,117 @@
+#ifndef NAVIT_AUDIO_H
+#define NAVIT_AUDIO_H
+#include "glib_slice.h"
+#include "attr.h"
+#include "item.h"
+#include "xmlconfig.h"
+#include <callback.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define AUDIO_PLAYBACK_TOGGLE -1
+#define AUDIO_PLAYBACK_NEXT_TRACK -2
+#define AUDIO_PLAYBACK_PREVIOUS_TRACK -3
+#define AUDIO_PLAYBACK_NEXT_PLAYLIST -4
+#define AUDIO_PLAYBACK_PREVIOUS_PLAYLIST -5
+#define AUDIO_PLAYBACK_NEXT_ARTIST -6
+#define AUDIO_PLAYBACK_PREVIOUS_ARTIST -7
+#define AUDIO_MODE_TOGGLE_SHUFFLE -8
+#define AUDIO_MODE_TOGGLE_REPEAT -9
+#define AUDIO_MISC_DELETE_PLAYLIST -10
+#define AUDIO_MISC_RELOAD_PLAYLISTS -11
+#define AUDIO_PLAYBACK_PLAY -12
+#define AUDIO_PLAYBACK_PAUSE -13
+
+#define AUDIO_RAISE_VOLUME -1
+#define AUDIO_LOWER_VOLUME -2
+#define AUDIO_MUTE 0
+
+#define STATUS_PLAYING 1
+#define STATUS_RATING_POOR 2
+#define STATUS_RATING_NORMAL 4
+#define STATUS_RATING_GOOD 6
+
+//#define AUDIO_MISC_DELETE_PLAYLIST -10
+ struct audio_priv;
+
+ struct audio_methods {
+ int (*volume) (struct audio_priv * this_,
+ const int direction);
+ int (*playback) (struct audio_priv * this_,
+ const int action);
+ int (*action_do) (struct audio_priv * this_,
+ const int action);
+ GList *(*tracks) (struct audio_priv * this_,
+ int playlist_index);
+ GList *(*playlists) (struct audio_priv * this_);
+ GList *(*actions) (struct audio_priv * this_);
+ char *(*current_track) (struct audio_priv * this_);
+ char *(*current_playlist) (struct audio_priv * this_);
+ int (*attr_get) (struct audio_priv * priv,
+ enum attr_type type, struct attr * attr);
+ int (*attr_set) (struct audio_priv * priv,
+ struct attr * attr);
+ };
+
+
+ struct audio {
+ NAVIT_OBJECT char *name;
+ struct callback_list *cbl;
+ struct audio_methods meth;
+ struct audio_priv *priv;
+ };
+
+
+ struct audio_playlist {
+ char *name;
+ char *icon;
+ int index;
+ int status;
+ };
+
+ struct audio_track {
+ char *name;
+ char *icon;
+ int index;
+ int status;
+ };
+
+
+/** actions which will be visible as a player toolbar in gui
+ *
+ * */
+ struct audio_actions {
+ char *icon;
+ int action;
+ };
+
+
+
+ int audio_get_attr(struct audio *this_, enum attr_type type,
+ struct attr *attr, struct attr_iter *iter);
+ int audio_set_attr(struct audio *this_, struct attr *attr);
+ int audio_add_attr(struct audio *this_, struct attr *attr);
+ int audio_remove_attr(struct audio *this_, struct attr *attr);
+/*
+int audio_register_callback(struct audio* this_, enum attr_type *type, struct callback *cb);
+int audio_unregister_callback(struct audio* this_, enum attr_type *type, struct callback *cb);
+//*/
+ struct audio *audio_new(struct attr *parent, struct attr **attrs);
+//int change_volume(struct audio *this_, const int direction);
+ void audio_play_track(struct navit *this, int track_index);
+ GList *audio_get_playlists(struct navit *this);
+ GList *audio_get_tracks(struct navit *this,
+ const int playlist_index);
+ void audio_set_volume(struct navit *this, int action);
+ void audio_do_action(struct navit *this, int action);
+ char *audio_get_current_playlist(struct navit *this);
+ char *audio_get_current_track(struct navit *this);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/navit/audio/output-alsa/CMakeLists.txt b/navit/audio/output-alsa/CMakeLists.txt
new file mode 100644
index 000000000..302c3a07e
--- /dev/null
+++ b/navit/audio/output-alsa/CMakeLists.txt
@@ -0,0 +1,2 @@
+module_add_library(audio_output-alsa alsa.c)
+target_link_libraries(audio_output-alsa ${ALSA_LIBRARY})
diff --git a/navit/audio/output-alsa/alsa.c b/navit/audio/output-alsa/alsa.c
new file mode 100644
index 000000000..9d451377b
--- /dev/null
+++ b/navit/audio/output-alsa/alsa.c
@@ -0,0 +1,294 @@
+#include "debug.h"
+#include "glib.h"
+#include "alsa.h"
+#include "item.h"
+#include "navit.h"
+#include "attr.h"
+#include "audio.h"
+#include "command.h"
+
+#ifdef OSSCONTROL
+#define MIXER_DEV "/dev/dsp"
+
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#else
+#include <alsa/asoundlib.h>
+#endif
+
+
+struct audio_priv {
+ struct navit *nav;
+};
+
+char *card = "";
+char *selem_name = "";
+
+void
+enumerate_devices()
+{
+ char **hints;
+ int err = snd_device_name_hint(-1, "pcm", (void ***) &hints);
+ if (err != 0) {
+ dbg(lvl_error, "Can't enumerate audio devices\n");
+ } else {
+ char **n = hints;
+ while (*n != NULL) {
+ char *name = snd_device_name_get_hint(*n, "NAME");
+
+ if (name != NULL && 0 != strcmp("null", name)) {
+ dbg(lvl_info,
+ "Found audio playback device %s\n",
+ name);
+ free(name);
+ }
+ n++;
+ }
+ snd_device_name_free_hint((void **) hints);
+ }
+}
+
+void
+audio_toggle_mute()
+{
+ long min, max;
+ int value;
+ snd_mixer_t *handle;
+ snd_mixer_selem_id_t *sid;
+
+ snd_mixer_open(&handle, 0);
+ if (!handle) {
+ dbg(lvl_error, "snd_mixer_open failed for card %s\n",
+ card);
+ return;
+ }
+ snd_mixer_elem_t *elem;
+ for (elem = snd_mixer_first_elem(handle);
+ elem != NULL; elem = snd_mixer_elem_next(elem)) {
+ dbg(lvl_error, "Found item %s : %d\n",
+ snd_mixer_selem_get_name(elem),
+ snd_mixer_selem_get_index(elem)
+ );
+ }
+ snd_mixer_attach(handle, card);
+ snd_mixer_selem_register(handle, NULL, NULL);
+ snd_mixer_load(handle);
+
+ snd_mixer_selem_id_alloca(&sid);
+ snd_mixer_selem_id_set_index(sid, 0);
+ snd_mixer_selem_id_set_name(sid, selem_name);
+ elem = snd_mixer_find_selem(handle, sid);
+ if (elem) {
+ snd_mixer_selem_get_playback_switch(elem,
+ SND_MIXER_SCHN_UNKNOWN,
+ &value);
+ if (snd_mixer_selem_has_playback_switch(elem)) {
+ snd_mixer_selem_set_playback_switch_all(elem,
+ value ? 0 :
+ 1);
+ }
+ } else {
+ dbg(lvl_error,
+ "snd_mixer_selem_get_playback_switch failed for card %s\n",
+ card);
+ enumerate_devices();
+ }
+
+ snd_mixer_close(handle);
+}
+
+/*
+ Drawbacks. Sets volume on both channels but gets volume on one. Can be easily adapted.
+ */
+int
+audio_volume(audio_volume_action action, long *outvol)
+{
+ int ret = 0;
+#ifdef OSSCONTROL
+ int fd, devs;
+
+ if ((fd = open(MIXER_DEV, O_WRONLY)) > 0) {
+ if (action == AUDIO_VOLUME_SET) {
+ if (*outvol < 0 || *outvol > 100)
+ return -2;
+ *outvol = (*outvol << 8) | *outvol;
+ ioctl(fd, SOUND_MIXER_WRITE_VOLUME, outvol);
+ } else if (action == AUDIO_VOLUME_GET) {
+ ioctl(fd, SOUND_MIXER_READ_VOLUME, outvol);
+ *outvol = *outvol & 0xff;
+ }
+ close(fd);
+ return 0;
+ }
+ return -1;;
+#else
+ snd_mixer_t *handle;
+ snd_mixer_elem_t *elem;
+ snd_mixer_selem_id_t *sid;
+
+ static int mix_index = 0;
+
+ long pmin, pmax;
+ long get_vol, set_vol;
+ float f_multi;
+
+ snd_mixer_selem_id_alloca(&sid);
+
+ //sets simple-mixer index and name
+ snd_mixer_selem_id_set_index(sid, mix_index);
+ snd_mixer_selem_id_set_name(sid, selem_name);
+
+ if ((snd_mixer_open(&handle, 0)) < 0) {
+ dbg(lvl_error, "snd_mixer_open(&handle, 0) failed\n");
+ return -1;
+ }
+ if ((snd_mixer_attach(handle, card)) < 0) {
+ dbg(lvl_error, "snd_mixer_attach(handle, card) failed\n");
+ snd_mixer_close(handle);
+ return -2;
+ }
+ if ((snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
+ dbg(lvl_error,
+ "snd_mixer_selem_register(handle, NULL, NULL) failed\n");
+ snd_mixer_close(handle);
+ return -3;
+ }
+ ret = snd_mixer_load(handle);
+ if (ret < 0) {
+ dbg(lvl_error, "snd_mixer_load(handle failed\n");
+ snd_mixer_close(handle);
+ return -4;
+ }
+ //snd_mixer_elem_t *elem;
+ for (elem = snd_mixer_first_elem(handle);
+ elem != NULL; elem = snd_mixer_elem_next(elem)) {
+ dbg(lvl_error, "Found item %s : %d\n",
+ snd_mixer_selem_get_name(elem),
+ snd_mixer_selem_get_index(elem)
+ );
+ }
+ elem = snd_mixer_find_selem(handle, sid);
+ if (!elem) {
+ snd_mixer_close(handle);
+ return -5;
+ }
+
+ long minv, maxv;
+
+ snd_mixer_selem_get_playback_volume_range(elem, &minv, &maxv);
+ dbg(lvl_error, "Volume range <%li,%li>\n", minv, maxv);
+
+ if (action == AUDIO_VOLUME_GET) {
+ if (snd_mixer_selem_get_playback_volume(elem, 0, outvol) <
+ 0) {
+ snd_mixer_close(handle);
+ dbg(lvl_error, "Failed to get volume\n");
+ return -6;
+ }
+
+ dbg(lvl_error, "Get volume %li with status %i\n", *outvol,
+ ret);
+ /* make the value bound to 100 */
+ *outvol -= minv;
+ maxv -= minv;
+ minv = 0;
+ *outvol = 100 * (*outvol) / maxv; // make the value bound from 0 to 100
+ } else if (action == AUDIO_VOLUME_SET) {
+ // if(*outvol < 0 || *outvol > VOLUME_BOUND) // out of bounds
+ if (*outvol < 0 || *outvol > 100) // out of bounds
+ return -7;
+ *outvol = (*outvol * (maxv - minv) / (100 - 1)) + minv;
+
+ dbg(lvl_error, "Setting volume to %li\n", outvol);
+
+ if (snd_mixer_selem_set_playback_volume(elem, 0, *outvol) <
+ 0) {
+ snd_mixer_close(handle);
+ return -8;
+ }
+ if (snd_mixer_selem_set_playback_volume(elem, 1, *outvol) <
+ 0) {
+ dbg(lvl_error, "About to crash?\n");
+ snd_mixer_close(handle);
+
+ return -9;
+ }
+ dbg(lvl_error, "Set volume %li with status %i\n", *outvol,
+ ret);
+ }
+
+ snd_mixer_close(handle);
+ return 0;
+#endif
+}
+
+int
+alsa_volume(struct audio_priv *this, const int direction)
+{
+ dbg(lvl_error, "In the alsa_volume method\n");
+ long vol = -1;
+ int ret;
+ if (direction) {
+ dbg(lvl_error, "Changing volume direction %i\n",
+ direction);
+ ret = audio_volume(AUDIO_VOLUME_GET, &vol);
+ dbg(lvl_error, "Got get ret = %d\n", ret);
+ vol += 10 * direction;
+ audio_volume(AUDIO_VOLUME_SET, &vol);
+ dbg(lvl_error, "Got set ret = %d\n", ret);
+ } else {
+ dbg(lvl_error, "Toggling volume mute\n");
+ audio_toggle_mute();
+ }
+ return 0;
+}
+
+static struct audio_methods output_alsa_meth = {
+ alsa_volume,
+ NULL, //playback
+ NULL, //action_do,
+ NULL, //tracks,
+ NULL, //playlists,
+ NULL, //actions,
+ NULL, //current_track,
+ NULL, //current_playlist,
+
+};
+
+static struct audio_priv *
+output_alsa_new(struct audio_methods *meth, struct callback_list *cbl,
+ struct attr **attrs, struct attr *parent)
+{
+ struct audio_priv *this;
+ struct attr *attr;
+ if (!parent || parent->type != attr_navit)
+ return NULL;
+ this = g_new(struct audio_priv, 1);
+ this->nav = parent->u.navit;
+ *meth = output_alsa_meth;
+
+ dbg(lvl_error, "Real alsa init\n");
+
+ if ((attr = attr_search(attrs, NULL, attr_audio_device))) {
+ card = g_strdup(attr->u.str);
+ dbg(lvl_info, "Will use alsa device %s\n", card);
+ }
+
+ if ((attr = attr_search(attrs, NULL, attr_audio_device_mixer))) {
+ selem_name = g_strdup(attr->u.str);
+ dbg(lvl_info, "Will use alsa mixer %s\n", selem_name);
+ }
+
+
+ dbg(lvl_debug, "Real alsa init\n");
+ enumerate_devices();
+ return this;
+}
+
+void
+plugin_init(void)
+{
+ dbg(lvl_debug, "output-alsa plugin init\n");
+ plugin_register_category_audio("output-alsa", output_alsa_new);
+}
diff --git a/navit/audio/output-alsa/alsa.h b/navit/audio/output-alsa/alsa.h
new file mode 100644
index 000000000..3c23d7f40
--- /dev/null
+++ b/navit/audio/output-alsa/alsa.h
@@ -0,0 +1,16 @@
+#ifndef NAVIT_AUDIO_OUTPUT_ALSA_H
+#define NAVIT_AUDIO_OUTPUT_ALSA_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include "audio/queue.h"
+
+
+void enumerate_devices();
+
+typedef enum {
+ AUDIO_VOLUME_SET,
+ AUDIO_VOLUME_GET,
+} audio_volume_action;
+
+#endif
diff --git a/navit/audio/player-mpd/CMakeLists.txt b/navit/audio/player-mpd/CMakeLists.txt
new file mode 100644
index 000000000..03655c5d4
--- /dev/null
+++ b/navit/audio/player-mpd/CMakeLists.txt
@@ -0,0 +1,2 @@
+module_add_library(audio_player-mpd mpd.c alsa-audio.c audio.c )
+target_link_libraries(audio_player-mpd ${ALSA_LIBRARY})
diff --git a/navit/audio/player-mpd/alsa-audio.c b/navit/audio/player-mpd/alsa-audio.c
new file mode 100644
index 000000000..d4ead7978
--- /dev/null
+++ b/navit/audio/player-mpd/alsa-audio.c
@@ -0,0 +1,237 @@
+//* vim: set tabstop=4 expandtab: */
+/*
+ * Copyright (c) 2006-2009 Spotify Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * ALSA audio output driver.
+ *
+ * This file is part of the libspotify examples suite.
+ */
+
+
+
+
+#include <alsa/asoundlib.h>
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "debug.h"
+
+#include "alsa.h"
+#include "mpd.h"
+
+static snd_pcm_t *alsa_open(char *dev, int rate, int channels)
+{
+ snd_pcm_hw_params_t *hwp;
+ snd_pcm_sw_params_t *swp;
+ snd_pcm_t *h;
+ int r;
+ int dir;
+ snd_pcm_uframes_t period_size_min;
+ snd_pcm_uframes_t period_size_max;
+ snd_pcm_uframes_t buffer_size_min;
+ snd_pcm_uframes_t buffer_size_max;
+ snd_pcm_uframes_t period_size;
+ snd_pcm_uframes_t buffer_size;
+
+ if ((r = snd_pcm_open(&h, dev, SND_PCM_STREAM_PLAYBACK, 0) < 0)) {
+ dbg(lvl_error, "Something went wrong with %s\n", dev);
+ return NULL;
+ }
+
+ hwp = alloca(snd_pcm_hw_params_sizeof());
+ memset(hwp, 0, snd_pcm_hw_params_sizeof());
+ snd_pcm_hw_params_any(h, hwp);
+
+ snd_pcm_hw_params_set_access(h, hwp, SND_PCM_ACCESS_RW_INTERLEAVED);
+ snd_pcm_hw_params_set_format(h, hwp, SND_PCM_FORMAT_S16_LE);
+ snd_pcm_hw_params_set_rate(h, hwp, rate, 0);
+ snd_pcm_hw_params_set_channels(h, hwp, channels);
+
+ /* Configurue period */
+
+ dir = 0;
+ snd_pcm_hw_params_get_period_size_min(hwp, &period_size_min, &dir);
+ dir = 0;
+ snd_pcm_hw_params_get_period_size_max(hwp, &period_size_max, &dir);
+
+ period_size = 1024;
+
+ dir = 0;
+ r = snd_pcm_hw_params_set_period_size_near(h, hwp, &period_size, &dir);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to set period size %lu (%s)\n",
+ period_size, snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ dir = 0;
+ r = snd_pcm_hw_params_get_period_size(hwp, &period_size, &dir);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to get period size (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ /* Configurue buffer size */
+
+ snd_pcm_hw_params_get_buffer_size_min(hwp, &buffer_size_min);
+ snd_pcm_hw_params_get_buffer_size_max(hwp, &buffer_size_max);
+ buffer_size = period_size * 4;
+
+ dir = 0;
+ r = snd_pcm_hw_params_set_buffer_size_near(h, hwp, &buffer_size);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to set buffer size %lu (%s)\n",
+ buffer_size, snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ r = snd_pcm_hw_params_get_buffer_size(hwp, &buffer_size);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to get buffer size (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ /* write the hw params */
+ r = snd_pcm_hw_params(h, hwp);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to configure hardware parameters (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ /*
+ * Software parameters
+ */
+
+ swp = alloca(snd_pcm_sw_params_sizeof());
+ memset(hwp, 0, snd_pcm_sw_params_sizeof());
+ snd_pcm_sw_params_current(h, swp);
+
+ r = snd_pcm_sw_params_set_avail_min(h, swp, period_size);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to configure wakeup threshold (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ snd_pcm_sw_params_set_start_threshold(h, swp, 0);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to configure start threshold (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ r = snd_pcm_sw_params(h, swp);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Cannot set soft parameters (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ r = snd_pcm_prepare(h);
+ if (r < 0) {
+ dbg(lvl_error, "audio: Cannot prepare audio for playback (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ return h;
+}
+
+static void* alsa_audio_start(void *aux)
+{
+ audio_fifo_t *af = aux;
+ snd_pcm_t *h = NULL;
+ int c;
+ int cur_channels = 0;
+ int cur_rate = 0;
+
+ audio_fifo_data_t *afd;
+
+ for (;;) {
+ afd = audio_get(af);
+
+ if (!h || cur_rate != afd->rate || cur_channels != afd->channels) {
+ if (h) snd_pcm_close(h);
+
+ cur_rate = afd->rate;
+ cur_channels = afd->channels;
+
+ // h = alsa_open("front:CARD=Set,DEV=0", cur_rate, cur_channels);
+ h = alsa_open("dmixer", cur_rate, cur_channels);
+
+ if (!h) {
+ dbg(lvl_error, "Unable to open ALSA device (%d channels, %d Hz), can't continue\n", cur_channels, cur_rate);
+ return;
+ } else {
+ dbg(lvl_info, "ALSA device (%d channels, %d Hz), ok\n", cur_channels, cur_rate);
+ }
+ }
+
+ c = snd_pcm_wait(h, 1000);
+
+ if (c >= 0)
+ c = snd_pcm_avail_update(h);
+
+ if (c == -EPIPE)
+ snd_pcm_prepare(h);
+
+ snd_pcm_writei(h, afd->samples, afd->nsamples);
+ free(afd);
+ }
+}
+
+void audio_init(audio_fifo_t *af)
+{
+ pthread_t tid;
+
+ TAILQ_INIT(&af->q);
+ af->qlen = 0;
+
+ pthread_mutex_init(&af->mutex, NULL);
+ pthread_cond_init(&af->cond, NULL);
+
+ pthread_create(&tid, NULL, alsa_audio_start, af);
+}
+
diff --git a/navit/audio/player-mpd/audio.c b/navit/audio/player-mpd/audio.c
new file mode 100644
index 000000000..6d76317d9
--- /dev/null
+++ b/navit/audio/player-mpd/audio.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010 Spotify Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * Audio helper functions.
+ *
+ * This file is part of the libspotify examples suite.
+ */
+
+#include "mpd.h"
+#include <stdlib.h>
+#include <alsa/asoundlib.h>
+
+audio_fifo_data_t* audio_get(audio_fifo_t *af)
+{
+ audio_fifo_data_t *afd;
+ pthread_mutex_lock(&af->mutex);
+
+ while (!(afd = TAILQ_FIRST(&af->q)))
+ pthread_cond_wait(&af->cond, &af->mutex);
+
+ TAILQ_REMOVE(&af->q, afd, link);
+ af->qlen -= afd->nsamples;
+
+ pthread_mutex_unlock(&af->mutex);
+ return afd;
+}
+
+void audio_fifo_flush(audio_fifo_t *af)
+{
+ audio_fifo_data_t *afd;
+
+
+ pthread_mutex_lock(&af->mutex);
+
+ while((afd = TAILQ_FIRST(&af->q))) {
+ TAILQ_REMOVE(&af->q, afd, link);
+ free(afd);
+ }
+
+ af->qlen = 0;
+ pthread_mutex_unlock(&af->mutex);
+}
diff --git a/navit/audio/player-mpd/mpd.c b/navit/audio/player-mpd/mpd.c
new file mode 100644
index 000000000..d686000c7
--- /dev/null
+++ b/navit/audio/player-mpd/mpd.c
@@ -0,0 +1,1975 @@
+/**
+ * Navit, a modular navigation system.
+ * Copyright (C) 2005-2016 Navit Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include "item.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <math.h>
+#include <time.h>
+#include <termios.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+
+#include <navit/map.h>
+#include <navit/navit.h>
+#include <navit/file.h>
+#include <navit/plugin.h>
+#include <navit/command.h>
+#include <navit/config_.h>
+#include <navit/main.h>
+#include <navit/item.h>
+#include <navit/debug.h>
+#include <navit/callback.h>
+#include <navit/event.h>
+#include <navit/audio.h>
+#include "graphics.h"
+#include "color.h"
+#include "mpd.h"
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+#define PLAYLIST 1
+#define TRACK 2
+#define NO_TRACK ' '
+
+static audio_fifo_t g_audiofifo;
+
+// placeholder for the current track used to communicate with the osd item
+char track[64];
+
+
+struct audio_priv
+{
+ struct navit *navit;
+ struct callback *callback;
+ struct callback *idle;
+ struct callback_list *cbl;
+ struct event_timeout *timeout;
+ struct attr **attrs;
+ GList* current_playlist; // the currently playing album/playlist
+ GList* playlists; // the head of the playlist list
+ GList* actions; // all possible actions
+ gchar *musicdir; // path where the music is stored
+ int num_playlists; // count value for the number of playlists
+ int playlist_index; // the index of the currently loaded list
+ gboolean random_track; // setting for randomized playback, true means play randomized
+ gboolean random_playlist; // setting for randomized playback of playlists, true means choose a playlist randomized
+ gboolean repeat; // setting for repeated playback.
+ gboolean single; // setting for single playback the playback stops if the track ended.
+ gboolean shuffle; // setting for shuffle playback
+ char current_track[64];
+ int volume; // stores the volume for mpc - needs to be restored if the audio output was muted
+ int width;
+ gboolean muted; // muting status true is volume = 0
+ gboolean playing; // playing status true is playing
+} *mpd;
+
+
+GList* sort_playlists(GList* list);
+
+char * mpd_get_playlist_name (int playlist_index);
+char* get_playlist_name(GList* list);
+void mpd_play(void);
+void mpd_pause(void);
+void mpd_play_track(int track);
+GList* get_entry_by_index(GList* list, int index);
+//void save_playlist(GList* list);
+int mpd_get_playlists_count (void);
+void reload_playlists(struct audio_priv* this);
+struct audio_actions* get_specific_action(GList* actions, int specific_action);
+void mpd_toggle_repeat(struct audio_actions *action);
+void mpd_toggle_shuffle(struct audio_actions *action);
+int mpd_get_attr(struct audio_priv* priv, enum attr_type type, struct attr *attr){
+ int ret = 1;
+ dbg(lvl_debug, "priv: %p, type: %i (%s), attr: %p\n", priv, type,attr_to_name(type), attr);
+ if(priv != mpd) {
+ dbg(lvl_debug, "failed\n");
+ return -1;
+ }
+ switch(type){
+ case attr_playing:{
+ attr->u.num = mpd->playing;
+ dbg(lvl_debug, "Status: %ld\n", attr->u.num);
+ break;
+ }
+ case attr_name:{
+ attr->u.str = g_strdup("mpd");
+ dbg(lvl_debug, "%s\n", attr->u.str);
+ break;
+ }
+ case attr_shuffle:{
+ int toggle = 0;
+ if(mpd->random_track) toggle++;
+ if(mpd->random_playlist) toggle += 2;
+ attr->u.num = toggle;
+ dbg(lvl_debug, "%ld\n", attr->u.num);
+ break;
+ }
+ case attr_repeat:{
+ int toggle = 0;
+ if(mpd->single) toggle++;
+ if(mpd->repeat) toggle += 2;
+ attr->u.num = toggle;
+ dbg(lvl_debug, "%ld\n", attr->u.num);
+ break;
+ }
+ default:{
+ dbg(lvl_error, "Don't know what to do with ATTR type %s\n", attr_to_name(attr->type));
+ ret = 0;
+ break;
+ }
+ }
+ attr->type = type;
+ return ret;
+}
+
+int mpd_set_attr(struct audio_priv *priv, struct attr *attr){
+ dbg(lvl_debug, "priv: %p, type: %i (%s), attr: %p\n", priv, attr->type,attr_to_name(attr->type), attr);
+ if(priv != mpd){
+ dbg(lvl_debug, "failed\n");
+ return -1;
+ }
+ if(attr){
+ switch(attr->type){
+ case attr_name:{
+ dbg(lvl_debug, "%s\n", attr->u.str);
+ break;
+ }
+ case attr_playing:{
+ dbg(lvl_debug, "attr->u.num: %ld\n", attr->u.num);
+
+ if(attr->u.num == 0){
+ dbg(lvl_debug, "mpd_pause();%ld\n", attr->u.num);
+ mpd_pause();
+ }else{
+ dbg(lvl_debug, "mpd_play();%ld\n", attr->u.num);
+ mpd_play();
+ }
+
+ break;
+ }
+ case attr_shuffle:{
+
+ mpd_toggle_shuffle(get_specific_action(mpd->actions, AUDIO_MODE_TOGGLE_SHUFFLE));
+ break;
+ }
+ case attr_repeat:{
+
+ mpd_toggle_repeat(get_specific_action(mpd->actions, AUDIO_MODE_TOGGLE_REPEAT));
+ break;
+ }
+ default:{
+ dbg(lvl_error, "Don't know what to do with ATTR type %s", attr_to_name(attr->type));
+ return 0;
+ break;
+ }
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+// playlist functions
+
+/**
+* @brief this function checks if the audio player is playing
+*
+* @return the status of the player. True is playing.
+*
+* this function checks if the player is playing.
+* If the player 'says' not playing it asks the mpc process if its playing.
+*/
+gboolean mpd_get_playing_status(void){
+ if(mpd->playing)
+ return mpd->playing;
+ // check if mpd is really paused
+ FILE *fp;
+ fp = popen("mpc ", "r");
+ char text[32] = {0,};
+ if (fp == NULL) {
+ dbg(lvl_error, "Failed to run command\n" );
+ return false;
+ }
+ while (fgets(text, sizeof(text)-1, fp) != NULL) {
+ if(strstr(text, "[playing]")){
+ mpd->playing = true;
+ pclose(fp);
+ return true;
+ }else if (strstr(text, "[paused]")){
+ mpd->playing = false;
+ pclose(fp);
+ return false;
+ }
+ }
+ pclose(fp);
+ return false;
+}
+/**
+* @brief this function reads a data of a certain playlist
+*
+* @param the playlist object
+* @return the playlist data structure or NULL on error
+*/
+struct audio_playlist*
+get_playlist_data(GList* list)
+{
+ if(list != NULL)
+ {
+ if(list->data!=NULL)
+ {
+ return (struct audio_playlist*) list->data;
+ }
+ }
+
+ dbg(lvl_error, "No playlists or data is corrupted: %p\n", list);
+
+ return NULL;
+}
+
+
+/**
+* @brief this function reindexes a list of playlists
+*
+* @param the playlist object to start indexing
+* @return the number of reindexed playlists.
+*
+* this function reindexes a list of playlists to reorder them.
+* It's intended to be used after a sorting algorithm on the head of a list of playlists.
+*/
+int
+reindex_playlists(GList *list)
+{
+ GList *current = list;
+ int i = 0;
+ dbg(lvl_debug, "read %p\n", list);
+ get_playlist_data(list)->index = i;
+ dbg(lvl_debug, "playlist:%s \n\n", get_playlist_name(list));
+
+ while (NULL != (current = current->next))
+ {
+ dbg(lvl_debug, "playlist:%s (%p)\n", get_playlist_name(current), current);
+ get_playlist_data(current)->index = ++i;
+ }
+ dbg(lvl_debug, "%i Playlists indexed\n",i);
+ return i;
+}
+
+
+/**
+* @brief this function creates a new playlist data structure
+*
+* @param the playlist name
+* @param the desired playlist index
+* @return the playlist data object.
+*/
+struct audio_playlist* new_audio_playlist(char *name, int index)
+{
+ struct audio_playlist *pl;
+ pl = g_new0(struct audio_playlist, 1);
+ pl->name=g_strdup(name);
+ pl->index = index;
+ pl->status=0;
+ pl->icon = "playlist";
+ dbg(lvl_debug, "Created a playlist with name %s and index %i\n", name, index);
+ return pl;
+}
+
+/**
+* @brief this function appends a playlist data structure to the list of playlists
+*
+* @param list the playlist to apped the data
+* @param playlist the playlist data to append
+* @return the playlist.
+*/
+GList*
+insert_right(GList* list, struct audio_playlist* playlist)
+{
+ dbg(lvl_debug, "Appending playlist %s\n", playlist->name);
+ return g_list_append(list, (gpointer) playlist);
+}
+
+void
+print_all(GList* list)
+{
+ //return;
+ int i = 0;
+ if(list==NULL) return;
+ GList* current = list;
+ while (NULL != current->next)
+ {
+ if(get_playlist_data(current)!=NULL)
+ {
+ printf("List element %i, %s. Index %i (%p)\n", i++, get_playlist_name(current), get_playlist_data(current)->index, current );
+ }else{
+ dbg(lvl_error, "%i: This appears to be an empty list. That's probably a Bug!\n",i);
+ }
+ current = current->next;
+ }
+}
+/**
+* @brief choose a playlist randomly
+*
+* @param list the entire list of playlists
+*
+* @return the randomly chosen list element
+*/
+GList*
+random_entry(GList* list){
+ int n = mpd_get_playlists_count();
+ int i = random();
+ i %= n;
+ dbg(lvl_debug, "\tTest a random number: \t%i\n", i );
+ return get_entry_by_index(g_list_first(list), i);
+}
+
+/**
+* @brief switch to the next playlist
+*
+* @param list the entire list of playlists
+*
+* @return the next list element
+*/
+GList*
+next_playlist(GList* list)
+{
+ if(mpd->random_playlist){
+ return random_entry(list);
+ }
+ return (list->next!=NULL)?g_list_next(list):g_list_first(list);
+}
+
+/**
+* @brief switch to the previous playlist
+*
+* @param list the entire list of playlists
+*
+* @return the previous list element
+*/
+GList*
+prev_playlist(GList* list)
+{
+ if(mpd->random_playlist){
+ return random_entry(list);
+ }
+ return (list->prev!=NULL)?g_list_previous(list):g_list_last(list);
+}
+
+
+/**
+* @brief Get the name of the playlist
+*
+* @param list the playlist object
+*
+* @return the name of the playlist
+*/
+char*
+get_playlist_name(GList* list)
+{
+ if(list != NULL)
+ {
+ struct audio_playlist *pl = list->data;
+ if(pl != NULL)
+ {
+ if(pl->name != NULL)
+ {
+ return pl->name;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+* @brief Delete a playlist from the list of playlists
+*
+* @param list the playlist object to be deleted
+*
+* @return the list of playlists without the deleted element
+*/
+GList*
+delete_and_free(GList *list)
+{
+ return g_list_delete_link(g_list_first(list),list);
+}
+
+/**
+* @brief Delete a playlist from the list of playlists and from mpc's database
+*
+* @param list the playlist object to be deleted
+*
+* @return the list of playlists without the deleted element
+*/
+GList*
+delete_playlist(GList* list)
+{
+ char command[100] = {0,};
+ strcat(command, "mpc rm \"");
+ strcat(command, get_playlist_name(list));
+ strcat(command, "\"");
+ system(&command[0]);
+ return g_list_delete_link(g_list_first(list),list);
+}
+
+/**
+* @brief Initialize the mpd database and create the list of playlists
+*
+* @return the created list of playlists alphabetically sorted
+*/
+GList*
+load_playlists(void)
+{
+ DIR *d;
+ struct dirent *dir;
+ GList* list = NULL;
+ dbg(lvl_debug, "MusicDir: %s\n", mpd->musicdir);
+ d = opendir(mpd->musicdir);
+ system("mpc clear");
+ if (d)
+ {
+ while ((dir = readdir(d)) != NULL)
+ {
+
+ if(dir->d_name[0] != '.')
+ {
+ dbg(lvl_debug, "%s\n", dir->d_name);
+ char command[100] = {0,};
+ strcat(command, "mpc ls \"");
+ strcat(command, dir->d_name);
+ strcat(command, "\" | mpc add");
+ system(&command[0]);
+ dbg(lvl_debug, command);
+ dbg(lvl_debug, "\n");
+ char command2[100] = {0,};
+ strcat(command2, "mpc save \"");
+ strcat(command2, dir->d_name);
+ strcat(command2, "\"");
+ system(command2);
+ //system("mpc playlist");
+ system("mpc clear");
+ dbg(lvl_debug, command2);
+ dbg(lvl_debug, "\n");
+ list = insert_right(list, new_audio_playlist(dir->d_name, -1));
+ }
+ }
+ closedir(d);
+
+ }
+ return sort_playlists(list);
+}
+
+/**
+* @brief set the index of a playlist
+*
+* @param entry the playlist element
+* @param entry_index the desired index
+*/
+void set_playlist_index(GList* entry, int entry_index)
+{
+ struct audio_playlist *pl;
+ if(entry)
+ {
+ pl = (struct audio_playlist*) entry->data;
+ if(pl)
+ {
+ pl->index = entry_index;
+ }
+ }
+}
+
+/**
+* @brief swap two playlists
+*
+* @param a playlist a to swap
+* @param b playlist b to swap
+*
+* this function is used to bubblesort the playlists
+*/
+void
+swap_playlists(GList* a, GList* b)
+{
+ char* temp = NULL;
+ temp = a->data;
+ a->data = b->data;
+ b->data = temp;
+}
+
+/**
+* @brief this function sorts a list of playlists alphabetically
+*
+* @param list the entire list of playlists
+*
+* @return the sorted list of playlists
+*/
+GList*
+sort_playlists(GList* list)
+{
+ // bubblesort for playlists
+ if(list == NULL) return NULL;
+ dbg(lvl_debug, "\n");
+ dbg(lvl_debug, "start\n");
+ GList* i;
+ GList* j;
+ int index = 0;
+ for(i=list; i->next!=NULL; i=i->next)
+ {
+ for(j=list; j->next!=NULL; j=j->next)
+ {
+ if(strcmp(get_playlist_data(j)->name, get_playlist_data(j->next)->name) > 0)
+ {
+ swap_playlists(j, j->next);
+ }
+ }
+ }
+ for(i=g_list_first(list); i->next!=NULL; i=i->next)
+ {
+ set_playlist_index(i, index++);
+ }
+ print_all(g_list_first(list));
+ dbg(lvl_debug, "end\n");
+ return list;
+}
+
+/**
+* @brief this function loads a playlist to mpd/mpd
+*
+* @param list the playlist to be loaded
+*/
+void
+load_playlist(GList * list){
+ if(list){
+ char command[100] = {0,};
+ system("mpc clear");
+ strcat(command, "mpc load \"");
+ strcat(command, get_playlist_name(list));
+ strcat(command, "\"");
+ system(&command[0]);
+ //save_playlist(list);
+ }
+}
+
+/**
+* @brief this function choses the next playlist from the list of playlists
+*
+* @param current the currently loaded playlist element
+*
+* @return the playlist element that was loaded
+*/
+GList*
+load_next_playlist(GList* current)
+{
+ GList* next = next_playlist(current);
+ load_playlist(next);
+ mpd->playlist_index = g_list_index(mpd->playlists, next->data);
+ mpd_play();
+ return next;
+}
+
+/**
+* @brief seeks and returns the playlist with the given data
+*
+* @param head the list of playlist to search on
+* @param data the data, which contains the playlist name to search
+*
+* @return the searched element or NULL if it wasnt found
+*/
+GList*
+get_entry(GList* head, char *data)
+{
+ print_all(head);
+ if(head != NULL)
+ {
+ dbg(lvl_debug, "Search Entry: %s\n",data);
+ GList* current = head;
+ struct audio_playlist *currend_data = NULL;
+ int cmp = -1;
+ while(cmp != 0){
+ dbg(lvl_debug, "%i\n", cmp);
+ currend_data = get_playlist_data(current);
+ dbg(lvl_debug, "%i, %p, %s\n", cmp, currend_data, currend_data->name);
+ if(currend_data){
+ if(currend_data->name){
+ dbg(lvl_debug, "Got Entry: %s\n",currend_data->name);
+ cmp = strcmp(currend_data->name, data);
+ if(cmp != 0){
+ dbg(lvl_debug, "cmp != 0: %s ? %s\n",currend_data->name, data);
+ current = current->next;
+ }
+ //*
+ else{
+ dbg(lvl_debug, "cmp == 0: %s ? %s\n",currend_data->name, data);
+ }
+ //*/
+ }else{
+ dbg(lvl_debug, "Nothing Found!\n");
+ return NULL; //nothing found!
+ }
+ }else{
+ dbg(lvl_debug, "Nothing Found!\n");
+ return NULL; //nothing found!
+ }
+ }
+ dbg(lvl_debug, "Found Entry: %s\n",currend_data->name);
+ return current; //found!
+ }
+ dbg(lvl_debug, "Did not find Entry\n");
+ return NULL;
+}
+
+/**
+* @brief this function returns the nth element of the list of playlists
+*
+* @param list the list of playlists
+* @param index the index to get
+*
+* @return the nth playlist
+*/
+GList*
+get_entry_by_index(GList* list, int index)
+{
+ return g_list_nth(list, index);
+}
+
+/**
+* @brief this function choses the previous playlist from the list of playlists
+*
+* @param current the currently loaded playlist element
+*
+* @return the playlist element that was loaded
+*/
+GList*
+load_prev_playlist(GList* current)
+{
+ GList* previous = prev_playlist(current);
+ load_playlist(previous);
+ mpd->playlist_index = g_list_index(mpd->playlists, previous->data);
+ mpd_play();
+ return previous;
+}
+
+/**
+* @brief this function gets the next playlist where the artist differs
+*
+* @param current playlist entry
+* @param next specifies if we go forwards or backwards
+*
+* @return the playlist item which differs in its artist name
+*
+* For proper us of this function the playlists must be named in a special way:
+* "Artist - Album" The algorithm iterates over the list of playlists forwards
+* or backwards (depending on next is set or not) and stops and return
+* the first item which differs in the part until the dash char
+*/
+GList*
+change_artist(GList* current, int next)
+{
+ char ca[32] = {0,};
+ char na[32] = {0,};
+ int i,j;
+ for(i=0; i<32; i++)
+ {
+ if(get_playlist_data(current)->name[i] == '-')
+ {
+ ca[i] = 0x00;
+ break;
+ }
+ else
+ {
+ ca[i] = get_playlist_data(current)->name[i];
+ }
+ }
+ do {
+ if(next)
+ {
+ current = next_playlist(current);
+ }else{
+ current = prev_playlist(current);
+ }
+ for(j=0; j<32; j++)
+ {
+ if(get_playlist_data(current)->name[j] == '-')
+ {
+ na[j] = 0x00;
+ break;
+ }
+ else
+ {
+ na[j] = get_playlist_data(current)->name[j];
+ }
+ }
+ dbg(lvl_debug, "%s ca:%s na:%s strcmp: %i\n", get_playlist_name(current), ca, na, strncmp(ca, na, min(i,j)));
+ } while (strncmp(ca, na, min(i,j))==0);
+
+ system("mpc clear");
+ load_playlist(current);
+ mpd_play();
+ return current;
+}
+
+void
+mpd_next_artist(void)
+{
+ mpd->current_playlist = change_artist(mpd->current_playlist,1);
+ printf("next Artist");
+}
+
+void
+mpd_prev_artist(void)
+{
+ mpd->current_playlist = change_artist(mpd->current_playlist,0);
+ printf("next Artist");
+}
+
+/**
+* @brief test if a directory on the file system exists
+*
+* @param dir_name path of the directory
+*
+* @return true if the directory exists, false otherwise. surprise.
+*/
+gboolean
+directory_exists(char* dir_name)
+{
+ gboolean exists = false;
+ dbg(lvl_debug, "%s\n", dir_name);
+ DIR* dir = opendir(dir_name);
+ if (dir)
+ {
+ exists = true;
+ closedir(dir);
+ }
+ return exists;
+}
+
+/**
+* @brief this function checks if mpd already knows playlists and gets it or creates a new list of playlists
+*
+* @return the list of playlists
+*
+* mpd stores the saved playlists on the file system and can be asked for
+* them. If the audio player gets a list of playlists from mpd, it will
+* create a list of playlists from them. Otherwise it creates a new list
+* of playlist from the music that is stored inside the music directory
+*/
+GList*
+check_playlists(void)
+{
+ FILE *fp;
+ char _playlist[64];
+ GList* list = NULL;
+ gchar md[100] = {0,};
+ int i = 0;
+
+ fp = popen("mpc lsplaylists", "r");
+ if (fp == NULL) {
+ dbg(lvl_error, "Failed to run command\n" );
+ return(NULL);
+ }
+
+ while (fgets(_playlist, sizeof(_playlist)-1, fp) != NULL) {
+ strtok(_playlist, "\n");
+ dbg(lvl_debug, "Check Playlist: %s\n", _playlist);
+ i = 0;
+
+ while(i<100 && mpd->musicdir[i] != 0){
+ md[i] = mpd->musicdir[i];
+ i++;
+ }
+ for(;i<100;i++){
+ md[i] = 0;
+ }
+ dbg(lvl_debug,"Check Dir: %s/%s\n", mpd->musicdir, _playlist);
+ if(directory_exists(strcat(strcat(md,"/"),_playlist)))
+ {
+ dbg(lvl_debug,"%s exists\n",md);
+ list = insert_right(list, new_audio_playlist(_playlist, -1));
+ dbg(lvl_debug, "list = insert_right(list, new_audio_playlist(_playlist, -1));\n\n");
+ }
+ else
+ {
+ pclose(fp);
+ dbg(lvl_debug,"%s doesnt exist\n",md);
+ dbg(lvl_debug, "reload_playlists(mpd);\n\n");
+ reload_playlists(mpd);
+ return mpd->playlists;
+ }
+ }
+ pclose(fp);
+ return list;
+}
+
+/**
+* @brief this function deletes all playlists
+*
+* @param this the audio player object
+*/
+void
+delete_all_playlists(struct audio_priv* this)
+{
+ GList *head = g_list_first(this->playlists);
+ GList *current = g_list_first(this->current_playlist);
+ if(head == NULL || current == NULL)
+ { //
+ dbg(lvl_error,"no playlists found\n");
+ FILE *fp;
+ char text[64];
+ fp = popen("mpc lsplaylists", "r");
+ if (fp == NULL) {
+ dbg(lvl_error, "Failed to run command 'mpc lsplaylists'\n" );;
+ return;
+ }
+ while (fgets(text, sizeof(text)-1, fp) != NULL) {
+ strtok(text, "\n");
+ char command[100] = {0,};
+ strcat(command, "mpc rm \"");
+ strcat(command, text);
+ strcat(command, "\"");
+ system(&command[0]);
+ dbg(lvl_debug,command);
+ dbg(lvl_debug,"\n");
+ }
+ pclose(fp);
+ dbg(lvl_debug,"return;\n");
+ return;
+ }
+ while (current->next != NULL)
+ {
+ dbg(lvl_debug, "Delete: %s\n", get_playlist_name(current));
+ current = delete_playlist(current);
+ print_all(current);
+ }
+ char command[100] = {0,};
+ strcat(command, "mpc rm \"");
+ strcat(command, get_playlist_name(current));
+ strcat(command, "\"");
+ system(&command[0]);
+ dbg(lvl_debug,command);
+ system("mpc update --wait");
+ dbg(lvl_debug,"%s\n",get_playlist_name(current));
+ this->playlists = current;
+ this->current_playlist = current;
+}
+
+/**
+* @brief this function deletes and reloads all playlists
+*
+* @param this the audio player object
+*
+* This function is used to clean all playlist data and reload the playlist
+* this might be useful if the music is stored on a usb media and the usb
+* storage device is changed to change the music
+*/
+void
+reload_playlists(struct audio_priv* this)
+{
+ dbg(lvl_debug, "\nreload_playlists\n\n");
+ delete_all_playlists(this);
+ system("mpc clear");
+ system("mpc stop");
+ system("mpc update");
+ this->playlists = load_playlists();
+ sort_playlists(this->playlists);
+ this->current_playlist = this->playlists;
+ system("mpc update");
+ sleep(1);
+}
+
+/**
+* @brief this function reads the currently by mpd loaded playlist and
+* sets the playlist pointer on the corresponding object in the list of
+* playlists
+*
+* @param this the audio player object
+*
+* @return the pointer to the read playlist
+*
+* When the embedded device is shut down mpd keeps the currently loaded playlist and track position. On restart
+*/
+GList*
+get_last_playlist(struct audio_priv* this)
+{
+ FILE *fp;
+ GList* playlist = NULL;
+ char text[64];
+ char *playlist_name;
+ fp = popen("mpc -f %file%","r");
+ if (fp == NULL) {
+ dbg(lvl_error, "Failed to open file\n" );
+ return NULL;
+ }
+ if(fgets(text, sizeof(text)-1, fp) != NULL) {
+ playlist_name = strtok(text, "/");
+ if(strstr(text, "volume:") != NULL){
+ load_playlist(mpd->playlists);
+ //mpd_play();
+ fclose(fp);
+ return mpd->playlists;
+ }
+ dbg(lvl_debug, "Last Playlist: %s\n", playlist_name);
+ playlist = get_entry(this->playlists, playlist_name);
+ if(playlist == NULL){
+ dbg(lvl_debug, "Last Playlist not found! %p => %p\n",this->playlists, playlist);
+ playlist = this->playlists;
+ }
+ mpd->current_playlist = playlist;
+ }
+ fclose(fp);
+ dbg(lvl_debug, "returning: %s\n",get_playlist_name(playlist));
+ return playlist;
+}
+
+
+/**
+* @brief this function reads the track name to print it on the gui
+*
+* @param track_index the index of the track
+*
+* @return the track name
+*/
+char *
+mpd_get_track_name (int track_index)
+{
+ FILE *fp;
+ int l = 0;
+
+ fp = popen("mpc playlist -f %title%", "r");
+ if (fp == NULL) {
+ dbg(lvl_error, "Failed to run command 'mpc playlist'\n" );
+ return "Failed to run command 'mpc playlist'\n";
+ }
+ while (fgets(track, sizeof(track)-1, fp) != NULL) {
+ strtok(track, "\n");
+ if(l == track_index)
+ {
+ fclose(fp);
+ return track;
+ }
+ if(l>64)
+ {
+ fclose(fp);
+ return "NULL (track-name)";
+ }
+ l++;
+ }
+ fclose(fp);
+ return "NULL (track name)";
+}
+
+/**
+* @brief this function gets and returns the current playing playlist
+*
+* @return the playlist name
+*
+* this function is used for OSD and GUI
+*/
+char *
+mpd_get_current_playlist_name ()
+{
+
+ char *data = get_playlist_name(mpd->current_playlist);
+ if(data == NULL)
+ {
+ return " ";
+ }
+ return data;
+}
+
+/**
+* @brief this function reads the playlist name to print it on the gui
+*
+* @param playlist_index the index of the playlist
+*
+* @return the playlist name
+*/
+char *
+mpd_get_playlist_name (int playlist_index)
+{
+
+ dbg( lvl_debug, "%s\n", get_playlist_name(mpd->current_playlist));
+ GList *p = get_entry_by_index(mpd->playlists, playlist_index);
+ if(p != NULL)
+ {
+ char *data = get_playlist_name(p);
+ if(data != NULL)
+ {
+ dbg(lvl_debug, "%s\n", data);
+ return data;
+ }
+ }
+ dbg( lvl_debug, "%s\n", get_playlist_name(mpd->current_playlist));
+ return "NULL (playlist name index)";//text;
+}
+
+/**
+* @brief this function starts playback for the track from the current playlist
+*
+* @param track_index the number of the track
+*/
+void
+mpd_set_current_track (int track_index)
+{
+ mpd_play_track(track_index + 1);
+}
+
+/**
+* @brief this funtion reads the number of tracks for the current playlist
+*
+* @return the number of tracks
+*/
+int
+mpd_get_current_playlist_items_count ()
+{
+ FILE *fp;
+ int l = 0;
+ char text[64];
+ char command[100]= {0,};
+ strcat(command, "mpc playlist");
+ dbg(lvl_debug, "Run command '%s'\n", command );
+ fp = popen(command, "r");
+ if (fp == NULL)
+ {
+ dbg(lvl_error, "Failed to run command '%s'\n", command );
+ pclose(fp);
+ return 0;
+ }
+ while (fgets(text, sizeof(text)-1, fp) != NULL)
+ {
+ l++;
+ }
+ pclose(fp);
+ dbg (lvl_debug, "%i\n",l);
+ return l;
+}
+
+/**
+* @brief this function counts all playlists and returns the number
+*
+* @return the number of playlists
+*/
+int
+mpd_get_playlists_count(void)
+{
+ FILE *fp;
+ int l = 0;
+ char text[64];
+ fp = popen("mpc lsplaylists", "r");
+ if (fp == NULL) {
+ dbg(lvl_error, "Failed to run command 'mpc lsplaylists'\n" );
+ return 0;
+ }
+ while (fgets(text, sizeof(text)-1, fp) != NULL) {
+ l++;
+ }
+ pclose(fp);
+ return l;
+}
+
+/**
+* @brief this function starts playback for the given playlist
+*
+* @param playlist_index the given playlist to play
+*/
+void
+mpd_set_active_playlist (int playlist_index)
+{
+ mpd->playlist_index = playlist_index;
+ mpd->current_playlist = get_entry_by_index(mpd->playlists, playlist_index);
+ if(mpd->current_playlist == NULL){
+ mpd->playlist_index = -1;
+ return;
+ }
+ system("mpc clear");
+ load_playlist(mpd->current_playlist);
+ mpd_play();
+}
+
+
+/**
+ * @brief this function creates all possible actions for the player
+ *
+ * @return the list of actions
+ *
+ * Function to provide all possible audio actions for the player. These
+ * actions are acessible inside the media gui as a toolbar, so they
+ * might provide an icon. Their order will be applied to the toolbar too.
+ * It is possible to change the icon depending on the actions state
+ */
+GList*
+mpd_get_actions(void){
+ GList* actions = NULL;
+ struct audio_actions *aa;
+ if(mpd->actions){
+ return mpd->actions;
+ }else{
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_ARTIST;
+ aa->icon = g_strdup("media_prev_artist");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_PLAYLIST;
+ aa->icon = g_strdup("media_prev");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_TRACK;
+ aa->icon = g_strdup("media_minus");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_TOGGLE;
+ if(mpd->playing){
+ aa->icon = g_strdup("media_pause");
+ }else{
+ aa->icon = g_strdup("media_play");
+ }
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_TRACK;
+ aa->icon = g_strdup("media_plus");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_PLAYLIST;
+ aa->icon = g_strdup("media_next");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_ARTIST;
+ aa->icon = g_strdup("media_next_artist");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MODE_TOGGLE_REPEAT;
+ if(mpd->single && mpd->repeat){
+ aa->icon = g_strdup("media_repeat_track");
+ }else if(!mpd->single && mpd->repeat){
+ aa->icon = g_strdup("media_repeat_playlist");
+ }else{
+ aa->icon = g_strdup("media_repeat_off");
+ }
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MODE_TOGGLE_SHUFFLE;
+ if(mpd->random_track && mpd->random_playlist){
+ aa->icon = g_strdup("media_shuffle_tracks_playlists");
+ }else if(!mpd->random_track && mpd->random_playlist){
+ aa->icon = g_strdup("media_shuffle_playlists");
+ }else if(mpd->random_track && !mpd->random_playlist){
+ aa->icon = g_strdup("media_shuffle_tracks");
+ }else{
+ aa->icon = g_strdup("media_shuffle_off");
+ }
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MISC_DELETE_PLAYLIST;
+ aa->icon = g_strdup("media_trash");
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MISC_RELOAD_PLAYLISTS;
+ aa->icon = g_strdup("media_close");
+ actions = g_list_append(actions, aa);
+ }
+ return actions;
+}
+
+
+/**
+* @brief the loop function for the audio player
+*
+* @param the audio player object
+*
+* this function is the loop core for this plugin. Here is the playlist
+* change and the currend trackname processes for continous playback and
+* nice osd view. If the last song of a playlist ends, the mpd process
+* pauses playback automatically. For continous playback we have to
+* change to the next playlist.
+*
+* We also read some statuses and save them to the audio player object
+*/
+static void
+mpd_mpd_idle (struct audio_priv *mpd)
+{
+ dbg(lvl_debug, "%s\n", mpd_get_current_playlist_name());
+ if(mpd->current_playlist == NULL)
+ {
+ dbg(lvl_error,"Playlist=nULL\n");
+ return;
+ }
+ FILE *fp;
+ int l = 0, j = 0;
+ char text[64] = {0,};
+ fp = popen("mpc -f %title%", "r");
+ if (fp == NULL)
+ {
+ dbg(lvl_error, "Failed to run command 'mpc'\n" );
+ return;
+ }
+ while (fgets(text, sizeof(text)-1, fp) != NULL) {
+ strtok(text, "\n");
+ if(l==0)
+ {
+ if(mpd->playing){
+ if(strstr(text, "volume:") != NULL && mpd->current_track[0] != NO_TRACK)
+ {
+ // playlist ist zu ende.. die naechste bitte ;)
+ // playlist has finished, the next please.
+ system("mpc clear");
+ mpd->current_playlist = load_next_playlist(mpd->current_playlist);
+ for(j=0; j<64;j++)
+ {
+ mpd->current_track[j] = NO_TRACK;
+ }
+ mpd_play();
+ fclose(fp);
+ return;
+ }else{
+ strcpy(mpd->current_track, text);
+ dbg(lvl_debug, "Current track: %s\n",mpd->current_track);
+ }
+ }
+ }else{
+ // get mpc output and save as attribute
+
+ if(strstr(text, "volume:") != NULL)
+ {
+
+ if(!mpd->muted){
+ int i0 = text[7];
+ int i1 = text[8];
+ int i2 = text[9];
+ int i = (i0=='1')?100:0;
+ i+=(i1-'0')*10;
+ i+=(i2-'0');
+ mpd->volume = i;
+ }
+ if(strstr(text, "repeat: off") != NULL)
+ {
+ mpd->repeat = false;
+ }
+ else if(strstr(text, "repeat: on") != NULL)
+ {
+ mpd->repeat = true;
+ }
+ if(strstr(text, "random: off") != NULL)
+ {
+ mpd->random_track = false;
+ }
+ else if(strstr(text, "random: on") != NULL)
+ {
+ mpd->random_track = true;
+ }
+ if(strstr(text, "single: off") != NULL)
+ {
+ mpd->single = false;
+ }
+ else if(strstr(text, "single: on") != NULL)
+ {
+ mpd->single = true;
+ }
+ }
+ }
+ l++;
+ }
+ fclose(fp);
+
+}
+
+/**
+* @brief pause :)
+*/
+void mpd_pause(void)
+{
+ mpd->playing = 0;
+ system("mpc pause");
+ struct attr* playing = attr_search(mpd->attrs,NULL, attr_playing);
+ if(playing)
+ playing->u.num = mpd->playing;
+ else
+ dbg(lvl_debug, "No such Attr in %p\n", mpd->attrs);
+ dbg(lvl_debug,"\n");
+ callback_list_call_attr_0(mpd->cbl, attr_playing);
+}
+
+/**
+* @brief play :)
+*/
+void mpd_play(void)
+{
+ mpd->playing = true;
+ system("mpc play");
+ struct attr* playing = attr_search(mpd->attrs,NULL, attr_playing);
+ if(playing)
+ playing->u.num = mpd->playing;
+ else
+ dbg(lvl_debug, "No such Attr in %p\n", mpd->attrs);
+ dbg(lvl_debug, "\n");
+ callback_list_call_attr_0(mpd->cbl, attr_playing);
+}
+
+/**
+* @brief play a specific track
+*
+* @param track the number of the track to play
+*/
+void mpd_play_track(int track)
+{
+ char command[15] = {0,};
+ mpd->playing = true;
+ sprintf(command, "mpc play %i", track);
+ system(command);
+ struct attr* playing = attr_search(mpd->attrs,NULL, attr_playing);
+ if(playing)
+ playing->u.num = mpd->playing;
+ else
+ dbg(lvl_debug, "No such Attr in %p\n", mpd->attrs);
+ dbg(lvl_debug, "\n");
+ callback_list_call_attr_0(mpd->cbl, attr_playing);
+}
+
+/**
+* @brief load the next playlist
+*/
+void
+mpd_next_playlist(void)
+{
+ mpd->current_playlist = load_next_playlist(mpd->current_playlist);
+}
+
+/**
+* @brief load the previous playlist
+*/
+void
+mpd_prev_playlist(void)
+{
+ mpd->current_playlist = load_prev_playlist(mpd->current_playlist);
+}
+
+/**
+* @brief getter for the volume value of the audio player object
+*
+* @return the volume
+*/
+int mpd_get_volume(void)
+{
+ return mpd->volume;
+}
+
+/**
+* @brief sets the volume value
+*
+* @param vol the volume value to set
+*/
+void mpd_set_volume(int vol)
+{
+ char command[100] = {0,};
+ sprintf(command, "mpc volume %i", vol);
+ system(command);
+ mpd->volume = vol;
+}
+
+/**
+* @brief delete the current playlist
+*/
+void mpd_delete_playlist(void){
+ dbg(lvl_debug, "Hi, I was told to delete this playlist: %s", mpd_get_current_playlist_name());
+ delete_playlist(get_entry(mpd->playlists, mpd_get_current_playlist_name()));
+}
+
+/**
+* @brief this function toggles the repeat mode
+*
+* @param action the action that owns the toggle
+*/
+void mpd_toggle_repeat(struct audio_actions *action){
+
+ int toggle = 0;
+ if(mpd->single) toggle++;
+ if(mpd->repeat) toggle += 2;
+ switch(toggle){
+ case 0:{ // repeat playlist
+ system("mpc repeat on");
+ system("mpc single off");
+ mpd->single = FALSE;
+ mpd->repeat = TRUE;
+ if(action != NULL){
+ action->icon = g_strdup("media_repeat_playlist");
+ }
+ dbg(lvl_debug, " repeat playlist %i\n", toggle);
+ break;
+ }
+ case 2:
+ case 1:{//repeat off -> repeat track
+ system("mpc single on");
+ system("mpc repeat on");
+ mpd->single = TRUE;
+ mpd->repeat = TRUE;
+ if(action != NULL){
+ action->icon = g_strdup("media_repeat_track");
+ }
+ dbg(lvl_debug, " repeat track %i\n", toggle);
+ break;
+ }
+ case 3:{//repeat track -> repeat off
+ system("mpc repeat off");
+ system("mpc single off");
+ mpd->single = FALSE;
+ mpd->repeat = FALSE;
+ if(action != NULL){
+ action->icon = g_strdup("media_repeat_off");
+ }
+ dbg(lvl_debug, " repeat off %i\n", toggle);
+ break;
+ }
+ }
+ callback_list_call_attr_0(mpd->cbl, attr_repeat);
+}
+
+/**
+* @brief this function toggles the shuffle mode
+*
+* @param action the action that owns the toggle
+*
+* starting from OFF state the command switches through
+* shuffle track only (of the loaded playlist)
+* shuffle playlist and track (a randomly chosen track from the entire music database)
+* shuffle playlist only (play all tracks on the playlist in the given order but coose the next playlist randomly)
+*/
+void mpd_toggle_shuffle(struct audio_actions *action){
+
+ int toggle = 0;
+ if(mpd->random_track) toggle++;
+ if(mpd->random_playlist) toggle += 2;
+ dbg(lvl_debug, "Toggle Shuffle: %i\n", toggle);
+ switch(toggle){
+ //0 -> 1 -> 3 -> 2 -> 0
+ case 0:{
+ system("mpc random on");
+ mpd->random_track = TRUE;
+ mpd->random_playlist = FALSE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_playlists");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Playlists: %i\n", toggle);
+ break;
+ }
+ case 1:{
+ system("mpc random on");
+ mpd->random_track = TRUE;
+ mpd->random_playlist = TRUE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_tracks_playlists");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Tracks & Playlists: %i\n", toggle);
+ break;
+ }
+ case 3:{
+ system("mpc random off");
+ mpd->random_track = FALSE;
+ mpd->random_playlist = TRUE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_tracks");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Tracks: %i\n", toggle);
+ break;
+ }
+ case 2:{
+ system("mpc random off");
+ mpd->random_track = FALSE;
+ mpd->random_playlist = FALSE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_off");
+ }
+ dbg(lvl_debug, "Toggle Shuffle OFF: %i\n", toggle);
+ break;
+ }
+ }
+ callback_list_call_attr_0(mpd->cbl, attr_shuffle);
+}
+
+/**
+* @brief command to toggle playback
+*
+* @param action the action that owns the toggle
+*
+* the action must be passed because the playback action keeps the icon. This icon changes dependent on the playback state
+*/
+void
+mpd_toggle_playback (struct audio_actions *action)
+{
+ struct attr* playing = attr_search(mpd->attrs,NULL, attr_playing);
+ if(playing){
+ mpd_get_attr(mpd, attr_playing, playing);
+ playing->u.num = mpd->playing;
+ if(mpd_get_attr(mpd, attr_playing, playing) && playing->u.num)
+ mpd->playing = 1;
+ }
+ else
+ dbg (lvl_debug, "No such Attr in: %p\n", mpd->attrs);
+ if (mpd->playing)
+ {
+ dbg (0, "pausing playback\n");
+ system("mpc pause");
+ if(action != NULL){
+ action->icon = g_strdup("media_play");
+ }
+ }
+ else
+ {
+ dbg (0, "resuming playback\n");
+ system("mpc play");
+ if(action != NULL){
+ action->icon = g_strdup("media_pause");
+ }
+ }
+ mpd->playing = !mpd->playing;
+ if(playing){
+ playing->u.num = mpd->playing;
+ mpd_set_attr(mpd, playing);
+ }
+ callback_list_call_attr_0(mpd->cbl, attr_playing);
+}
+
+/**
+* @brief this function returns a list of playlists
+*
+* @param this the audio player object
+*
+* @return the list of playlists
+*
+* if no playlists are found, this function iniyializes the playists for this player
+*/
+GList *
+playlists(struct audio_priv *this)
+{
+ dbg(lvl_debug, "Mpd's playlists method\n");
+ GList * playlists=NULL;
+ playlists = mpd->playlists;
+ if(playlists==NULL)
+ {
+ dbg(lvl_error,"No Playlists found. Try again.\n");
+ mpd->playlists = load_playlists();
+ return mpd->playlists;
+ }
+ return playlists;
+}
+
+/**
+* @brief this function returns a list of tracks for a specific playlist
+*
+* @param this the audio player object
+* @param playlist_index the index of the playlist to get the tracks from
+*
+* @return the list of tracks
+*/
+GList *
+tracks(struct audio_priv *this, int playlist_index)
+{
+ GList * tracks=NULL;
+ struct audio_track *t;
+ int i, track_count;
+ if ( playlist_index < 0 ) {
+ playlist_index = mpd->playlist_index;
+ }
+
+ if(playlist_index != mpd->playlist_index)
+ {
+ mpd_set_active_playlist(playlist_index);
+ }
+ track_count = mpd_get_current_playlist_items_count();
+ dbg(lvl_debug, "playlist_index: %i\n", playlist_index);
+ for (i = 0; i < track_count; i++) {
+ t = g_new0(struct audio_track, 1);
+ t->name=g_strdup(mpd_get_track_name(i));
+ t->index=i;
+ t->status=0;
+ t->icon = "media_rating_full"; //we init with the green icon - maybe we will use thsi for any kind of rating in the future
+ tracks=g_list_append(tracks, t);
+ }
+ dbg(lvl_debug, "Active playlist updated\n");
+ return tracks;
+}
+
+/**
+* @brief this function returns the list of possible actions
+*
+* @param this the audio player object
+*
+* @return the list of actions
+*
+* if there are no actions present, the command inits the action list
+*/
+GList*
+actions(struct audio_priv *this){
+ dbg(lvl_debug, "In mpd's actions\n");
+ GList *act = NULL;
+ act = mpd->actions;
+ if(act){
+ return act;
+ }else{
+ mpd->actions = mpd_get_actions();
+ }
+ return mpd->actions;
+}
+
+/**
+* @brief this function iterates over all possible actions for this player and searches for an action
+*
+* @param actions the list of actions
+* @param action the action we want to find
+*
+* @return the audio action object wh searched or NULL if its not present
+*/
+struct audio_actions*
+get_specific_action(GList* actions, int specific_action)
+{
+ GList* result = g_list_first(actions);
+ if(result){
+ while(result->next != NULL){
+ struct audio_actions *aa = result->data;
+ if(aa->action == specific_action)
+ return aa;
+ result = g_list_next(result);
+ }
+ }
+ return NULL;
+}
+
+/**
+* @brief this function provides the action control for the audio player
+*
+* @param this the audio player object
+* @param action the action to be performed on the player
+*
+* @return returns the action
+*
+* possible actions:
+* AUDIO_PLAYBACK_PLAY
+* AUDIO_PLAYBACK_PAUSE
+* AUDIO_PLAYBACK_TOGGLE
+* AUDIO_PLAYBACK_NEXT_TRACK
+* AUDIO_PLAYBACK_PREVIOUS_TRACK
+* AUDIO_PLAYBACK_NEXT_PLAYLIST
+* AUDIO_PLAYBACK_PREVIOUS_PLAYLIST
+* AUDIO_PLAYBACK_NEXT_ARTIST: switches to the next playlist that differs before the Artist - Track delimiter " - "
+* AUDIO_PLAYBACK_PREVIOUS_ARTIST: switches to the next playlist that differs before the Artist - Track delimiter " - " but backwards
+* AUDIO_MISC_DELETE_PLAYLIST
+* AUDIO_MODE_TOGGLE_REPEAT: switches through the repeat modes
+* AUDIO_MODE_TOGGLE_SHUFFLE: switches through the shuffle modes
+* AUDIO_MISC_RELOAD_PLAYLISTS: reload all playlists (delete and reload)
+*/
+static int
+action_do(struct audio_priv *this, const int action)
+{
+ dbg(lvl_debug, "In mpd's action control\n");
+ switch(action)
+ {
+ case AUDIO_PLAYBACK_PLAY:{
+ dbg (lvl_debug, "resuming playback\n");
+ mpd_play();
+ break;
+ }
+ case AUDIO_PLAYBACK_PAUSE:{
+ dbg (lvl_debug, "pausing playback\n");
+ mpd_pause();
+ break;
+ }
+ case AUDIO_PLAYBACK_TOGGLE:{
+ mpd_toggle_playback(get_specific_action(mpd->actions, AUDIO_PLAYBACK_TOGGLE));
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_TRACK:{
+ system("mpc next");
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_TRACK:{
+ system("mpc prev");
+
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_PLAYLIST:{
+ mpd_next_playlist();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_PLAYLIST:{
+ mpd_prev_playlist();
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_ARTIST:{
+ mpd_next_artist();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_ARTIST:{
+ mpd_prev_artist();
+ break;
+ }
+ case AUDIO_MISC_DELETE_PLAYLIST:{
+ mpd_delete_playlist();
+ break;
+ }
+ case AUDIO_MODE_TOGGLE_REPEAT:{
+ mpd_toggle_repeat(get_specific_action(mpd->actions, AUDIO_MODE_TOGGLE_REPEAT));
+ break;
+ }
+ case AUDIO_MODE_TOGGLE_SHUFFLE:{
+ mpd_toggle_shuffle(get_specific_action(mpd->actions, AUDIO_MODE_TOGGLE_SHUFFLE));
+ break;
+ }
+ case AUDIO_MISC_RELOAD_PLAYLISTS:{
+ reload_playlists(mpd);
+ break;
+ }
+ default:{
+ dbg(lvl_error,"Don't know what to do with action '%i'. That's a bug\n", action);
+ break;
+ }
+ }
+ return action;
+}
+
+/**
+* @brief this function provides the playback control for the audio player
+*
+* @param this the audio player object
+* @param action the index of the song to play
+*
+* @return the playback status
+*
+* this function is accessed, when one clicks on a specific song in gui internal
+*/
+static int
+playback(struct audio_priv *this, const int action)
+{
+ dbg(lvl_debug, "In mpd's playback control\n");
+ if ( action > -1 ) {
+ mpd_play_track(action + 1);
+ } else {
+ dbg(lvl_error,"Don't know what to do with play track '%i'. That's a bug\n", action);
+ }
+ if(mpd->playing) return 1;
+ return 0;
+}
+
+/**
+* @brief this function provides the volume control for the audio player
+*
+* @param this the audio player object
+* @param action the kind of action to perform
+*
+* @return the resulting volume
+*
+* possible values are:
+* AUDIO_MUTE: save the current volume and set the mpd volume to 0
+* AUDIO_LOWER_VOLUME: decrease the volume by 1
+* AUDIO_RAISE_VOLUME: increase the volume by 1
+* any positive integer between 1 and 100: Set the value as volume
+*/
+static int
+volume(struct audio_priv *this, const int action)
+{
+ char command[18] = {0,};
+ switch(action)
+ {
+ case AUDIO_MUTE:
+ if(mpd->muted){
+ mpd_set_volume(mpd->volume);
+ }else{
+ sprintf(command, "mpc volume 0");
+ system(&command[0]);
+ }
+ mpd->muted = !mpd->muted;
+ break;
+ case AUDIO_LOWER_VOLUME:
+ system("mpc volume -1");
+ if(mpd->volume >= 0){
+ mpd->volume--;
+ }else{
+ mpd->volume = 0;
+ }
+ break;
+ case AUDIO_RAISE_VOLUME:
+ system("mpc volume +1");
+ if(mpd->volume <= 100){
+ mpd->volume++;
+ }else{
+ mpd->volume = 100;
+ }
+ break;
+ default:
+ if(action >0 && action <= 100){
+ sprintf(command, "mpc volume %i", action);
+ system(&command[0]);
+ mpd->volume = action;
+ } else {
+ dbg(lvl_error,"Don't know what to do with action '%i'. That's a bug\n", action);
+ }
+ break;
+ }
+
+ return mpd->volume;
+}
+
+/**
+* @brief this function returns the currently playing trsck
+*
+* @param this the audio player object
+*
+* @return the track name of the current track
+*/
+char*
+get_current_track(struct audio_priv *this)
+{
+ if(this->current_track[0] != NO_TRACK){
+ return this->current_track;
+ }else{
+ return "";
+ }
+}
+
+/**
+* @brief this function returns the currently loaded playlist
+*
+* @param this the audio player object
+*
+* @return the playlist name
+*/
+char*
+get_current_playlist(struct audio_priv *this)
+{
+ return mpd_get_current_playlist_name();
+}
+
+/**
+* @brief the audio methods
+*
+* these methods are acessible from anywhere in the program
+*/
+static struct audio_methods player_mpd_meth = {
+ volume,
+ playback,
+ action_do,
+ tracks,
+ playlists,
+ actions,
+ get_current_track,
+ get_current_playlist,
+ mpd_get_attr,
+ mpd_set_attr,
+};
+
+static struct audio_priv *
+player_mpd_new(struct audio_methods *meth, struct callback_list * cbl, struct attr **attrs, struct attr *parent)
+{
+ struct attr *attr, *playing, *shuffle, *repeat;
+
+ dbg(lvl_info,"Initializing mpd\n");
+ srandom(time(NULL));
+ mpd = g_new0 (struct audio_priv, 1);
+
+ attr=attr_search(attrs, NULL, attr_music_dir);
+ if ((attr = attr_search (attrs, NULL, attr_music_dir)))
+ {
+ mpd->musicdir = g_strdup(attr->u.str);
+
+ dbg (lvl_info, "found music directory: %s\n", mpd->musicdir);
+
+ }
+
+ audio_init (&g_audiofifo);
+
+ GList* pl = check_playlists();
+ if(pl == NULL){
+ reload_playlists(mpd);
+ }else{
+ mpd->playlists = sort_playlists(pl);
+ mpd->current_playlist = get_last_playlist(mpd);
+ }
+ if(!mpd->idle)
+ mpd->idle = callback_new_1 (callback_cast (mpd_mpd_idle), mpd);
+
+ if(!mpd->timeout)
+ mpd->timeout = event_add_timeout(1000, 1, mpd->idle);
+
+ if(!mpd->callback)
+ mpd->callback = callback_new_1 (callback_cast (mpd_mpd_idle), mpd);
+
+ if(!mpd->timeout)
+ mpd->timeout = event_add_timeout(1000, 1, mpd->callback);
+
+ mpd->playing = false;
+ mpd->attrs=attrs;
+ //*
+ playing = attr_search(mpd->attrs, NULL, attr_playing);
+
+ if(!playing){
+ playing = g_new0( struct attr, 1);
+ playing->type = attr_playing;
+ mpd->attrs=attr_generic_add_attr(mpd->attrs, playing);
+
+ }
+ repeat = attr_search(mpd->attrs, NULL, attr_repeat);
+
+ if(!repeat){
+ repeat = g_new0( struct attr, 1);
+ repeat->type = attr_repeat;
+ mpd->attrs=attr_generic_add_attr(mpd->attrs, repeat);
+
+ }
+ shuffle = attr_search(mpd->attrs, NULL, attr_shuffle);
+
+ if(!shuffle){
+ shuffle = g_new0( struct attr, 1);
+ shuffle->type = attr_shuffle;
+ mpd->attrs=attr_generic_add_attr(mpd->attrs, shuffle);
+
+ }
+ //*/
+
+ dbg (lvl_debug, "Callback created successfully\n");
+
+ mpd->cbl = cbl;
+
+ *meth=player_mpd_meth;
+
+ mpd_play();
+
+ return mpd;
+}
+
+/**
+* @brief plugin entry point
+*/
+void
+plugin_init(void)
+{
+ dbg (lvl_debug, "player-mpd\n");
+ plugin_register_category_audio("player-mpd", player_mpd_new);
+}
diff --git a/navit/audio/player-mpd/mpd.h b/navit/audio/player-mpd/mpd.h
new file mode 100644
index 000000000..7abee1bc8
--- /dev/null
+++ b/navit/audio/player-mpd/mpd.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2006-2009 Spotify Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * Audio output driver.
+ *
+ * This file is part of the libspotify examples suite.
+ */
+#ifndef _MPD_AUDIO_H_
+#define _MPD_AUDIO_H_
+
+#include <pthread.h>
+#include <stdint.h>
+#include <glib.h>
+#include "queue.h"
+#include <alsa/asoundlib.h>
+
+
+/* --- Types --- */
+typedef struct audio_fifo_data {
+ TAILQ_ENTRY(audio_fifo_data) link;
+ int channels;
+ int rate;
+ int nsamples;
+ int16_t samples[0];
+} audio_fifo_data_t;
+
+typedef struct audio_fifo {
+ TAILQ_HEAD(, audio_fifo_data) q;
+ int qlen;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+} audio_fifo_t;
+
+
+/* --- Functions --- */
+extern void audio_init(audio_fifo_t *af);
+extern void audio_fifo_flush(audio_fifo_t *af);
+audio_fifo_data_t* audio_get(audio_fifo_t *af);
+
+
+#endif /* _MPD_AUDIO_H_ */
diff --git a/navit/audio/player-spotify/CMakeLists.txt b/navit/audio/player-spotify/CMakeLists.txt
new file mode 100644
index 000000000..acef63504
--- /dev/null
+++ b/navit/audio/player-spotify/CMakeLists.txt
@@ -0,0 +1,2 @@
+module_add_library(audio_player-spotify spotify.c api_keys.c alsa-audio.c audio.c)
+target_link_libraries(audio_player-spotify ${ALSA_LIBRARY} ${LIBSPOTIFY_LIBRARIES})
diff --git a/navit/audio/player-spotify/alsa-audio.c b/navit/audio/player-spotify/alsa-audio.c
new file mode 100644
index 000000000..c722abb83
--- /dev/null
+++ b/navit/audio/player-spotify/alsa-audio.c
@@ -0,0 +1,269 @@
+//* vim: set tabstop=4 expandtab: */
+/*
+ * Copyright (c) 2006-2009 Spotify Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * ALSA audio output driver.
+ *
+ * This file is part of the libspotify examples suite.
+ */
+
+#include <alsa/asoundlib.h>
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <glib.h>
+#include "debug.h"
+
+#include "alsa.h"
+#include "spotify.h"
+
+struct arg_struct {
+ audio_fifo_t *af;
+ char *audio_playback_pcm;
+};
+
+static snd_pcm_t *
+alsa_open(char *dev, int rate, int channels)
+{
+ snd_pcm_hw_params_t *hwp;
+ snd_pcm_sw_params_t *swp;
+ snd_pcm_t *h;
+ int r;
+ int dir;
+ snd_pcm_uframes_t period_size_min;
+ snd_pcm_uframes_t period_size_max;
+ snd_pcm_uframes_t buffer_size_min;
+ snd_pcm_uframes_t buffer_size_max;
+ snd_pcm_uframes_t period_size;
+ snd_pcm_uframes_t buffer_size;
+
+ if ((r = snd_pcm_open(&h, dev, SND_PCM_STREAM_PLAYBACK, 0) < 0)) {
+ dbg(lvl_error, "Something went wrong with %s\n", dev);
+ return NULL;
+ }
+
+ hwp = alloca(snd_pcm_hw_params_sizeof());
+ memset(hwp, 0, snd_pcm_hw_params_sizeof());
+ snd_pcm_hw_params_any(h, hwp);
+
+ snd_pcm_hw_params_set_access(h, hwp,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ snd_pcm_hw_params_set_format(h, hwp, SND_PCM_FORMAT_S16_LE);
+ snd_pcm_hw_params_set_rate(h, hwp, rate, 0);
+ snd_pcm_hw_params_set_channels(h, hwp, channels);
+
+ /* Configurue period */
+
+ dir = 0;
+ snd_pcm_hw_params_get_period_size_min(hwp, &period_size_min, &dir);
+ dir = 0;
+ snd_pcm_hw_params_get_period_size_max(hwp, &period_size_max, &dir);
+
+ period_size = 1024;
+
+ dir = 0;
+ r = snd_pcm_hw_params_set_period_size_near(h, hwp, &period_size,
+ &dir);
+
+ if (r < 0) {
+ dbg(lvl_error,
+ "audio: Unable to set period size %lu (%s)\n",
+ period_size, snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ dir = 0;
+ r = snd_pcm_hw_params_get_period_size(hwp, &period_size, &dir);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to get period size (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ /* Configurue buffer size */
+
+ snd_pcm_hw_params_get_buffer_size_min(hwp, &buffer_size_min);
+ snd_pcm_hw_params_get_buffer_size_max(hwp, &buffer_size_max);
+ buffer_size = period_size * 4;
+
+ dir = 0;
+ r = snd_pcm_hw_params_set_buffer_size_near(h, hwp, &buffer_size);
+
+ if (r < 0) {
+ dbg(lvl_error,
+ "audio: Unable to set buffer size %lu (%s)\n",
+ buffer_size, snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ r = snd_pcm_hw_params_get_buffer_size(hwp, &buffer_size);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Unable to get buffer size (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ /* write the hw params */
+ r = snd_pcm_hw_params(h, hwp);
+
+ if (r < 0) {
+ dbg(lvl_error,
+ "audio: Unable to configure hardware parameters (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ /*
+ * Software parameters
+ */
+
+ swp = alloca(snd_pcm_sw_params_sizeof());
+ memset(hwp, 0, snd_pcm_sw_params_sizeof());
+ snd_pcm_sw_params_current(h, swp);
+
+ r = snd_pcm_sw_params_set_avail_min(h, swp, period_size);
+
+ if (r < 0) {
+ dbg(lvl_error,
+ "audio: Unable to configure wakeup threshold (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ snd_pcm_sw_params_set_start_threshold(h, swp, 0);
+
+ if (r < 0) {
+ dbg(lvl_error,
+ "audio: Unable to configure start threshold (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ r = snd_pcm_sw_params(h, swp);
+
+ if (r < 0) {
+ dbg(lvl_error, "audio: Cannot set soft parameters (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ r = snd_pcm_prepare(h);
+ if (r < 0) {
+ dbg(lvl_error,
+ "audio: Cannot prepare audio for playback (%s)\n",
+ snd_strerror(r));
+ snd_pcm_close(h);
+ return NULL;
+ }
+
+ return h;
+}
+
+static void *
+alsa_audio_start(void *arguments)
+{
+ struct arg_struct *args = arguments;
+ char *audio_playback_pcm;
+ audio_playback_pcm = malloc(256);
+ strcpy(audio_playback_pcm, args->audio_playback_pcm);
+ dbg(lvl_info, "will use audio_playback_pcm : %s\n",
+ audio_playback_pcm);
+
+ audio_fifo_t *af = args->af;
+ snd_pcm_t *h = NULL;
+ int c;
+ int cur_channels = 0;
+ int cur_rate = 0;
+
+ audio_fifo_data_t *afd;
+ for (;;) {
+ afd = audio_get(af);
+ if (!h || cur_rate != afd->rate
+ || cur_channels != afd->channels) {
+ if (h)
+ snd_pcm_close(h);
+
+ cur_rate = afd->rate;
+ cur_channels = afd->channels;
+
+ h = alsa_open(audio_playback_pcm, cur_rate,
+ cur_channels);
+
+ if (!h) {
+ dbg(lvl_error,
+ "Unable to open ALSA device (%d channels, %d Hz), can't continue\n",
+ cur_channels, cur_rate);
+ return NULL;
+ } else {
+ dbg(lvl_info,
+ "ALSA device (%d channels, %d Hz), ok\n",
+ cur_channels, cur_rate);
+ }
+ }
+
+ c = snd_pcm_wait(h, 1000);
+
+ if (c >= 0)
+ c = snd_pcm_avail_update(h);
+
+ if (c == -EPIPE)
+ snd_pcm_prepare(h);
+
+ snd_pcm_writei(h, afd->samples, afd->nsamples);
+ free(afd);
+ }
+ dbg(lvl_debug, "spotify alsa_audio_start done\n");
+}
+
+void
+audio_init(audio_fifo_t * af, char *audio_playback_pcm)
+{
+ pthread_t tid;
+
+ TAILQ_INIT(&af->q);
+ af->qlen = 0;
+
+ pthread_mutex_init(&af->mutex, NULL);
+ pthread_cond_init(&af->cond, NULL);
+
+ struct arg_struct args;
+ args.af = af;
+ args.audio_playback_pcm = g_strdup(audio_playback_pcm);
+
+ pthread_create(&tid, NULL, alsa_audio_start, (void *) &args);
+ // The sleep ensures that the audio is initialized asynchronously before returning.
+ // We probably need to find a better way to do this
+ sleep(1);
+}
diff --git a/navit/audio/player-spotify/api_keys.c b/navit/audio/player-spotify/api_keys.c
new file mode 100644
index 000000000..0ec9f1ced
--- /dev/null
+++ b/navit/audio/player-spotify/api_keys.c
@@ -0,0 +1,37 @@
+#include <stdint.h>
+#include <stdlib.h>
+char *googleplaces_apikey = "AIzaSyA1BExvFLmDlKfsJJIUITjMH0y4OFspuZc";
+char *forecast_io_key = "d36966d743ffb038bdb0bf01eb133cd9";
+
+char *foursquare_client_id = "J404HD3HDXDO0HPD3FAVEMQ3BCU0SOE0YUPCMZOUHRBQC04M";
+char *foursquare_client_secret = "PJCGRAXQ1QZG0MKGCJPYXKF0GDHTB1I2PDESWSN11ZDFMJS3";
+char *foursquare_oauth_token = "VRHALS14NFZ2IWMIY4LGABFEG0RUWGNHIZUPUYTAAZ2KFPRD";
+
+char *echonest_api_key= "LJK7YPKRPD1HFGU43";
+char *echonest_consumer_key= "d53dd4819a561a513008e80f931734e1";
+char *echonest_shared_secret= "ASxQM/9nRlqeJL3GquAWug";
+
+const uint8_t spotify_apikey[] = {
+ 0x01, 0xC0, 0x10, 0xE9, 0xA6, 0x8B, 0x00, 0xA5, 0x57, 0xDC, 0x1D, 0x66, 0xAC, 0x37, 0x99, 0x93,
+ 0x9D, 0x33, 0xB2, 0x4E, 0x04, 0x8B, 0xCC, 0xAD, 0x86, 0x9B, 0x36, 0x6B, 0x20, 0x0B, 0x8D, 0x30,
+ 0x6A, 0x69, 0x7E, 0xA8, 0x92, 0x04, 0x12, 0x68, 0x09, 0xB5, 0xBE, 0xD1, 0xB3, 0xBA, 0xA6, 0xB8,
+ 0xBE, 0xD9, 0xED, 0x84, 0x02, 0x6A, 0x0B, 0xCA, 0xA9, 0xB8, 0x61, 0xD1, 0x86, 0xDB, 0x9E, 0xC9,
+ 0xAA, 0x02, 0xEA, 0xA2, 0xC4, 0x6A, 0x7F, 0xB9, 0xC1, 0xC2, 0x52, 0x7B, 0x1B, 0x97, 0xA6, 0xA1,
+ 0xD7, 0x4E, 0xBC, 0x05, 0x08, 0x48, 0x8A, 0x96, 0xF0, 0x12, 0xD0, 0x0E, 0xEC, 0x94, 0xAC, 0x6F,
+ 0x4F, 0x97, 0xCC, 0x58, 0x65, 0x45, 0xC2, 0x4F, 0xF8, 0x0E, 0xD5, 0xC3, 0x53, 0x55, 0x2A, 0x39,
+ 0xD4, 0x6C, 0x8F, 0xD3, 0x98, 0xE8, 0x69, 0x16, 0x4D, 0x6A, 0x81, 0x20, 0xA8, 0x8F, 0x0C, 0x68,
+ 0x33, 0x3B, 0x80, 0x9A, 0x4C, 0xDC, 0x0E, 0x15, 0x98, 0x06, 0xF8, 0x98, 0xCF, 0x55, 0x23, 0xD8,
+ 0xF4, 0x7F, 0x39, 0x1A, 0xF8, 0xE4, 0x9C, 0x3A, 0x28, 0x31, 0xBF, 0xFC, 0xF9, 0xDC, 0xCA, 0x95,
+ 0x64, 0x26, 0x82, 0x91, 0x0E, 0xD3, 0x28, 0xE6, 0x8A, 0xAE, 0xFF, 0x32, 0xD9, 0xFF, 0xEF, 0x2F,
+ 0x4A, 0xBA, 0xC3, 0x80, 0x00, 0x47, 0x15, 0x97, 0xBC, 0x5A, 0xA8, 0x25, 0x73, 0x8B, 0xC9, 0xE9,
+ 0xC0, 0xD7, 0x6C, 0x63, 0x25, 0x13, 0xB0, 0x19, 0x4D, 0x70, 0x0C, 0xC1, 0xF5, 0x85, 0x2F, 0x4B,
+ 0xD6, 0xB5, 0xD0, 0x50, 0x47, 0x96, 0xC8, 0xA9, 0x23, 0x89, 0x75, 0x03, 0x3E, 0x96, 0x68, 0xC3,
+ 0x27, 0x2A, 0xDA, 0xE0, 0xA5, 0xB5, 0x64, 0x78, 0x84, 0x21, 0xDE, 0x7A, 0xF6, 0x48, 0xA1, 0x4E,
+ 0xA0, 0x49, 0x8C, 0x75, 0xCE, 0x54, 0x43, 0xC5, 0x80, 0x29, 0xC9, 0x41, 0x58, 0xE1, 0xA7, 0xC6,
+ 0xD6, 0xB1, 0x45, 0xF4, 0x06, 0xE1, 0x38, 0x50, 0x7A, 0x3E, 0x00, 0x6A, 0x58, 0xEC, 0xD5, 0x8C,
+ 0x5D, 0xFD, 0x9B, 0x3C, 0x67, 0xDC, 0x45, 0x49, 0x98, 0x35, 0x95, 0x16, 0xE8, 0x64, 0xE2, 0x35,
+ 0x4D, 0x4A, 0x8D, 0xD3, 0x15, 0xE1, 0x04, 0x74, 0x1E, 0xD2, 0xD3, 0x3E, 0x00, 0xFF, 0x8A, 0x0D,
+ 0x3D, 0x94, 0x9F, 0x91, 0xAC, 0x43, 0x19, 0x60, 0x7C, 0x1C, 0x20, 0xEF, 0xCF, 0x03, 0x8D, 0xE8,
+ 0xD4,
+};
+const size_t spotify_apikey_size = sizeof(spotify_apikey);
diff --git a/navit/audio/player-spotify/audio.c b/navit/audio/player-spotify/audio.c
new file mode 100644
index 000000000..2bada491d
--- /dev/null
+++ b/navit/audio/player-spotify/audio.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010 Spotify Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * Audio helper functions.
+ *
+ * This file is part of the libspotify examples suite.
+ */
+
+#include "spotify.h"
+#include <stdlib.h>
+
+audio_fifo_data_t *
+audio_get(audio_fifo_t * af)
+{
+ audio_fifo_data_t *afd;
+ pthread_mutex_lock(&af->mutex);
+
+ while (!(afd = TAILQ_FIRST(&af->q)))
+ pthread_cond_wait(&af->cond, &af->mutex);
+
+ TAILQ_REMOVE(&af->q, afd, link);
+ af->qlen -= afd->nsamples;
+
+ pthread_mutex_unlock(&af->mutex);
+ return afd;
+}
+
+void
+audio_fifo_flush(audio_fifo_t * af)
+{
+ printf("audio_fifo_flush\n");
+ audio_fifo_data_t *afd;
+
+
+ pthread_mutex_lock(&af->mutex);
+
+ while ((afd = TAILQ_FIRST(&af->q))) {
+ TAILQ_REMOVE(&af->q, afd, link);
+ free(afd);
+ }
+
+ af->qlen = 0;
+ pthread_mutex_unlock(&af->mutex);
+}
diff --git a/navit/audio/player-spotify/spotify.c b/navit/audio/player-spotify/spotify.c
new file mode 100644
index 000000000..4ee7178bd
--- /dev/null
+++ b/navit/audio/player-spotify/spotify.c
@@ -0,0 +1,1119 @@
+/* vim: set tabstop=8 expandtab: */
+#include <glib.h>
+#include "item.h"
+#include <navit/main.h>
+#include <navit/debug.h>
+#include <libspotify/api.h>
+#include <navit/callback.h>
+#include <navit/event.h>
+#include <navit/audio.h>
+#include "spotify.h"
+
+#define AUDIO_PLAYBACK_NEXT AUDIO_PLAYBACK_NEXT_TRACK
+#define AUDIO_PLAYBACK_PREVIOUS AUDIO_PLAYBACK_PREVIOUS_TRACK
+
+
+struct audio_priv {
+ /* this is the data structure for the audio plugin
+ * you might not need every element of it
+ */
+ struct navit *navit;
+ struct callback *callback;
+ struct event_idle *idle;
+ struct callback_list *cbl;
+ struct event_timeout *timeout;
+ struct attr **attrs;
+ GList *current_playlist;
+ GList *playlists;
+ GList *actions;
+ gboolean random_track;
+ gboolean random_playlist;
+ gboolean repeat;
+ gboolean single;
+ gboolean shuffle;
+ char current_track[64];
+ int volume;
+ int width;
+ gboolean muted;
+ int playing;
+ char *login;
+ char *password;
+ char *playlist;
+ sp_playlistcontainer *pc;
+ char *audio_playback_pcm;
+
+} *spotify;
+
+
+const bool autostart = 0;
+
+int current_playlist_index;
+/// Handle to the playlist currently being played
+static sp_playlist *g_jukeboxlist;
+/// Handle to the current track
+static sp_track *g_currenttrack;
+/// Index to the next track
+static int g_track_index;
+static audio_fifo_t g_audiofifo;
+static sp_session *g_sess;
+int g_logged_in;
+int next_timeout = 0;
+
+extern const uint8_t spotify_apikey[];
+extern const size_t spotify_apikey_size;
+
+
+char *spotify_get_playlist_name(int playlist_index);
+char *get_playlist_name(GList * list);
+void spotify_play(void);
+void spotify_pause(void);
+void spotify_play_track(int track);
+GList *get_entry_by_index(GList * list, int index);
+void spotify_toggle_repeat(struct audio_actions *action);
+void spotify_toggle_shuffle(struct audio_actions *action);
+struct audio_actions *get_specific_action(GList * actions,
+ int specific_action);
+
+/**
+ * Get function for attributes
+ *
+ * @param priv Pointer to the audio instance data
+ * @param type The attribute type to look for
+ * @param attr Pointer to a {@code struct attr} to store the attribute
+ * @return True for success, false for failure
+ */
+int
+spotify_get_attr(struct audio_priv *priv, enum attr_type type,
+ struct attr *attr)
+{
+ int ret = 1;
+ dbg(lvl_debug, "priv: %p, type: %i (%s), attr: %p\n", priv, type,
+ attr_to_name(type), attr);
+ if (priv != spotify) {
+ dbg(lvl_debug, "failed\n");
+ return -1;
+ }
+ switch (type) {
+ case attr_playing:{
+ attr->u.num = spotify->playing;
+ dbg(lvl_debug, "Status: %ld\n", attr->u.num);
+ break;
+ }
+ case attr_name:{
+ attr->u.str = g_strdup("spotify");
+ dbg(lvl_debug, "%s\n", attr->u.str);
+ break;
+ }
+ case attr_shuffle:{
+ int toggle = 0;
+ if (spotify->random_track)
+ toggle++;
+ if (spotify->random_playlist)
+ toggle += 2;
+ attr->u.num = toggle;
+ dbg(lvl_debug, "%ld\n", attr->u.num);
+ break;
+ }
+ case attr_repeat:{
+ int toggle = 0;
+ if (spotify->single)
+ toggle++;
+ if (spotify->repeat)
+ toggle += 2;
+ attr->u.num = toggle;
+ dbg(lvl_debug, "%ld\n", attr->u.num);
+ break;
+ }
+ default:{
+ dbg(lvl_error,
+ "Don't know what to do with ATTR type %s\n",
+ attr_to_name(attr->type));
+ ret = 0;
+ break;
+ }
+ }
+ attr->type = type;
+ return ret;
+}
+
+/**
+ * Set function for attributes
+ *
+ * @param priv An audio instance data
+ * @param attr The attribute to set
+ * @return False on success, true on failure
+ */
+int
+spotify_set_attr(struct audio_priv *priv, struct attr *attr)
+{
+ dbg(lvl_debug, "priv: %p, type: %i (%s), attr: %p\n", priv,
+ attr->type, attr_to_name(attr->type), attr);
+ if (priv != spotify) {
+ dbg(lvl_debug, "failed\n");
+ return -1;
+ }
+ if (attr) {
+ switch (attr->type) {
+ case attr_name:{
+ dbg(lvl_debug, "%s\n", attr->u.str);
+ break;
+ }
+ case attr_playing:{
+ dbg(lvl_debug, "attr->u.num: %ld\n",
+ attr->u.num);
+
+ if (attr->u.num == 0) {
+ dbg(lvl_debug,
+ "spotify_pause();%ld\n",
+ attr->u.num);
+ spotify_pause();
+ } else {
+ dbg(lvl_debug,
+ "spotify_play();%ld\n",
+ attr->u.num);
+ spotify_play();
+ }
+
+ break;
+ }
+ case attr_shuffle:{
+
+ spotify_toggle_shuffle(get_specific_action
+ (spotify->actions,
+ AUDIO_MODE_TOGGLE_SHUFFLE));
+ break;
+ }
+ case attr_repeat:{
+
+ spotify_toggle_repeat(get_specific_action
+ (spotify->actions,
+ AUDIO_MODE_TOGGLE_REPEAT));
+ break;
+ }
+ default:{
+ dbg(lvl_error,
+ "Don't know what to do with ATTR type %s",
+ attr_to_name(attr->type));
+ return 0;
+ break;
+ }
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief this function creates all possible actions for the player
+ *
+ * @return the list of actions
+ *
+ * Function to provide all possible audio actions for the player. These
+ * actions are acessible inside the media gui as a toolbar, so they
+ * might provide an icon. Their order will be applied to the toolbar too.
+ * It is possible to change the icon depending on the actions state
+ */
+GList *
+spotify_get_actions(void)
+{
+ /**
+ * this function creates all actions your player is able to perform
+ * except volume and choosing of a specific track
+ * just remove the actions you do not need
+ * all listed actions will be put into a player toolbar with its icon
+ * so you should define one for each action
+ */
+ GList *actions = NULL;
+ struct audio_actions *aa;
+ if (spotify->actions) {
+ return spotify->actions;
+ } else {
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_ARTIST;
+ aa->icon = g_strdup("media_prev_artist"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_PLAYLIST;
+ aa->icon = g_strdup("media_prev"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_TRACK;
+ aa->icon = g_strdup("media_minus"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_TOGGLE;
+ if (spotify->playing) {
+ aa->icon = g_strdup("media_pause");
+ } else {
+ aa->icon = g_strdup("media_play");
+ }
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_TRACK;
+ aa->icon = g_strdup("media_plus"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_PLAYLIST;
+ aa->icon = g_strdup("media_next"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_ARTIST;
+ aa->icon = g_strdup("media_next_artist"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MODE_TOGGLE_REPEAT;
+ aa->icon = g_strdup("media_repeat_off"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MODE_TOGGLE_SHUFFLE;
+ aa->icon = g_strdup("media_shuffle_off"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MISC_DELETE_PLAYLIST;
+ aa->icon = g_strdup("media_trash"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MISC_RELOAD_PLAYLISTS;
+ aa->icon = g_strdup("media_close"); //todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+ }
+ return actions;
+}
+
+
+
+/**
+* @brief this function returns the list of possible actions
+*
+* @param this the audio player object
+*
+* @return the list of actions
+*
+* if there are no actions present, the command inits the action list
+*/
+GList *
+actions(struct audio_priv * this)
+{
+ dbg(lvl_debug, "In spotify's actions\n");
+ GList *act = NULL;
+ act = spotify->actions;
+ if (act) {
+ return act;
+ } else {
+ spotify->actions = spotify_get_actions();
+ }
+ return spotify->actions;
+}
+
+/**
+* @brief this function iterates over all possible actions for this player and searches for an action
+*
+* @param actions the list of actions
+* @param action the action we want to find
+*
+* @return the audio action object wh searched or NULL if its not present
+*/
+struct audio_actions *
+get_specific_action(GList * actions, int specific_action)
+{
+ GList *result = g_list_first(actions);
+ while (result != NULL && result->next != NULL) {
+ struct audio_actions *aa = result->data;
+ if (aa->action == specific_action)
+ return aa;
+ result = g_list_next(result);
+ }
+ return NULL;
+}
+
+/**
+* @brief this function provides the action control for the audio player
+*
+* @param this the audio player object
+* @param action the action to be performed on the player
+*
+* @return returns the action
+*
+* possible actions:
+* AUDIO_PLAYBACK_PLAY
+* AUDIO_PLAYBACK_PAUSE
+* AUDIO_PLAYBACK_TOGGLE
+* AUDIO_PLAYBACK_NEXT_TRACK
+* AUDIO_PLAYBACK_PREVIOUS_TRACK
+* AUDIO_PLAYBACK_NEXT_PLAYLIST
+* AUDIO_PLAYBACK_PREVIOUS_PLAYLIST
+* AUDIO_PLAYBACK_NEXT_ARTIST: switches to the next playlist that differs before the Artist - Track delimiter " - "
+* AUDIO_PLAYBACK_PREVIOUS_ARTIST: switches to the next playlist that differs before the Artist - Track delimiter " - " but backwards
+* AUDIO_MISC_DELETE_PLAYLIST
+* AUDIO_MODE_TOGGLE_REPEAT: switches through the repeat modes
+* AUDIO_MODE_TOGGLE_SHUFFLE: switches through the shuffle modes
+* AUDIO_MISC_RELOAD_PLAYLISTS: reload all playlists (delete and reload)
+*/
+static int
+action_do(struct audio_priv *this, const int action)
+{
+ dbg(lvl_debug, "In spotify's action control\n");
+ /** methosd where the defined actions are mentioned
+ * remove the case blocks for actions you do not need
+ */
+ switch (action) {
+ case AUDIO_PLAYBACK_PAUSE:{
+ spotify_pause();
+ break;
+ }
+ case AUDIO_PLAYBACK_PLAY:{
+ spotify_play();
+ break;
+ }
+ case AUDIO_PLAYBACK_TOGGLE:{
+ spotify_toggle_playback(get_specific_action
+ (spotify->actions,
+ AUDIO_PLAYBACK_TOGGLE));
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_TRACK:{
+ ++g_track_index;
+ try_jukebox_start();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_TRACK:{
+ if (g_track_index > 0) {
+ --g_track_index;
+ try_jukebox_start();
+ }
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_PLAYLIST:{
+ //spotify_next_playlist();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_PLAYLIST:{
+ //spotify_prev_playlist();
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_ARTIST:{
+ //spotify_next_artist();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_ARTIST:{
+ //spotify_prev_artist();
+ break;
+ }
+ case AUDIO_MISC_DELETE_PLAYLIST:{
+ //spotify_delete_playlist();
+ break;
+ }
+ case AUDIO_MODE_TOGGLE_REPEAT:{
+ /* if your player has different repeat modes
+ * you can have different icons for each mode
+ */
+ spotify_toggle_repeat(get_specific_action
+ (spotify->actions,
+ AUDIO_MODE_TOGGLE_REPEAT));
+ break;
+ }
+ case AUDIO_MODE_TOGGLE_SHUFFLE:{
+ /* if your player has different shuffle modes
+ * you can have different icons for each mode
+ */
+ spotify_toggle_shuffle(get_specific_action
+ (spotify->actions,
+ AUDIO_MODE_TOGGLE_SHUFFLE));
+ break;
+ }
+ case AUDIO_MISC_RELOAD_PLAYLISTS:{
+ /* maybe you'll never need this
+ */
+ //reload_playlists(spotify);
+ break;
+ }
+ default:{
+ dbg(lvl_error,
+ "Don't know what to do with action '%i'. That's a bug\n",
+ action);
+ break;
+ }
+ }
+ return action;
+}
+
+/**
+* @brief this function returns the currently playing trsck
+*
+* @param this the audio player object
+*
+* @return the track name of the current track
+*/
+char *
+current_track(struct audio_priv *this)
+{
+ if (g_currenttrack) {
+ return sp_track_name(g_currenttrack);
+ } else {
+ return "";
+ }
+}
+
+/**
+* @brief this function returns the currently loaded playlist
+*
+* @param this the audio player object
+*
+* @return the playlist name
+*/
+char *
+current_playlist(struct audio_priv *this)
+{
+ if (g_jukeboxlist) {
+ return sp_playlist_name(g_jukeboxlist);
+ } else {
+ return "";
+ }
+}
+
+
+
+static void
+try_jukebox_start(void)
+{
+ dbg(lvl_info, "Starting the jukebox\n");
+ sp_track *t;
+ spotify->playing = 0;
+
+ if (!g_jukeboxlist) {
+ dbg(lvl_info, "jukebox: No playlist. Waiting\n");
+ return;
+ }
+
+ if (!sp_playlist_num_tracks(g_jukeboxlist)) {
+ dbg(lvl_info, "jukebox: No tracks in playlist. Waiting\n");
+ return;
+ }
+
+ if (sp_playlist_num_tracks(g_jukeboxlist) < g_track_index) {
+ dbg(lvl_info,
+ "jukebox: No more tracks in playlist. Waiting\n");
+ return;
+ }
+
+ t = sp_playlist_track(g_jukeboxlist, g_track_index);
+
+ if (g_currenttrack && t != g_currenttrack) {
+ /* Someone changed the current track */
+ audio_fifo_flush(&g_audiofifo);
+ sp_session_player_unload(g_sess);
+ g_currenttrack = NULL;
+ }
+
+ if (!t)
+ return;
+
+ if (sp_track_error(t) != SP_ERROR_OK)
+ return;
+
+ if (g_currenttrack == t)
+ return;
+
+ g_currenttrack = t;
+
+ dbg(lvl_info, "jukebox: Now playing \"%s\"... g_sess=%p\n",
+ sp_track_name(t), g_sess);
+
+ // Ensure that the playlist is "offline" if we play a track
+ sp_playlist_set_offline_mode(g_sess, g_jukeboxlist, 1);
+ dbg(lvl_info, "Forced offline mode for the playlist\n");
+
+ sp_session_player_load(g_sess, t);
+ spotify->playing = 1;
+ if (g_sess) {
+ sp_session_player_play(g_sess, 1);
+ dbg(lvl_info, "sp_session_player_play called\n");
+ } else {
+ dbg(lvl_error, "g_sess is null\n");
+ }
+}
+
+/**
+ * Callback from libspotify, saying that a track has been added to a playlist.
+ *
+ * @param pl The playlist handle
+ * @param tracks An array of track handles
+ * @param num_tracks The number of tracks in the \c tracks array
+ * @param position Where the tracks were inserted
+ * @param userdata The opaque pointer
+ */
+static void
+tracks_added(sp_playlist * pl, sp_track * const *tracks, int num_tracks,
+ int position, void *userdata)
+{
+ dbg(lvl_info, "jukebox: %d tracks were added to %s\n", num_tracks,
+ sp_playlist_name(pl));
+
+ if (!strcasecmp(sp_playlist_name(pl), spotify->playlist)) {
+ g_jukeboxlist = pl;
+ if (autostart)
+ try_jukebox_start();
+ }
+}
+
+/**
+ * The callbacks we are interested in for individual playlists.
+ */
+static sp_playlist_callbacks pl_callbacks = {
+ .tracks_added = &tracks_added,
+// .tracks_removed = &tracks_removed,
+// .tracks_moved = &tracks_moved,
+// .playlist_renamed = &playlist_renamed,
+};
+
+/**
+ * Callback from libspotify, telling us a playlist was added to the playlist container.
+ *
+ * We add our playlist callbacks to the newly added playlist.
+ *
+ * @param pc The playlist container handle
+ * @param pl The playlist handle
+ * @param position Index of the added playlist
+ * @param userdata The opaque pointer
+ */
+static void
+playlist_added(sp_playlistcontainer * pc, sp_playlist * pl, int position,
+ void *userdata)
+{
+ sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
+ dbg(lvl_info, "List name: %s\n", sp_playlist_name(pl));
+
+ if (!strcasecmp(sp_playlist_name(pl), spotify->playlist)) {
+ g_jukeboxlist = pl;
+ current_playlist_index = position;
+ if (autostart)
+ try_jukebox_start();
+ }
+}
+
+/**
+ * Callback from libspotify, telling us the rootlist is fully synchronized
+ * We can resume playback
+ *
+ * @param pc The playlist container handle
+ * @param userdata The opaque pointer
+ */
+static void
+container_loaded(sp_playlistcontainer * pc, void *userdata)
+{
+ dbg(lvl_info, "jukebox: Rootlist synchronized (%d playlists)\n",
+ sp_playlistcontainer_num_playlists(pc));
+ // FIXME : should we implement autostart?
+}
+
+/**
+ * The playlist container callbacks
+ */
+static sp_playlistcontainer_callbacks pc_callbacks = {
+ .playlist_added = &playlist_added,
+// .playlist_removed = &playlist_removed,
+ .container_loaded = &container_loaded,
+};
+
+static void
+on_login(sp_session * session, sp_error error)
+{
+ if (error != SP_ERROR_OK) {
+ dbg(lvl_error, "Error: unable to log in: %s\n",
+ sp_error_message(error));
+ return;
+ } else {
+ dbg(lvl_info, "Logged in!\n");
+ }
+
+ g_logged_in = 1;
+ spotify->pc = sp_session_playlistcontainer(session);
+ sp_playlistcontainer_add_callbacks(spotify->pc, &pc_callbacks,
+ NULL);
+}
+
+static int
+on_music_delivered(sp_session * session, const sp_audioformat * format,
+ const void *frames, int num_frames)
+{
+ audio_fifo_t *af = &g_audiofifo;
+ audio_fifo_data_t *afd;
+ size_t s;
+ if (num_frames == 0)
+ return 0; // Audio discontinuity, do nothing
+ pthread_mutex_lock(&af->mutex);
+ /* Buffer one second of audio */
+ if (af->qlen > format->sample_rate) {
+ pthread_mutex_unlock(&af->mutex);
+ return 0;
+ }
+ s = num_frames * sizeof(int16_t) * format->channels;
+ afd = malloc(sizeof(*afd) + s);
+ memcpy(afd->samples, frames, s);
+ afd->nsamples = num_frames;
+ afd->rate = format->sample_rate;
+ afd->channels = format->channels;
+ TAILQ_INSERT_TAIL(&af->q, afd, link);
+ af->qlen += num_frames;
+ pthread_cond_signal(&af->cond);
+ pthread_mutex_unlock(&af->mutex);
+ dbg(lvl_debug, "Delivery done\n");
+ return num_frames;
+}
+
+static void
+on_end_of_track(sp_session * session)
+{
+ dbg(lvl_debug, "end of track\n");
+ ++g_track_index;
+ try_jukebox_start();
+}
+
+static sp_session_callbacks session_callbacks = {
+ .logged_in = &on_login,
+// .notify_main_thread = &on_main_thread_notified,
+ .music_delivery = &on_music_delivered,
+// .log_message = &on_log,
+ .end_of_track = &on_end_of_track,
+// .offline_status_updated = &offline_status_updated,
+// .play_token_lost = &play_token_lost,
+};
+
+static sp_session_config spconfig = {
+ .api_version = SPOTIFY_API_VERSION,
+ .cache_location = "/var/spotify",
+ .settings_location = "/var/spotify",
+ .application_key = spotify_apikey,
+ .application_key_size = 0, // set in main()
+ .user_agent = "navit",
+ .callbacks = &session_callbacks,
+ NULL
+};
+
+static void
+spotify_spotify_idle(struct audio_priv *spotify)
+{
+ sp_session_process_events(g_sess, &next_timeout);
+}
+
+
+/**
+* @brief this function toggles the repeat mode
+*
+* @param action the action that owns the toggle
+*/
+void
+spotify_toggle_repeat(struct audio_actions *action)
+{
+ int toggle = 0;
+ if (spotify->single)
+ toggle++;
+ if (spotify->repeat)
+ toggle += 2;
+ switch (toggle) {
+ case 0: // no repeat
+ case 1:{
+ spotify->single = 0;
+ spotify->repeat = 1;
+ if (action != NULL) {
+ action->icon =
+ g_strdup("media_repeat_playlist");
+ }
+ dbg(lvl_debug, "\nrepeat playlist\n");
+ break;
+ }
+ case 2:{ // repeat playlist
+ spotify->single = 1;
+ spotify->repeat = 1;
+ if (action != NULL) {
+ action->icon =
+ g_strdup("media_repeat_track");
+ }
+ dbg(lvl_debug, "\nrepeat track\n");
+ break;
+ }
+ case 3:{ // repeat track
+ spotify->single = 0;
+ spotify->repeat = 0;
+ if (action != NULL) {
+ action->icon =
+ g_strdup("media_repeat_off");
+ }
+ dbg(lvl_debug, "\nrepeat off\n");
+ break;
+ }
+ }
+ callback_list_call_attr_0(spotify->cbl, attr_repeat);
+}
+
+/**
+* @brief this function toggles the shuffle mode
+*
+* @param action the action that owns the toggle
+*/
+void
+spotify_toggle_shuffle(struct audio_actions *action)
+{
+
+ int toggle = 0;
+ if (spotify->random_track)
+ toggle++;
+ if (spotify->random_playlist)
+ toggle += 2;
+ dbg(lvl_debug, "Toggle Shuffle: %i\n", toggle);
+ switch (toggle) {
+ case 0:{
+
+ spotify->random_track = TRUE;
+ spotify->random_playlist = FALSE;
+ if (action != NULL) {
+ action->icon =
+ g_strdup("media_shuffle_playlists");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Playlists: %i\n",
+ toggle);
+ break;
+ }
+ case 1:{
+
+ spotify->random_track = TRUE;
+ spotify->random_playlist = TRUE;
+ if (action != NULL) {
+ action->icon =
+ g_strdup
+ ("media_shuffle_tracks_playlists");
+ }
+ dbg(lvl_debug,
+ "Toggle Shuffle Tracks & Playlists: %i\n",
+ toggle);
+ break;
+ }
+ case 3:{
+
+ spotify->random_track = FALSE;
+ spotify->random_playlist = TRUE;
+ if (action != NULL) {
+ action->icon =
+ g_strdup("media_shuffle_tracks");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Tracks: %i\n",
+ toggle);
+ break;
+ }
+ case 2:{
+
+ spotify->random_track = FALSE;
+ spotify->random_playlist = FALSE;
+ if (action != NULL) {
+ action->icon =
+ g_strdup("media_shuffle_off");
+ }
+ dbg(lvl_debug, "Toggle Shuffle OFF: %i\n", toggle);
+ break;
+ }
+ }
+ callback_list_call_attr_0(spotify->cbl, attr_shuffle);
+}
+
+/**
+* @brief command to toggle playback
+*
+* @param action the action that owns the toggle
+*
+* the action must be passed because the playback action keeps the icon. This icon changes dependent on the playback state
+*/
+void
+spotify_toggle_playback(struct audio_actions *action)
+{
+
+ struct attr *playing =
+ attr_search(spotify->attrs, NULL, attr_playing);
+ if (playing) {
+ spotify_get_attr(spotify, attr_playing, playing);
+ playing->u.num = spotify->playing;
+ if (spotify_get_attr(spotify, attr_playing, playing)
+ && playing->u.num)
+ spotify->playing = 1;
+ } else
+ dbg(lvl_debug, "No such Attr in: %p\n", spotify->attrs);
+ if (spotify->playing) {
+ dbg(lvl_debug, "pausing playback\n");
+ // pause your playback here
+ sp_session_player_play(g_sess, 0);
+ if (action != NULL) {
+ action->icon = g_strdup("media_play");
+ }
+ } else {
+ dbg(lvl_debug, "resuming playback\n");
+ // resume your playback here
+ sp_session_player_play(g_sess, 1);
+ try_jukebox_start();
+ if (action != NULL) {
+ action->icon = g_strdup("media_pause");
+ }
+ }
+ spotify->playing = !spotify->playing;
+ if (playing) {
+ playing->u.num = spotify->playing;
+ spotify_set_attr(spotify, playing);
+ }
+ callback_list_call_attr_0(spotify->cbl, attr_playing);
+}
+
+void
+spotify_play()
+{
+ try_jukebox_start();
+}
+
+void
+spotify_pause()
+{
+ try_jukebox_start();
+}
+
+GList *
+playlists(struct audio_priv *this)
+{
+ GList *playlists = NULL;
+ struct audio_playlist *pl;
+ int i;
+ dbg(lvl_error, "Listing playlists via Spotify\n");
+ sp_playlistcontainer *pc = sp_session_playlistcontainer(g_sess);
+ dbg(lvl_info, "get_playlists: Looking at %d playlists\n",
+ sp_playlistcontainer_num_playlists(pc));
+ for (i = 0; i < sp_playlistcontainer_num_playlists(pc); ++i) {
+ sp_playlist *spl = sp_playlistcontainer_playlist(pc, i);
+ // fixme : should we enable this callback?
+ // sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
+ pl = g_new0(struct audio_playlist, 1);
+ pl->name = g_strdup(sp_playlist_name(spl));
+ pl->index = i;
+ pl->status = 0;
+ playlists = g_list_append(playlists, pl);
+
+ switch (sp_playlist_get_offline_status(g_sess, spl)) {
+ case SP_PLAYLIST_OFFLINE_STATUS_NO:
+ pl->icon = "media_playlist_no_offline";
+ break;
+
+ case SP_PLAYLIST_OFFLINE_STATUS_YES:
+ pl->icon = "media_playlist_offline";
+ break;
+
+ case SP_PLAYLIST_OFFLINE_STATUS_DOWNLOADING:
+ pl->icon = "media_playlist_downloading";
+ break;
+
+ case SP_PLAYLIST_OFFLINE_STATUS_WAITING:
+ pl->icon = "media_playlist_pending";
+ break;
+
+ default:
+ pl->icon = "media_playlist_no_offline";
+ break;
+ }
+ }
+ return playlists;
+}
+
+GList *
+tracks(struct audio_priv * this, int playlist_index)
+{
+ GList *tracks = NULL;
+ struct audio_track *t;
+ sp_playlist *spl;
+ int i;
+ if (playlist_index < 0) {
+ playlist_index = current_playlist_index;
+ }
+ dbg(lvl_debug, "Spotify's tracks method\n");
+ sp_playlistcontainer *pc = sp_session_playlistcontainer(g_sess);
+ spl = sp_playlistcontainer_playlist(pc, playlist_index);
+ for (i = 0; i < sp_playlist_num_tracks(spl); i++) {
+ t = g_new0(struct audio_track, 1);
+ sp_track *track = sp_playlist_track(spl, i);
+ t->name = g_strdup(sp_track_name(track));
+ t->index = i;
+ t->status = 0;
+
+ switch (sp_track_offline_get_status(track)) {
+ case SP_TRACK_OFFLINE_DONE:
+ t->icon = "media_track_offline_done";
+ break;
+ case SP_TRACK_OFFLINE_DOWNLOADING:
+ t->icon = "media_track_downloading";
+ break;
+ case SP_TRACK_OFFLINE_NO:
+ t->icon = "media_track_pending";
+ break;
+ default:
+ t->icon = "media_track_offline";
+ }
+ t->icon =
+ g_strdup((i == g_track_index) ? "play" : t->icon);
+
+ tracks = g_list_append(tracks, t);
+ }
+ g_jukeboxlist = spl;
+ dbg(lvl_debug, "Active playlist updated\n");
+ return tracks;
+}
+
+static int
+playback(struct audio_priv *this, const int action)
+{
+ dbg(lvl_debug, "in spotify's playback control\n");
+ switch (action) {
+ case AUDIO_PLAYBACK_TOGGLE:
+ spotify_toggle_playback(get_specific_action
+ (spotify->actions,
+ AUDIO_PLAYBACK_TOGGLE));
+ break;
+ case AUDIO_PLAYBACK_NEXT:
+ ++g_track_index;
+ try_jukebox_start();
+ break;
+ case AUDIO_PLAYBACK_PREVIOUS:
+ if (g_track_index > 0)
+ --g_track_index;
+ try_jukebox_start();
+ break;
+ default:
+ if (action < 0) {
+ dbg(lvl_error,
+ "Don't know what to do with action '%i'. That's a bug\n",
+ action);
+ } else {
+ g_track_index = action;
+ try_jukebox_start();
+ }
+ }
+ return 0;
+}
+
+static struct audio_methods player_spotify_meth = {
+ NULL,
+ playback,
+ action_do,
+ tracks,
+ playlists,
+ actions,
+ current_track,
+ current_playlist,
+ spotify_get_attr,
+ spotify_set_attr,
+};
+
+
+static struct audio_priv *
+player_spotify_new(struct audio_methods *meth, struct callback_list *cbl,
+ struct attr **attrs, struct attr *parent)
+{
+ struct audio_priv *this;
+ struct attr *attr, *playing, *shuffle, *repeat;
+ sp_error error;
+ sp_session *session;
+ attr = attr_search(attrs, NULL, attr_spotify_password);
+ if (spotify_apikey_size == 0) {
+ dbg(lvl_error,
+ "You need to set your spotify api key. Cannot initialize plugin\n");
+ return NULL;
+ }
+ dbg(lvl_debug, "Initializing spotify\n");
+
+
+ spotify = g_new0(struct audio_priv, 1);
+ if ((attr = attr_search(attrs, NULL, attr_spotify_login))) {
+ spotify->login = g_strdup(attr->u.str);
+ dbg(lvl_info, "found spotify_login %s\n", spotify->login);
+ }
+ if ((attr = attr_search(attrs, NULL, attr_spotify_password))) {
+ spotify->password = g_strdup(attr->u.str);
+ dbg(lvl_info, "found spotify_password %s\n",
+ spotify->password);
+ }
+ if ((attr = attr_search(attrs, NULL, attr_spotify_playlist))) {
+ spotify->playlist = g_strdup(attr->u.str);
+ dbg(lvl_info, "found spotify_playlist %s\n",
+ spotify->playlist);
+ }
+ if ((attr = attr_search(attrs, NULL, attr_audio_playback_pcm))) {
+ spotify->audio_playback_pcm = g_strdup(attr->u.str);
+ dbg(lvl_info, "found audio playback pcm %s\n",
+ spotify->audio_playback_pcm);
+ }
+ spconfig.application_key_size = spotify_apikey_size;
+ error = sp_session_create(&spconfig, &session);
+ if (error != SP_ERROR_OK) {
+ dbg(lvl_error, "Can't create spotify session :(\n");
+ return NULL;
+ }
+ dbg(lvl_info, "Session created successfully :)\n");
+ g_sess = session;
+ g_logged_in = 0;
+ sp_session_login(session, spotify->login, spotify->password, 0,
+ NULL);
+ audio_init(&g_audiofifo, spotify->audio_playback_pcm);
+ // FIXME : we should maybe use a timer instead of the idle loop
+
+ spotify->callback =
+ callback_new_1(callback_cast(spotify_spotify_idle), spotify);
+ spotify->timeout = event_add_timeout(1000, 1, spotify->callback);
+
+ spotify->playing = FALSE;
+ spotify->attrs = attrs;
+ playing = attr_search(spotify->attrs, NULL, attr_playing);
+
+ if (!playing) {
+ playing = g_new0(struct attr, 1);
+ playing->type = attr_playing;
+ spotify->attrs =
+ attr_generic_add_attr(spotify->attrs, playing);
+ dbg(lvl_debug, "*\n");
+ }
+ repeat = attr_search(spotify->attrs, NULL, attr_repeat);
+
+ if (!repeat) {
+ repeat = g_new0(struct attr, 1);
+ repeat->type = attr_repeat;
+ spotify->attrs =
+ attr_generic_add_attr(spotify->attrs, repeat);
+ dbg(lvl_debug, "*\n");
+ }
+ shuffle = attr_search(spotify->attrs, NULL, attr_shuffle);
+
+ if (!shuffle) {
+ shuffle = g_new0(struct attr, 1);
+ shuffle->type = attr_shuffle;
+ spotify->attrs =
+ attr_generic_add_attr(spotify->attrs, shuffle);
+ dbg(lvl_debug, "*\n");
+ }
+
+
+ dbg(lvl_info, "Callback created successfully\n");
+ this = g_new(struct audio_priv, 1);
+
+ *meth = player_spotify_meth;
+ return this;
+}
+
+
+void
+plugin_init(void)
+{
+ plugin_register_category_audio("player-spotify",
+ player_spotify_new);
+}
diff --git a/navit/audio/player-spotify/spotify.h b/navit/audio/player-spotify/spotify.h
new file mode 100644
index 000000000..3a400a947
--- /dev/null
+++ b/navit/audio/player-spotify/spotify.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006-2009 Spotify Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * Audio output driver.
+ *
+ * This file is part of the libspotify examples suite.
+ */
+#ifndef _JUKEBOX_AUDIO_H_
+#define _JUKEBOX_AUDIO_H_
+
+#include <pthread.h>
+#include <stdint.h>
+#include "audio/queue.h"
+
+
+/* --- Types --- */
+typedef struct audio_fifo_data {
+ TAILQ_ENTRY(audio_fifo_data) link;
+ int channels;
+ int rate;
+ int nsamples;
+ int16_t samples[0];
+} audio_fifo_data_t;
+
+typedef struct audio_fifo {
+ TAILQ_HEAD(, audio_fifo_data) q;
+ int qlen;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+} audio_fifo_t;
+
+
+/* --- Functions --- */
+extern void audio_init(audio_fifo_t * af, char *audio_playback_pcm);
+extern void audio_fifo_flush(audio_fifo_t * af);
+audio_fifo_data_t *audio_get(audio_fifo_t * af);
+static void try_jukebox_start(void);
+
+#endif /* _JUKEBOX_AUDIO_H_ */
diff --git a/navit/audio/player-stub/CMakeLists.txt b/navit/audio/player-stub/CMakeLists.txt
new file mode 100644
index 000000000..f38fe1315
--- /dev/null
+++ b/navit/audio/player-stub/CMakeLists.txt
@@ -0,0 +1 @@
+module_add_library(audio_player-stub stub.c )
diff --git a/navit/audio/player-stub/stub.c b/navit/audio/player-stub/stub.c
new file mode 100644
index 000000000..34b634888
--- /dev/null
+++ b/navit/audio/player-stub/stub.c
@@ -0,0 +1,1580 @@
+/**
+ * Navit, a modular navigation system.
+ * Copyright (C) 2005-2016 Navit Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+ /*
+ * this file is a working example for an entry point for developing an
+ * own audio plugin.
+ *
+ * you need to enable it inside the navit.xml file with the following:
+ * <audio type="player-stub" /> anywhere between the <navit></navit>
+ * tags. You also might want need an entry point to the gui_internal_media
+ * This could look that way:
+ * <img src='music-blue' onclick='media_show_playlist()'><text>Music</text></img>
+ * put this inside the <gui type="internal" enabled="yes" ...></gui> block
+ */
+#include <glib.h>
+#include "item.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <math.h>
+#include <time.h>
+#include <termios.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+
+#include <attr.h>
+#include <navit/map.h>
+#include <navit/navit.h>
+#include <navit/file.h>
+#include <navit/plugin.h>
+#include <navit/command.h>
+#include <navit/config_.h>
+#include <navit/main.h>
+#include <navit/item.h>
+#include <navit/debug.h>
+#include <navit/callback.h>
+#include <navit/event.h>
+#include <navit/audio.h>
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+#define PLAYLIST 1
+#define TRACK 2
+
+/// Index to the next track
+static int g_track_index;
+
+char str[25]; // this string is just for visualisation of the functions
+char track[64];
+
+struct audio_priv
+{
+ /* this is the data structure for the audio plugin
+ * you might not need every element of it
+ */
+ struct navit *navit;
+ struct callback *callback;
+ struct callback *idle;
+ struct callback_list *cbl;
+ struct event_timeout *timeout;
+ struct attr **attrs;
+ GList* current_playlist;
+ GList* playlists;
+ GList* actions;
+ gchar *musicdir;
+ int num_playlists;
+ int playlist_index;
+ gboolean random_track;
+ gboolean random_playlist;
+ gboolean repeat;
+ gboolean single;
+ gboolean shuffle;
+ char current_track[64];
+ int volume;
+ int width;
+ gboolean muted;
+ int playing;
+} *stub;
+
+
+GList* sort_playlists(GList* list);
+
+char * stub_get_playlist_name (int playlist_index);
+char* get_playlist_name(GList* list);
+void stub_play(void);
+void stub_pause(void);
+void stub_play_track(int track);
+GList* get_entry_by_index(GList* list, int index);
+void stub_toggle_repeat(struct audio_actions *action);
+void stub_toggle_shuffle(struct audio_actions *action);
+struct audio_actions* get_specific_action(GList* actions, int specific_action);
+
+/**
+ * Get function for attributes
+ *
+ * @param priv Pointer to the audio instance data
+ * @param type The attribute type to look for
+ * @param attr Pointer to a {@code struct attr} to store the attribute
+ * @return True for success, false for failure
+ */
+int stub_get_attr(struct audio_priv* priv, enum attr_type type, struct attr *attr){
+ int ret = 1;
+ dbg(lvl_debug, "priv: %p, type: %i (%s), attr: %p\n", priv, type,attr_to_name(type), attr);
+ if(priv != stub) {
+ dbg(lvl_debug, "failed\n");
+ return -1;
+ }
+ switch(type){
+ case attr_playing:{
+ attr->u.num = stub->playing;
+ dbg(lvl_debug, "Status: %ld\n", attr->u.num);
+ break;
+ }
+ case attr_name:{
+ attr->u.str = g_strdup("STUB");
+ dbg(lvl_debug, "%s\n", attr->u.str);
+ break;
+ }
+ case attr_shuffle:{
+ int toggle = 0;
+ if(stub->random_track) toggle++;
+ if(stub->random_playlist) toggle += 2;
+ attr->u.num = toggle;
+ dbg(lvl_debug, "%ld\n", attr->u.num);
+ break;
+ }
+ case attr_repeat:{
+ int toggle = 0;
+ if(stub->single) toggle++;
+ if(stub->repeat) toggle += 2;
+ attr->u.num = toggle;
+ dbg(lvl_debug, "%ld\n", attr->u.num);
+ break;
+ }
+ default:{
+ dbg(lvl_error, "Don't know what to do with ATTR type %s\n", attr_to_name(attr->type));
+ ret = 0;
+ break;
+ }
+ }
+ attr->type = type;
+ return ret;
+}
+/**
+ * Set function for attributes
+ *
+ * @param priv An audio instance data
+ * @param attr The attribute to set
+ * @return False on success, true on failure
+ */
+int stub_set_attr(struct audio_priv *priv, struct attr *attr){
+ dbg(lvl_debug, "priv: %p, type: %i (%s), attr: %p\n", priv, attr->type,attr_to_name(attr->type), attr);
+ if(priv != stub){
+ dbg(lvl_debug, "failed\n");
+ return -1;
+ }
+ if(attr){
+ switch(attr->type){
+ case attr_name:{
+ dbg(lvl_debug, "%s\n", attr->u.str);
+ break;
+ }
+ case attr_playing:{
+ dbg(lvl_debug, "attr->u.num: %ld\n", attr->u.num);
+
+ if(attr->u.num == 0){
+ dbg(lvl_debug, "stub_pause();%ld\n", attr->u.num);
+ stub_pause();
+ }else{
+ dbg(lvl_debug, "stub_play();%ld\n", attr->u.num);
+ stub_play();
+ }
+
+ break;
+ }
+ case attr_shuffle:{
+
+ stub_toggle_shuffle(get_specific_action(stub->actions, AUDIO_MODE_TOGGLE_SHUFFLE));
+ break;
+ }
+ case attr_repeat:{
+
+ stub_toggle_repeat(get_specific_action(stub->actions, AUDIO_MODE_TOGGLE_REPEAT));
+ break;
+ }
+ default:{
+ dbg(lvl_error, "Don't know what to do with ATTR type %s", attr_to_name(attr->type));
+ return 0;
+ break;
+ }
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+/**
+* @brief this function reads a data of a certain playlist
+*
+* @param the playlist object
+* @return the playlist data structure or NULL on error
+*/
+struct audio_playlist*
+get_playlist_data(GList* list)
+{
+ if(list != NULL)
+ {
+ if(list->data!=NULL)
+ {
+ return (struct audio_playlist*) list->data;
+ }
+ }
+ dbg(lvl_error, "WAH! Data is corrupted\n");
+ return NULL;
+}
+
+/**
+* @brief this function reindexes a list of playlists
+*
+* @param the playlist object to start indexing
+* @return the number of reindexed playlists.
+*
+* this function reindexes a list of playlists to reorder them.
+* It's intended to be used after a sorting algorithm on the head of a list of playlists.
+*/
+int
+reindex_playlists(GList *list)
+{
+ GList *current = list;
+ int i = 0;
+ get_playlist_data(list)->index = i;
+ dbg(lvl_debug, "playlist:%s \n\n", get_playlist_name(list));
+
+ while (NULL != (current = current->next))
+ {
+ dbg(lvl_debug, "playlist:%s \n", get_playlist_name(current));
+ get_playlist_data(current)->index = ++i;
+ }
+ dbg(lvl_debug, "%i Playlists indexed\n",i);
+ return i;
+}
+
+/**
+* @brief this function creates a new playlist data structure
+*
+* @param the playlist name
+* @param the desired playlist index
+* @return the playlist data object.
+*/
+struct audio_playlist* new_audio_playlist(char *name, int index)
+{
+ struct audio_playlist *pl;
+ pl = g_new0(struct audio_playlist, 1);
+ pl->name=g_strdup(name);
+ pl->index = index;
+ pl->status=0;
+ pl->icon = "playlist";
+ dbg(lvl_debug, "Created a playlist with name %s and index %i\n", name, index);
+ return pl;
+}
+
+/**
+* @brief this function appends a playlist data structure to the list of playlists
+*
+* @param list the playlist to apped the data
+* @param playlist the playlist data to append
+* @return the playlist.
+*/
+GList*
+insert_right(GList* list, struct audio_playlist* playlist)
+{
+ dbg(lvl_debug, "Appending playlist %s\n", playlist->name);
+ return g_list_append(list, (gpointer) playlist);
+}
+
+/**
+ * @brief Print all nodes of a list of playlists
+ * @param list the list of playlists
+ *
+ */
+void
+print_all(GList* list)
+{
+ int i = 0;
+ if(list==NULL) return;
+ GList* current = list;
+ while (NULL != current->next)
+ {
+ if(get_playlist_data(current)!=NULL)
+ {
+ printf("List element %i, %s. Index %i\n", i++, get_playlist_name(current), get_playlist_data(current)->index );
+ }else{
+ dbg(lvl_error, "%i: This appears to be an empty list. That's probably a Bug!\n",i);
+ }
+ current = current->next;
+ }
+}
+
+/**
+* @brief choose a playlist randomly
+*
+* @param list the entire list of playlists
+*
+* @return the randomly chosen list element
+*/
+GList*
+random_entry(GList* list){
+ int n = 9;
+ int i = random();
+ i %= n;
+ dbg(lvl_debug, "\tTest a random number: \t%i\n", i );
+ return get_entry_by_index(g_list_first(list), i);
+}
+
+/**
+* @brief switch to the next playlist
+*
+* @param list the entire list of playlists
+*
+* @return the next list element
+*/
+GList*
+next_playlist(GList* list)
+{
+ if(stub->random_playlist){
+ return random_entry(list);
+ }
+ return (list->next!=NULL)?g_list_next(list):g_list_first(list);
+}
+
+/**
+* @brief switch to the previous playlist
+*
+* @param list the entire list of playlists
+*
+* @return the previous list element
+*/
+GList*
+prev_playlist(GList* list)
+{
+ if(stub->random_playlist){
+ return random_entry(list);
+ }
+ return (list->prev!=NULL)?g_list_previous(list):g_list_last(list);
+}
+/**
+* @brief Get the name of the playlist
+*
+* @param list the playlist object
+*
+* @return the name of the playlist
+*/
+char*
+get_playlist_name(GList* list)
+{
+ if(list != NULL)
+ {
+ struct audio_playlist *pl = list->data;
+ if(pl != NULL)
+ {
+ if(pl->name != NULL)
+ {
+ return pl->name;
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+* @brief Delete a playlist from the list of playlists
+*
+* @param list the playlist object to be deleted
+*
+* @return the list of playlists without the deleted element
+*/
+GList*
+delete_and_free(GList *list)
+{
+ return g_list_delete_link(g_list_first(list),list);
+}
+
+/**
+* @brief Delete a playlist from the list of playlists and from mpc's database
+*
+* @param list the playlist object to be deleted
+*
+* @return the list of playlists without the deleted element
+*/
+GList*
+delete_playlist(GList* list)
+{
+ // remove the playlist from your api or lib here
+ // and finally it removes the playlist from the list
+ return g_list_delete_link(g_list_first(list),list);
+}
+
+/**
+* @brief set the index of a playlist
+*
+* @param entry the playlist element
+* @param entry_index the desired index
+*/
+void set_playlist_index(GList* entry, int entry_index)
+{
+ struct audio_playlist *pl;
+ if(entry)
+ {
+ pl = (struct audio_playlist*) entry->data;
+ if(pl)
+ {
+ pl->index = entry_index;
+ }
+ }
+}
+
+
+/**
+* @brief swap two playlists
+*
+* @param a playlist a to swap
+* @param b playlist b to swap
+*
+* this function is used to bubblesort the playlists
+*/
+void
+swap_playlists(GList* a, GList* b)
+{
+ char* temp = NULL;
+ temp = a->data;
+ a->data = b->data;
+ b->data = temp;
+}
+
+/**
+* @brief this function sorts a list of playlists alphabetically
+*
+* @param list the entire list of playlists
+*
+* @return the sorted list of playlists
+*/
+GList*
+sort_playlists(GList* list)
+{
+ // bubblesort for playlists
+ if(list == NULL) return NULL;
+ GList* i;
+ GList* j;
+ int index = 0;
+ for(i=list; i->next!=NULL; i=i->next)
+ {
+ for(j=list; j->next!=NULL; j=j->next)
+ {
+ if(strcmp(get_playlist_data(j)->name, get_playlist_data(j->next)->name) > 0)
+ {
+ swap_playlists(j, j->next);
+ }
+ }
+ }
+ for(i=g_list_first(list); i->next!=NULL; i=i->next)
+ {
+ set_playlist_index(i, index++);
+ }
+ print_all(g_list_first(list));
+ return list;
+}
+
+/**
+ * @brief Save a playlist
+ * @param list the list element to be saved
+ *
+ * Save a playlist in a proer way to be able to load it on next startup
+ */
+void
+save_playlist(GList* list){
+ /*
+ * save the current playlist to a file
+ */
+ }
+
+/**
+* @brief this function loads a playlist to mpd/mpd
+*
+* @param list the playlist to be loaded
+*/
+void
+load_playlist(GList * list){
+ if(list){
+ // load your playlist into the player here
+ save_playlist(list);
+ }
+}
+
+/**
+* @brief this function choses the next playlist from the list of playlists
+*
+* @param current the currently loaded playlist element
+*
+* @return the playlist element that was loaded
+*/
+GList*
+load_next_playlist(GList* current)
+{
+ GList* next = next_playlist(current);
+ load_playlist(next);
+ stub->playlist_index = g_list_index(stub->playlists, next->data);
+ stub_play();
+ return next;
+}
+
+/**
+* @brief seeks and returns the playlist with the given data
+*
+* @param head the list of playlist to search on
+* @param data the data, which contains the playlist name to search
+*
+* @return the searched element or NULL if it wasnt found
+*/
+GList*
+get_entry(GList* head, char *data)
+{
+ /* this function searches the list for an entry with name data
+ *
+ */
+ GList* current = head;
+ while(strcmp(get_playlist_data(current)->name, data) != 0)
+ {
+ current = current->next;
+ if(current == head)
+ {
+ return NULL; //nothing found!
+ }
+ }
+ return current; //found!
+}
+
+/**
+* @brief this function returns the nth element of the list of playlists
+*
+* @param list the list of playlists
+* @param index the index to get
+*
+* @return the nth playlist
+*/
+GList*
+get_entry_by_index(GList* list, int index)
+{
+ GList* entry = g_list_nth(list, index);
+ return entry;
+}
+
+/**
+* @brief this function choses the previous playlist from the list of playlists
+*
+* @param current the currently loaded playlist element
+*
+* @return the playlist element that was loaded
+*/
+GList*
+load_prev_playlist(GList* current)
+{
+ GList* previous = prev_playlist(current);
+ load_playlist(previous);
+ stub->playlist_index = g_list_index(stub->playlists, previous->data);
+ stub_play();
+ return previous;
+}
+
+/**
+* @brief this function gets the next playlist where the artist differs
+*
+* @param current playlist entry
+* @param next specifies if we go forwards or backwards
+*
+* @return the playlist item which differs in its artist name
+*
+* For proper us of this function the playlists must be named in a special way:
+* "Artist - Album" The algorithm iterates over the list of playlists forwards
+* or backwards (depending on next is set or not) and stops and return
+* the first item which differs in the part until the dash char
+*/
+GList*
+change_artist(GList* current, int next)
+{
+ /** this function iterates the playlists and checks the name
+ * it expects an "artist - album" named playlist
+ * if loads the first playlist with a differing "artist" substring
+ * the minus in the string is important
+ */
+ char ca[32] = {0,};
+ char na[32] = {0,};
+ int i,j;
+ for(i=0; i<32; i++)
+ {
+ if(get_playlist_data(current)->name[i] == '-') /// <= note the minus separator
+ {
+ ca[i] = 0x00;
+ break;
+ }
+ else
+ {
+ ca[i] = get_playlist_data(current)->name[i];
+ }
+ }
+ do {
+ if(next)
+ {
+ current = next_playlist(current);
+ }else{
+ current = prev_playlist(current);
+ }
+ for(j=0; j<32; j++)
+ {
+ if(get_playlist_data(current)->name[j] == '-') /// <= note the minus separator
+ {
+ na[j] = 0x00;
+ break;
+ }
+ else
+ {
+ na[j] = get_playlist_data(current)->name[j];
+ }
+ }
+ dbg(lvl_debug, "%s ca:%s na:%s strcmp: %i\n", get_playlist_name(current), ca, na, strncmp(ca, na, min(i,j)));
+ } while (strncmp(ca, na, min(i,j))==0);
+ load_playlist(current);
+ stub_play();
+ return current;
+}
+
+/**
+ * @brief wrapper for change next artist
+ * @param current the currently chosen playlist
+ * @returns the next chosen playlist
+ *
+ *
+ */
+GList*
+next_artist(GList* current)
+{
+ return change_artist(current, 1);
+}
+
+/**
+ * @brief wrapper for change previous artist
+ * @param current the currently chosen playlist
+ * @returns the previous chosen playlist
+ *
+ *
+ */
+GList*
+prev_artist(GList* current)
+{
+ return change_artist(current, 0);
+}
+
+/**
+* @brief this function reads the track name to print it on the gui
+*
+* @param track_index the index of the track
+*
+* @return the track name
+*/
+char *
+stub_get_track_name (int track_index)
+{
+ /** this function should return the
+ * track "track_index" of the current loaded playlist
+ */
+ sprintf(str, "track %i",track_index);
+ return str;
+}
+
+/**
+* @brief this function gets and returns the current playing playlist
+*
+* @return the playlist name
+*
+* this function is used for OSD and GUI
+*/
+char *
+stub_get_current_playlist_name ()
+{
+ /* return the name of the currently loaded playlist */
+
+ return "currently loaded playlist";
+}
+
+/**
+* @brief this function reads the playlist name to print it on the gui
+*
+* @param playlist_index the index of the playlist
+*
+* @return the playlist name
+*/
+char *
+stub_get_playlist_name (int playlist_index)
+{
+
+ /* this function should return the
+ * playlist name "playlist_index"
+ * of the playlist list
+ */
+ sprintf(str, "playlist %i",playlist_index);
+ return str;
+}
+
+/**
+* @brief this function starts playback for the track from the current playlist
+*
+* @param track_index the number of the track
+*/
+void
+stub_set_current_track (int track_index)
+{
+ stub_play_track(track_index + 1);
+ g_track_index = track_index;
+}
+
+/**
+* @brief this funtion reads the number of tracks for the current playlist
+*
+* @return the number of tracks
+*/
+int
+stub_get_current_playlist_items_count ()
+{
+ /* get and return the number of items in the currently
+ * loaded playlist
+ */
+ return 7;
+}
+
+/**
+* @brief this function counts all playlists and returns the number
+*
+* @return the number of playlists
+*/
+int
+stub_get_playlists_count ()
+{
+ /* get and return the number of items in the currently
+ * loaded playlist
+ */
+ return 9;
+}
+
+/**
+* @brief this function starts playback for the given playlist
+*
+* @param playlist_index the given playlist to play
+*/
+void
+stub_set_active_playlist (int playlist_index)
+{
+ stub->playlist_index = playlist_index;
+ stub->current_playlist = get_entry_by_index(stub->playlists, playlist_index);
+ if(stub->current_playlist == NULL){
+ stub->playlist_index = -1;
+ return;
+ }
+ load_playlist(stub->current_playlist);
+ stub_play();
+}
+
+
+/**
+ * @brief this function creates all possible actions for the player
+ *
+ * @return the list of actions
+ *
+ * Function to provide all possible audio actions for the player. These
+ * actions are acessible inside the media gui as a toolbar, so they
+ * might provide an icon. Their order will be applied to the toolbar too.
+ * It is possible to change the icon depending on the actions state
+ */
+GList*
+stub_get_actions(void){
+ /**
+ * this function creates all actions your player is able to perform
+ * except volume and choosing of a specific track
+ * just remove the actions you do not need
+ * all listed actions will be put into a player toolbar with its icon
+ * so you should define one for each action
+ */
+ GList* actions = NULL;
+ struct audio_actions *aa;
+ if(stub->actions){
+ return stub->actions;
+ }else{
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_ARTIST;
+ aa->icon = g_strdup("media_prev_artist");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_PLAYLIST;
+ aa->icon = g_strdup("media_prev");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_PREVIOUS_TRACK;
+ aa->icon = g_strdup("media_minus");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_TOGGLE;
+ if(stub->playing){
+ aa->icon = g_strdup("media_pause");
+ }else{
+ aa->icon = g_strdup("media_play");
+ }
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_TRACK;
+ aa->icon = g_strdup("media_plus");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_PLAYLIST;
+ aa->icon = g_strdup("media_next");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_PLAYBACK_NEXT_ARTIST;
+ aa->icon = g_strdup("media_next_artist");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MODE_TOGGLE_REPEAT;
+ aa->icon = g_strdup("media_repeat_off");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MODE_TOGGLE_SHUFFLE;
+ aa->icon = g_strdup("media_shuffle_off");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MISC_DELETE_PLAYLIST;
+ aa->icon = g_strdup("media_trash");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+
+ aa = g_new0(struct audio_actions, 1);
+ aa->action = AUDIO_MISC_RELOAD_PLAYLISTS;
+ aa->icon = g_strdup("media_close");//todo: make beautiful icon
+ actions = g_list_append(actions, aa);
+ }
+ return actions;
+}
+
+
+
+/**
+* @brief the loop function for the audio player
+*
+* @param the audio player object
+*
+* this function is the loop core for this plugin. Here is the playlist
+* change and the currend trackname processes for continous playback and
+* nice osd view. If the last song of a playlist ends, the mpd process
+* pauses playback automatically. For continous playback we have to
+* change to the next playlist.
+*
+* We also read some statuses and save them to the audio player object
+*/
+static void
+stub_stub_idle (struct audio_priv *stub)
+{
+ /**
+ * periodic called function
+ * here you might get the current track and other information from
+ * your player api or lib
+ */
+ dbg(lvl_debug, "In Stubs Idle Loop\n");
+
+ /* List all attached attrs.
+ struct attr** attrs = stub->attrs;
+ if (attrs) {
+ for (;*attrs; attrs++){
+ struct attr* a = *attrs;
+ dbg(lvl_debug, "Attr (%s) at %p\n", attr_to_name(a->type), *attrs);
+ }
+ }
+ //*/
+}
+
+/**
+* @brief pause :)
+*/
+void stub_pause(void)
+{
+ stub->playing = 0;
+ struct attr* playing = attr_search(stub->attrs,NULL, attr_playing);
+ if(playing)
+ playing->u.num = stub->playing;
+ else
+ dbg(lvl_debug, "No such Attr in %p\n", stub->attrs);
+ dbg(lvl_debug,"\n");
+ callback_list_call_attr_0(stub->cbl, attr_playing);
+ // pause your playback
+}
+/**
+* @brief play :)
+*/
+void stub_play(void)
+{
+ stub->playing = 1;
+ struct attr* playing = attr_search(stub->attrs,NULL, attr_playing);
+ if(playing)
+ playing->u.num = stub->playing;
+ else
+ dbg(lvl_debug, "No such Attr in %p\n", stub->attrs);
+ dbg(lvl_debug, "\n");
+ callback_list_call_attr_0(stub->cbl, attr_playing);
+ // start your playback
+}
+
+/**
+* @brief play a specific track
+*
+* @param track the number of the track to play
+*/
+void stub_play_track(int track)
+{
+ stub->playing = 1;
+ struct attr* playing = attr_search(stub->attrs,NULL, attr_playing);
+ if(playing)
+ playing->u.num = stub->playing;
+ else
+ dbg(lvl_debug, "No such Attr in %p\n", stub->attrs);
+ dbg(lvl_debug, "\n");
+ callback_list_call_attr_0(stub->cbl, attr_playing);
+ // choose and play track track here
+}
+
+/**
+* @brief load the next playlist
+*/
+void
+stub_next_playlist(void)
+{
+ dbg(lvl_debug, "\n");
+ stub->current_playlist = load_next_playlist(stub->current_playlist);
+}
+
+/**
+* @brief load the previous playlist
+*/
+void
+stub_prev_playlist(void)
+{
+ dbg(lvl_debug, "\n");
+ stub->current_playlist = load_prev_playlist(stub->current_playlist);
+}
+
+/**
+* @brief getter for the volume value of the audio player object
+*
+* @return the volume
+*/
+int stub_get_volume(void)
+{
+ return stub->volume;
+}
+
+/**
+* @brief sets the volume value
+*
+* @param vol the volume value to set
+*/
+void stub_set_volume(int vol)
+{
+ // set volume to vol here
+ stub->volume = vol;
+}
+
+/**
+* @brief this function toggles the repeat mode
+*
+* @param action the action that owns the toggle
+*/
+void stub_toggle_repeat(struct audio_actions *action){
+ int toggle = 0;
+ if(stub->single) toggle++;
+ if(stub->repeat) toggle += 2;
+ switch(toggle){
+ case 0:// no repeat
+ case 1:{
+ stub->single = 0;
+ stub->repeat = 1;
+ if(action != NULL){
+ action->icon = g_strdup("media_repeat_playlist");
+ }
+ dbg(lvl_debug, "\nrepeat playlist\n");
+ break;
+ }
+ case 2:{// repeat playlist
+ stub->single = 1;
+ stub->repeat = 1;
+ if(action != NULL){
+ action->icon = g_strdup("media_repeat_track");
+ }
+ dbg(lvl_debug, "\nrepeat track\n");
+ break;
+ }
+ case 3:{// repeat track
+ stub->single = 0;
+ stub->repeat = 0;
+ if(action != NULL){
+ action->icon = g_strdup("media_repeat_off");
+ }
+ dbg(lvl_debug, "\nrepeat off\n");
+ break;
+ }
+ }
+ callback_list_call_attr_0(stub->cbl, attr_repeat);
+}
+/**
+* @brief this function toggles the shuffle mode
+*
+* @param action the action that owns the toggle
+*/
+void stub_toggle_shuffle(struct audio_actions *action){
+
+ int toggle = 0;
+ if(stub->random_track) toggle++;
+ if(stub->random_playlist) toggle += 2;
+ dbg(lvl_debug, "Toggle Shuffle: %i\n", toggle);
+ switch(toggle){
+ case 0:{
+
+ stub->random_track = TRUE;
+ stub->random_playlist = FALSE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_playlists");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Playlists: %i\n", toggle);
+ break;
+ }
+ case 1:{
+
+ stub->random_track = TRUE;
+ stub->random_playlist = TRUE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_tracks_playlists");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Tracks & Playlists: %i\n", toggle);
+ break;
+ }
+ case 3:{
+
+ stub->random_track = FALSE;
+ stub->random_playlist = TRUE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_tracks");
+ }
+ dbg(lvl_debug, "Toggle Shuffle Tracks: %i\n", toggle);
+ break;
+ }
+ case 2:{
+
+ stub->random_track = FALSE;
+ stub->random_playlist = FALSE;
+ if(action != NULL){
+ action->icon = g_strdup("media_shuffle_off");
+ }
+ dbg(lvl_debug, "Toggle Shuffle OFF: %i\n", toggle);
+ break;
+ }
+ }
+ callback_list_call_attr_0(stub->cbl, attr_shuffle);
+}
+
+/**
+* @brief command to toggle playback
+*
+* @param action the action that owns the toggle
+*
+* the action must be passed because the playback action keeps the icon. This icon changes dependent on the playback state
+*/
+void
+stub_toggle_playback (struct audio_actions *action)
+{
+
+ struct attr* playing = attr_search(stub->attrs,NULL, attr_playing);
+ if(playing){
+ stub_get_attr(stub, attr_playing, playing);
+ playing->u.num = stub->playing;
+ if(stub_get_attr(stub, attr_playing, playing) && playing->u.num)
+ stub->playing = 1;
+ }
+ else
+ dbg (lvl_debug, "No such Attr in: %p\n", stub->attrs);
+ if (stub->playing)
+ {
+ dbg (lvl_debug, "pausing playback\n");
+ // pause your playback here
+ if(action != NULL){
+ action->icon = g_strdup("media_play");
+ }
+ }
+ else
+ {
+ dbg (lvl_debug, "resuming playback\n");
+ // resume your playback here
+ if(action != NULL){
+ action->icon = g_strdup("media_pause");
+ }
+ }
+ stub->playing = !stub->playing;
+ if(playing){
+ playing->u.num = stub->playing;
+ stub_set_attr(stub, playing);
+ }
+ callback_list_call_attr_0(stub->cbl, attr_playing);
+}
+
+/**
+* @brief this function returns a list of playlists
+*
+* @param this the audio player object
+*
+* @return the list of playlists
+*
+* if no playlists are found, this function iniyializes the playists for this player
+*/
+GList *
+playlists(struct audio_priv *this)
+{
+ dbg(lvl_debug, "stub's playlists method\n");
+ GList * playlists=NULL;
+ playlists = stub->playlists;
+ if(playlists==NULL)
+ {
+ dbg(lvl_error,"No Playlists found. Try again.\n");
+ /*
+ * add function to create a playlists-list from the data of your api or lib
+ */
+
+ /* this block is potentially rubbish you should provide a
+ * function which cares about your playlists
+ */
+ int i;
+ for(i=0;i<9;i++){
+ struct audio_playlist *apl = g_new0(struct audio_playlist, 1);
+ apl->name=g_strdup(stub_get_playlist_name(i));
+ apl->index = i;
+ apl->icon = "media_hierarchy";
+ playlists=g_list_append(playlists, apl);
+ stub->playlists = playlists;
+ }
+ return stub->playlists;
+ }
+ return playlists;
+}
+
+/**
+* @brief this function returns a list of tracks for a specific playlist
+*
+* @param this the audio player object
+* @param playlist_index the index of the playlist to get the tracks from
+*
+* @return the list of tracks
+*/
+GList *
+tracks(struct audio_priv *this, int playlist_index)
+{
+ GList * tracks=NULL;
+ struct audio_track *t;
+ int i, track_count;
+ if ( playlist_index < 0 ) {
+ playlist_index = stub->playlist_index;
+ }
+
+ if(playlist_index != stub->playlist_index)
+ {
+ stub_set_active_playlist(playlist_index);
+ }
+ track_count = stub_get_current_playlist_items_count();
+ dbg(lvl_debug, "playlist_index: %i\n", playlist_index);
+ for (i = 0; i < track_count; i++) {
+ t = g_new0(struct audio_track, 1);
+ t->name=g_strdup(stub_get_track_name(i));
+ t->index=i;
+ t->status=0;
+ t->icon = "media_audio";
+ tracks=g_list_append(tracks, t);
+ }
+ dbg(lvl_debug, "Active playlist updated\n");
+ return tracks;
+}
+
+/**
+* @brief this function returns the list of possible actions
+*
+* @param this the audio player object
+*
+* @return the list of actions
+*
+* if there are no actions present, the command inits the action list
+*/
+GList*
+actions(struct audio_priv *this){
+ dbg(lvl_debug, "In stub's actions\n");
+ GList *act = NULL;
+ act = stub->actions;
+ if(act){
+ return act;
+ }else{
+ stub->actions = stub_get_actions();
+ }
+ return stub->actions;
+}
+
+/**
+* @brief this function iterates over all possible actions for this player and searches for an action
+*
+* @param actions the list of actions
+* @param action the action we want to find
+*
+* @return the audio action object wh searched or NULL if its not present
+*/
+struct audio_actions*
+get_specific_action(GList* actions, int specific_action)
+{
+ GList* result = g_list_first(actions);
+ while(result != NULL && result->next != NULL){
+ struct audio_actions *aa = result->data;
+ if(aa->action == specific_action)
+ return aa;
+ result = g_list_next(result);
+ }
+ return NULL;
+}
+
+/**
+* @brief this function provides the action control for the audio player
+*
+* @param this the audio player object
+* @param action the action to be performed on the player
+*
+* @return returns the action
+*
+* possible actions:
+* AUDIO_PLAYBACK_PLAY
+* AUDIO_PLAYBACK_PAUSE
+* AUDIO_PLAYBACK_TOGGLE
+* AUDIO_PLAYBACK_NEXT_TRACK
+* AUDIO_PLAYBACK_PREVIOUS_TRACK
+* AUDIO_PLAYBACK_NEXT_PLAYLIST
+* AUDIO_PLAYBACK_PREVIOUS_PLAYLIST
+* AUDIO_PLAYBACK_NEXT_ARTIST: switches to the next playlist that differs before the Artist - Track delimiter " - "
+* AUDIO_PLAYBACK_PREVIOUS_ARTIST: switches to the next playlist that differs before the Artist - Track delimiter " - " but backwards
+* AUDIO_MISC_DELETE_PLAYLIST
+* AUDIO_MODE_TOGGLE_REPEAT: switches through the repeat modes
+* AUDIO_MODE_TOGGLE_SHUFFLE: switches through the shuffle modes
+* AUDIO_MISC_RELOAD_PLAYLISTS: reload all playlists (delete and reload)
+*/
+static int
+action_do(struct audio_priv *this, const int action)
+{
+ dbg(lvl_debug, "In stub's action control\n");
+ /** methosd where the defined actions are mentioned
+ * remove the case blocks for actions you do not need
+ */
+ switch(action)
+ {
+ case AUDIO_PLAYBACK_PAUSE:{
+ stub_pause();
+ break;
+ }
+ case AUDIO_PLAYBACK_PLAY:{
+ stub_play();
+ break;
+ }
+ case AUDIO_PLAYBACK_TOGGLE:{
+ stub_toggle_playback(get_specific_action(stub->actions, AUDIO_PLAYBACK_TOGGLE));
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_TRACK:{
+ ++g_track_index;
+ // call next track here
+
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_TRACK:{
+ if (g_track_index > 0)
+ {
+ --g_track_index;
+ // call previous track here
+ }
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_PLAYLIST:{
+ //stub_next_playlist();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_PLAYLIST:{
+ //stub_prev_playlist();
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_ARTIST:{
+ //stub_next_artist();
+ break;
+ }
+ case AUDIO_PLAYBACK_PREVIOUS_ARTIST:{
+ //stub_prev_artist();
+ break;
+ }
+ case AUDIO_MISC_DELETE_PLAYLIST:{
+ //stub_delete_playlist();
+ break;
+ }
+ case AUDIO_MODE_TOGGLE_REPEAT:{
+ /* if your player has different repeat modes
+ * you can have different icons for each mode
+ */
+ stub_toggle_repeat(get_specific_action(stub->actions, AUDIO_MODE_TOGGLE_REPEAT));
+ break;
+ }
+ case AUDIO_MODE_TOGGLE_SHUFFLE:{
+ /* if your player has different shuffle modes
+ * you can have different icons for each mode
+ */
+ stub_toggle_shuffle(get_specific_action(stub->actions, AUDIO_MODE_TOGGLE_SHUFFLE));
+ break;
+ }
+ case AUDIO_MISC_RELOAD_PLAYLISTS:{
+ /* maybe you'll never need this
+ */
+ //reload_playlists(stub);
+ break;
+ }
+ default:{
+ dbg(lvl_error,"Don't know what to do with action '%i'. That's a bug\n", action);
+ break;
+ }
+ }
+ return action;
+}
+
+/**
+* @brief this function provides the playback control for the audio player
+*
+* @param this the audio player object
+* @param action the index of the song to play
+*
+* @return the playback status
+*
+* this function is accessed, when one clicks on a specific song in gui internal
+*/
+static int
+playback(struct audio_priv *this, const int action)
+{
+ dbg(lvl_debug, "In stub's playback control %i\n", action);
+ /* here are the players playback functions
+ * it's called when a track is clicked to play that track
+ *
+ */
+ if ( action > -1 ) {
+ g_track_index = action;
+ stub_play();
+ // call play_track_function here
+ } else
+ dbg(lvl_error,"Don't know what to do with play track '%i'. That's a bug\n", action);
+ if(stub->playing) return 1;
+ return 0;
+}
+
+/**
+* @brief this function provides the volume control for the audio player
+*
+* @param this the audio player object
+* @param action the kind of action to perform
+*
+* @return the resulting volume
+*
+* possible values are:
+* AUDIO_MUTE: save the current volume and set the mpd volume to 0
+* AUDIO_LOWER_VOLUME: decrease the volume by 1
+* AUDIO_RAISE_VOLUME: increase the volume by 1
+* any positive integer between 1 and 100: Set the value as volume
+*/
+static int
+volume(struct audio_priv *this, const int action)
+{
+ switch(action)
+ {
+ case AUDIO_MUTE:
+ if(stub->muted){
+ stub_set_volume(stub->volume);
+ }else{
+ //mute volume here
+ }
+ stub->muted = !stub->muted;
+ break;
+ case AUDIO_LOWER_VOLUME:
+ // reduce volume here
+ if(stub->volume >= 0){
+ stub->volume--;
+ }else{
+ stub->volume = 0;
+ }
+ break;
+ case AUDIO_RAISE_VOLUME:
+ //raise volume here
+ if(stub->volume <= 100){
+ stub->volume++;
+ }else{
+ stub->volume = 100;
+ }
+ break;
+ default:
+ if(action >0){
+ // set volume to i here
+ stub->volume = action;
+ } else {
+ dbg(lvl_error,"Don't know what to do with action '%i'. That's a bug\n", action);
+ }
+ break;
+ }
+
+ return stub->volume;
+}
+
+/**
+* @brief this function returns the currently playing trsck
+*
+* @param this the audio player object
+*
+* @return the track name of the current track
+*/
+char*
+current_track(struct audio_priv *this)
+{
+ return "current track";
+ //return stub_get_current_track_name();
+}
+
+/**
+* @brief this function returns the currently loaded playlist
+*
+* @param this the audio player object
+*
+* @return the playlist name
+*/
+char*
+current_playlist(struct audio_priv *this)
+{
+ return "current playlist";
+ //return stub_get_current_playlist_name();
+}
+
+/**
+* @brief the audio methods
+*
+* these methods are acessible from anywhere in the program.
+* actions you do not need, should be NULL.
+*/
+static struct audio_methods player_stub_meth = {
+ volume,
+ playback,
+ action_do,
+ tracks,
+ playlists,
+ actions,
+ current_track,
+ current_playlist,
+ stub_get_attr,
+ stub_set_attr,
+};
+
+/**
+* @brief Inizialisation of the audio plugin instance data
+*
+* @param meth The methods to be overidden by the plugins methods
+* @param cbl A list of callbacks which are called from the plugin
+* @param attrs A list of attributes which are generated from navit.xml
+* @param parent reference to the parant attribute
+*
+* @return The audio plugin instance data
+*
+*/
+static struct audio_priv *
+player_stub_new(struct audio_methods *meth, struct callback_list * cbl, struct attr **attrs, struct attr *parent)
+{
+ struct attr *attr, *playing, *shuffle, *repeat;
+
+ dbg(lvl_debug,"Initializing stub\n");
+ srandom(time(NULL));
+ stub = g_new0 (struct audio_priv, 1);
+ /* example for reading an attribute from navit.xml
+ * here is the path to the music directory given
+ */
+ if ((attr = attr_search (attrs, NULL, attr_music_dir)))
+ {
+ stub->musicdir = g_strdup(attr->u.str);
+ dbg (lvl_info, "found music directory: %s\n", stub->musicdir);
+ }
+
+ /**
+ * place init code for your player implementation here
+ */
+ stub->idle = callback_new_1 (callback_cast (stub_stub_idle), stub);
+ stub->timeout = event_add_timeout(1000, 1, stub->idle);
+ stub->callback = callback_new_1 (callback_cast (stub_stub_idle), stub);
+ stub->timeout = event_add_timeout(1000, 1, stub->callback);
+
+ stub->playing = false;
+ stub->attrs=attrs;
+ //*
+ playing = attr_search(stub->attrs, NULL, attr_playing);
+
+ if(!playing){
+ playing = g_new0( struct attr, 1);
+ playing->type = attr_playing;
+ stub->attrs=attr_generic_add_attr(stub->attrs, playing);
+ dbg (lvl_debug,"*\n");
+ }
+ repeat = attr_search(stub->attrs, NULL, attr_repeat);
+
+ if(!repeat){
+ repeat = g_new0( struct attr, 1);
+ repeat->type = attr_repeat;
+ stub->attrs=attr_generic_add_attr(stub->attrs, repeat);
+ dbg (lvl_debug,"*\n");
+ }
+ shuffle = attr_search(stub->attrs, NULL, attr_shuffle);
+
+ if(!shuffle){
+ shuffle = g_new0( struct attr, 1);
+ shuffle->type = attr_shuffle;
+ stub->attrs=attr_generic_add_attr(stub->attrs, shuffle);
+ dbg (lvl_debug,"*\n");
+ }
+ //*/
+
+ dbg (lvl_debug, "Callback created successfully\n");
+
+ stub->cbl = cbl;
+ *meth=player_stub_meth;
+
+ return stub;
+}
+
+
+/**
+* @brief plugin entry point
+*/
+void
+plugin_init(void)
+{
+ dbg (lvl_debug, "player-stub\n");
+ plugin_register_category_audio("player-stub", player_stub_new);
+}
diff --git a/navit/audio/queue.h b/navit/audio/queue.h
new file mode 100644
index 000000000..b0e6b38c1
--- /dev/null
+++ b/navit/audio/queue.h
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list. Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction. Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ (head)->lh_first = NULL; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var); \
+ (var) = ((var)->field.le_next))
+
+/*
+ * List access methods.
+ */
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = NULL; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (listelm)->field.stqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->stqh_first; \
+ while (curelm->field.stqe_next != (elm)) \
+ curelm = curelm->field.stqe_next; \
+ if ((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->stqh_first); \
+ (var); \
+ (var) = ((var)->field.stqe_next))
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var); \
+ (var) = ((var)->field.sqe_next))
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define _TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define _TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { (void *)&head, (void *)&head }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->cqh_first); \
+ (var) != (const void *)(head); \
+ (var) = ((var)->field.cqe_next))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = ((head)->cqh_last); \
+ (var) != (const void *)(head); \
+ (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
+ (((elm)->field.cqe_next == (void *)(head)) \
+ ? ((head)->cqh_first) \
+ : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field) \
+ (((elm)->field.cqe_prev == (void *)(head)) \
+ ? ((head)->cqh_last) \
+ : (elm->field.cqe_prev))
+
+#endif /* sys/queue.h */
diff --git a/navit/graphics/qt5/event_qt5.cpp b/navit/graphics/qt5/event_qt5.cpp
index 7186534fe..70870ece7 100644
--- a/navit/graphics/qt5/event_qt5.cpp
+++ b/navit/graphics/qt5/event_qt5.cpp
@@ -73,7 +73,18 @@ void qt5_navit_timer::watchEvent(int id) {
}
}
-void qt5_navit_timer::timerEvent(QTimerEvent* event) {
+void qt5_navit_timer::watchEvent(int id)
+{
+ struct event_watch* ret = g_new0(struct event_watch, 1);
+ ret = (struct event_watch*)g_hash_table_lookup(watches, (void*)(long)id);
+ if (ret) {
+ dbg(lvl_debug, "callback found, calling it\n");
+ callback_call_0(ret->cb);
+ }
+}
+
+void qt5_navit_timer::timerEvent(QTimerEvent* event)
+{
int id = event->timerId();
void* multi = NULL;
// dbg(lvl_debug, "TimerEvent (%d)", id);
diff --git a/navit/gui/internal/CMakeLists.txt b/navit/gui/internal/CMakeLists.txt
index 27011bab3..bda2068da 100644
--- a/navit/gui/internal/CMakeLists.txt
+++ b/navit/gui/internal/CMakeLists.txt
@@ -2,6 +2,8 @@ if (GUI_INTERNAL_VISUAL_DBG)
add_definitions ("-DGUI_INTERNAL_VISUAL_DBG")
endif(GUI_INTERNAL_VISUAL_DBG)
-module_add_library(gui_internal gui_internal.c gui_internal_bookmark.c gui_internal_command.c gui_internal_gesture.c
- gui_internal_html.c gui_internal_menu.c gui_internal_poi.c gui_internal_search.c gui_internal_widget.c
- gui_internal_keyboard.c)
+if(USE_AUDIO_FRAMEWORK)
+ set(audio_sources gui_internal_media.c )
+endif(USE_AUDIO_FRAMEWORK)
+
+module_add_library(gui_internal ${audio_sources} gui_internal.c gui_internal_bookmark.c gui_internal_command.c gui_internal_gesture.c gui_internal_html.c gui_internal_menu.c gui_internal_poi.c gui_internal_search.c gui_internal_widget.c gui_internal_keyboard.c)
diff --git a/navit/gui/internal/gui_internal_command.c b/navit/gui/internal/gui_internal_command.c
index 011d78908..fce70e7db 100644
--- a/navit/gui/internal/gui_internal_command.c
+++ b/navit/gui/internal/gui_internal_command.c
@@ -36,6 +36,9 @@
#include "gui_internal_search.h"
#include "gui_internal_poi.h"
#include "gui_internal_command.h"
+#ifdef USE_AUDIO_FRAMEWORK
+#include "gui_internal_media.h"
+#endif
#if HAS_IFADDRS
#include <ifaddrs.h>
#include <arpa/inet.h>
@@ -1318,6 +1321,10 @@ static struct command_table commands[] = {
#if HAS_IFADDRS
{"network_info",command_cast(gui_internal_cmd2)},
#endif
+#ifdef USE_AUDIO_FRAMEWORK
+ {"media_show_playlist", command_cast (gui_internal_media_show_playlist)},
+#endif
+
};
void gui_internal_command_init(struct gui_priv *this, struct attr **attrs) {
diff --git a/navit/gui/internal/gui_internal_media.c b/navit/gui/internal/gui_internal_media.c
new file mode 100644
index 000000000..f8f9f10ed
--- /dev/null
+++ b/navit/gui/internal/gui_internal_media.c
@@ -0,0 +1,332 @@
+/**
+ * Navit, a modular navigation system.
+ * Copyright (C) 2005-2016 Navit Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#include <glib.h>
+#include <navit/main.h>
+#include <navit/debug.h>
+#include <navit/point.h>
+#include <navit/navit.h>
+#include <navit/callback.h>
+#include <navit/color.h>
+#include <navit/event.h>
+
+#include "time.h"
+#include "gui_internal.h"
+#include "gui_internal_menu.h"
+#include "coord.h"
+#include "gui_internal_widget.h"
+#include "gui_internal_priv.h"
+#include "gui_internal_media.h"
+#include "gui_internal_command.h"
+#include "audio.h"
+
+int currently_displayed_playlist = -1;
+
+static void
+tracks_free(gpointer data)
+{
+ if (data != NULL) {
+ struct audio_track *track = data;
+ g_free(track->name);
+ g_free(track);
+ }
+}
+
+/**
+ * @brief play a track from the playlist
+ * @param[in] this - pointer to the gui_priv
+ * wm - pointer the the parent widget
+ * date - pointer to arbitrary data
+ */
+void
+media_play_track(struct gui_priv *this, struct widget *wm, void *data)
+{
+ dbg(lvl_info, "Got a request to play a specific track : %i\n",
+ wm->c.x);
+ audio_play_track(this->nav, wm->c.x);
+ gui_internal_prune_menu(this, NULL);
+ //gui_internal_media_show_playlist (this, NULL, NULL);
+}
+
+/**
+ * @brief play a playlist from the list of playlist playlist
+ * @param[in] this - pointer to the gui_priv
+ * wm - pointer the the parent widget
+ * date - pointer to arbitrary data
+ */
+void
+media_play_playlist(struct gui_priv *this, struct widget *wm, void *data)
+{
+ dbg(lvl_info, "Got a request to play a specific playlist : %i\n",
+ wm->c.x);
+ currently_displayed_playlist = wm->c.x;
+ gui_internal_media_show_playlist(this, NULL, NULL);
+}
+
+/**
+ * @brief Perform a media action
+ * @param[in] this - pointer to the gui_priv
+ * wm - pointer the the parent widget
+ * date - pointer to arbitrary data
+ *
+ * Perform an action on the audio player which are defined in the player implementation
+ *
+ */
+void
+media_action_do(struct gui_priv *this, struct widget *wm, void *data)
+{
+ dbg(lvl_info, "Got a request to perform an action : %i\n",
+ (int) data);
+ int action = (int) data;
+ audio_do_action(this->nav, action);
+ switch (action) {
+ case AUDIO_PLAYBACK_TOGGLE:
+ case AUDIO_PLAYBACK_NEXT_TRACK:
+ case AUDIO_PLAYBACK_PREVIOUS_TRACK:
+ case AUDIO_MISC_RELOAD_PLAYLISTS:{
+ gui_internal_prune_menu(this, NULL);
+ break;
+ }
+ case AUDIO_PLAYBACK_NEXT_PLAYLIST:
+ case AUDIO_PLAYBACK_PREVIOUS_PLAYLIST:
+ case AUDIO_PLAYBACK_NEXT_ARTIST:
+ case AUDIO_PLAYBACK_PREVIOUS_ARTIST:
+ case AUDIO_MODE_TOGGLE_SHUFFLE:
+ case AUDIO_MODE_TOGGLE_REPEAT:{
+ gui_internal_media_show_playlist(this, NULL, NULL);
+ break;
+ }
+ case AUDIO_MISC_DELETE_PLAYLIST:{
+ gui_internal_media_show_rootlist(this, NULL, NULL);
+ break;
+ }
+ default:{
+ dbg(lvl_error,
+ "Don't know what to do with action '%i'. That's a bug\n",
+ action);
+ gui_internal_media_show_playlist(this, NULL, NULL);
+ }
+ }
+}
+
+/**
+ * @brief Build a playlist 'toolbar'
+ *
+ * @param[in] this - pointer to the gui_priv
+ *
+ * @return the resulting widget
+ *
+ * Builds a widget containing a button to browse the root playlist,
+ * and another botton to display and toggle the state of the offline availability
+ *
+ */
+static struct widget *
+gui_internal_media_playlist_toolbar(struct gui_priv *this)
+{
+ struct widget *wl, *wb;
+ int nitems, nrows;
+ GList *actions = audio_get_actions(this->nav);
+ wl = gui_internal_box_new(this,
+ gravity_left_center |
+ orientation_horizontal_vertical |
+ flags_fill);
+ wl->background = this->background;
+ wl->w = this->root.w;
+ wl->cols = this->root.w / this->icon_s;
+ nitems = 2;
+ nrows = nitems / wl->cols + (nitems % wl->cols > 0);
+ wl->h = this->icon_l * nrows;
+ wb = gui_internal_button_new_with_callback(this, "Playlists",
+ image_new_s(this,
+ "media_category"),
+ gravity_left_center |
+ orientation_horizontal,
+ gui_internal_media_show_rootlist,
+ NULL);
+ gui_internal_widget_append(wl, wb);
+ while (actions) {
+ struct audio_actions *aa = actions->data;
+ actions = g_list_next(actions);
+ if (aa->icon && aa->action) {
+ gui_internal_widget_append(wl, wb =
+ gui_internal_button_new_with_callback
+ (this, NULL,
+ image_new_s(this,
+ aa->icon),
+ gravity_left_center |
+ orientation_horizontal,
+ media_action_do,
+ aa->action));
+ }
+ }
+ return wl;
+}
+
+/**
+ * @brief Show the playlists in the root playlist
+ * @param[in] this - pointer to the gui_priv
+ * wm - pointer the the parent widget
+ * data - pointer to arbitrary data
+ *
+ * @return nothing
+ *
+ * Display a list of the playlists in the root playlist
+ *
+ */
+void
+gui_internal_media_show_rootlist(struct gui_priv *this, struct widget *wm,
+ void *data)
+{
+ struct widget *wb, *w, *wbm;
+ struct widget *tbl, *row;
+ dbg(lvl_error, "Showing rootlist\n");
+ GList *playlists = audio_get_playlists(this->nav);
+
+ gui_internal_prune_menu_count(this, 1, 0);
+ wb = gui_internal_menu(this, "Media > Playlists");
+ wb->background = this->background;
+ w = gui_internal_box_new(this,
+ gravity_top_center | orientation_vertical
+ | flags_expand | flags_fill);
+ gui_internal_widget_append(wb, w);
+
+
+ tbl =
+ gui_internal_widget_table_new(this,
+ gravity_left_top | flags_fill |
+ flags_expand |
+ orientation_vertical, 1);
+ gui_internal_widget_append(w, tbl);
+
+ while (playlists) {
+ struct audio_playlist *pl = playlists->data;
+ dbg(lvl_error, "playlist : %s\n", pl->name);
+
+ playlists = g_list_next(playlists);
+ row =
+ gui_internal_widget_table_row_new(this,
+ gravity_left |
+ flags_fill |
+ orientation_horizontal);
+ gui_internal_widget_append(tbl, row);
+ wbm = gui_internal_button_new_with_callback(this,
+ pl->name,
+ image_new_s
+ (this,
+ (pl->
+ icon) ? (pl->
+ icon)
+ :
+ ("media_hierarchy")),
+ gravity_left_center
+ |
+ orientation_horizontal
+ | flags_fill,
+ media_play_playlist,
+ NULL);
+
+ gui_internal_widget_append(row, wbm);
+ wbm->c.x = pl->index;
+ }
+
+ gui_internal_menu_render(this);
+
+}
+
+/**
+ * @brief Show the tracks in the current playlist
+ * @param[in] this - pointer to the gui_priv
+ * wm - pointer the the parent widget
+ * data - pointer to arbitrary data
+ *
+ * @return nothing
+ *
+ * Display a list of the tracks in the current playlist
+ *
+ */
+void
+gui_internal_media_show_playlist(struct gui_priv *this, struct widget *wm,
+ void *data)
+{
+ struct widget *wb, *w, *wbm;
+ struct widget *tbl, *row;
+ int index = 0;
+ gui_internal_prune_menu_count(this, 1, 0);
+#ifndef USE_AUDIO_FRAMEWORK
+ wb = gui_internal_menu(this,
+ g_strdup_printf("Media not available"));
+ wb->background = this->background;
+ w = gui_internal_box_new(this,
+ gravity_top_center | orientation_vertical
+ | flags_expand | flags_fill);
+ gui_internal_widget_append(wb, w);
+#else
+ GList *tracks =
+ audio_get_tracks(this->nav, currently_displayed_playlist);
+ struct audio_playlist *pl;
+ GList *playlist = audio_get_playlists(this->nav);
+
+ dbg(lvl_info, "\n\t%p\n\n", this);
+
+ wb = gui_internal_menu(this,
+ g_strdup_printf("Media > %s",
+ audio_get_current_playlist
+ (this->nav)));
+ wb->background = this->background;
+ w = gui_internal_box_new(this,
+ gravity_top_center | orientation_vertical
+ | flags_expand | flags_fill);
+ gui_internal_widget_append(wb, w);
+ gui_internal_widget_append(w,
+ gui_internal_media_playlist_toolbar
+ (this));
+ tbl =
+ gui_internal_widget_table_new(this,
+ gravity_left_top | flags_fill |
+ flags_expand |
+ orientation_vertical, 1);
+ gui_internal_widget_append(w, tbl);
+ while (tracks) {
+ struct audio_track *track = tracks->data;
+ tracks = g_list_next(tracks);
+ row =
+ gui_internal_widget_table_row_new(this,
+ gravity_left |
+ flags_fill |
+ orientation_horizontal);
+ gui_internal_widget_append(tbl, row);
+ wbm = gui_internal_button_new_with_callback(this,
+ track->name,
+ image_new_s
+ (this,
+ track->icon),
+ gravity_left_center
+ |
+ orientation_horizontal
+ | flags_fill,
+ media_play_track,
+ NULL);
+ wbm->c.x = track->index;
+ gui_internal_widget_append(row, wbm);
+ }
+ g_list_free_full(tracks, tracks_free);
+#endif
+ gui_internal_menu_render(this);
+}
diff --git a/navit/gui/internal/gui_internal_media.h b/navit/gui/internal/gui_internal_media.h
new file mode 100644
index 000000000..42791e5eb
--- /dev/null
+++ b/navit/gui/internal/gui_internal_media.h
@@ -0,0 +1,2 @@
+void gui_internal_media_show_playlist(struct gui_priv *this, struct widget *wm, void *data);
+void gui_internal_media_show_rootlist(struct gui_priv *this, struct widget *wm, void *data);
diff --git a/navit/icons/media_audio.svg b/navit/icons/media_audio.svg
new file mode 100644
index 000000000..04059dab7
--- /dev/null
+++ b/navit/icons/media_audio.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M2,6v4h3l3,3V3L5,6H2z M10.121,5.878L9.414,6.585C9.776,6.948,10,7.448,10,8
+ c0,0.553-0.224,1.053-0.586,1.414l0.707,0.707C10.664,9.579,11,8.829,11,8C11,7.171,10.664,6.421,10.121,5.878z M11.536,4.464
+ l-0.707,0.708C11.553,5.895,12,6.896,12,8c0,1.105-0.447,2.105-1.171,2.829l0.707,0.707C12.44,10.632,13,9.382,13,8
+ C13,6.619,12.44,5.369,11.536,4.464z"/>
+</svg>
diff --git a/navit/icons/media_category.svg b/navit/icons/media_category.svg
new file mode 100644
index 000000000..6e2cbd05c
--- /dev/null
+++ b/navit/icons/media_category.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<g id="Shape_1">
+ <g>
+ <polygon style="fill:#bfbfbf;" points="5.5,7 5,8 4,8 5,6 10,6 10,5 6.5,5 6,4 3,4 3,12 11,12 13,7 "/>
+ </g>
+</g>
+</svg>
diff --git a/navit/icons/media_close.svg b/navit/icons/media_close.svg
new file mode 100644
index 000000000..4a9965de5
--- /dev/null
+++ b/navit/icons/media_close.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<g id="Shape_2">
+ <g>
+ <polygon style="fill:#bfbfbf;" points="12,5 11,4 8.5,6.5 6,4 5,5 7.5,7.5 5,10 6,11 8.5,8.5 11,11 12,10 9.5,7.5 "/>
+ </g>
+</g>
+</svg>
diff --git a/navit/icons/media_document.svg b/navit/icons/media_document.svg
new file mode 100644
index 000000000..6b1c0c187
--- /dev/null
+++ b/navit/icons/media_document.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M9,3H8H4v10h8V7V6.949V6L9,3z M11,12H5V4h3v2v1h1h2V12z"/>
+</svg>
diff --git a/navit/icons/media_hierarchy.svg b/navit/icons/media_hierarchy.svg
new file mode 100644
index 000000000..fe6387c6b
--- /dev/null
+++ b/navit/icons/media_hierarchy.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Rectangle_3_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M2,5h2V3H2V5z M6,3v2h8V3H6z M2,9h2V7H2V9z M11,7H6v2h5V7z M2,13h2v-2H2V13z M6,13h7v-2H6V13z"/>
+</svg>
diff --git a/navit/icons/media_minus.svg b/navit/icons/media_minus.svg
new file mode 100644
index 000000000..4bc7ea37c
--- /dev/null
+++ b/navit/icons/media_minus.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<rect style="fill:#bfbfbf;" x="3" y="7" width="10" height="2"/>
+</svg>
diff --git a/navit/icons/media_next.svg b/navit/icons/media_next.svg
new file mode 100644
index 000000000..b74c13784
--- /dev/null
+++ b/navit/icons/media_next.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_next.svg next_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#bfbfbf;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>next_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(1.5,-2.25)">
+ <title>Tabelle.1</title>
+ <path d="M0 12 L6 8.25 L0 4.5 L0 12 ZM6.75 4.5 L6.75 12 L9 12 L9 4.5 L6.75 4.5 Z" class="st1"/>
+ </g>
+ </g>
+</svg>
diff --git a/navit/icons/media_next_artist.svg b/navit/icons/media_next_artist.svg
new file mode 100644
index 000000000..053dad172
--- /dev/null
+++ b/navit/icons/media_next_artist.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="ff_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#bfbfbf;" points="15,8 15,3 13,3 13,6.75 7,3 7,6.75 1,3 1,13 7,9.25 7,13 13,9.25 13,13 15,13 "/>
+</svg>
diff --git a/navit/icons/media_next_artist_bk.svg b/navit/icons/media_next_artist_bk.svg
new file mode 100644
index 000000000..bf3ba8afe
--- /dev/null
+++ b/navit/icons/media_next_artist_bk.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="ff_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#000000;" points="15,8 15,3 13,3 13,6.75 7,3 7,6.75 1,3 1,13 7,9.25 7,13 13,9.25 13,13 15,13 "/>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_next_artist_wh.svg b/navit/icons/media_next_artist_wh.svg
new file mode 100644
index 000000000..68b32df8c
--- /dev/null
+++ b/navit/icons/media_next_artist_wh.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="ff_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#FFFFFF;" points="15,8 15,3 13,3 13,6.75 7,3 7,6.75 1,3 1,13 7,9.25 7,13 13,9.25 13,13 15,13 "/>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_next_bk.svg b/navit/icons/media_next_bk.svg
new file mode 100644
index 000000000..52c00a064
--- /dev/null
+++ b/navit/icons/media_next_bk.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_next.svg next_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#000000;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>next_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(1.5,-2.25)">
+ <title>Tabelle.1</title>
+ <path d="M0 12 L6 8.25 L0 4.5 L0 12 ZM6.75 4.5 L6.75 12 L9 12 L9 4.5 L6.75 4.5 Z" class="st1"/>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_next_wh.svg b/navit/icons/media_next_wh.svg
new file mode 100644
index 000000000..f80d2c5ea
--- /dev/null
+++ b/navit/icons/media_next_wh.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_next.svg next_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#FFFFFF;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>next_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(1.5,-2.25)">
+ <title>Tabelle.1</title>
+ <path d="M0 12 L6 8.25 L0 4.5 L0 12 ZM6.75 4.5 L6.75 12 L9 12 L9 4.5 L6.75 4.5 Z" class="st1"/>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_pause.svg b/navit/icons/media_pause.svg
new file mode 100644
index 000000000..823692d79
--- /dev/null
+++ b/navit/icons/media_pause.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="pause_copy_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M4,13h3V3H4V13z M9,3v10h3V3H9z"/>
+</svg>
diff --git a/navit/icons/media_pause_bk.svg b/navit/icons/media_pause_bk.svg
new file mode 100644
index 000000000..3d366b30a
--- /dev/null
+++ b/navit/icons/media_pause_bk.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="pause_copy_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#000000;" d="M4,13h3V3H4V13z M9,3v10h3V3H9z"/>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_pause_wh.svg b/navit/icons/media_pause_wh.svg
new file mode 100644
index 000000000..f218ebbc0
--- /dev/null
+++ b/navit/icons/media_pause_wh.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="pause_copy_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#FFFFFF;" d="M4,13h3V3H4V13z M9,3v10h3V3H9z"/>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_play.svg b/navit/icons/media_play.svg
new file mode 100644
index 000000000..fab8f9bc2
--- /dev/null
+++ b/navit/icons/media_play.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="play_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#bfbfbf;" points="4,3 4,13 12,8 "/>
+</svg>
diff --git a/navit/icons/media_play_bk.svg b/navit/icons/media_play_bk.svg
new file mode 100644
index 000000000..f576832a4
--- /dev/null
+++ b/navit/icons/media_play_bk.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="play_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#000000;" points="4,3 4,13 12,8 "/>
+</svg>
diff --git a/navit/icons/media_play_wh.svg b/navit/icons/media_play_wh.svg
new file mode 100644
index 000000000..4f7b29657
--- /dev/null
+++ b/navit/icons/media_play_wh.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="play_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#FFFFFF;" points="4,3 4,13 12,8 "/>
+</svg>
diff --git a/navit/icons/media_playlist_downloading.svg b/navit/icons/media_playlist_downloading.svg
new file mode 100644
index 000000000..66b6a3f0e
--- /dev/null
+++ b/navit/icons/media_playlist_downloading.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="blue" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 57,35.5808L 57,55C 57,56.4468 56.8159,57.5384 56.4476,58.2749C 56.0794,59.0113 55.2259,59.8757 54.3455,60.3543C 53.4651,60.8328 52.4698,61.072 51.3595,61.072C 50.0847,61.072 49.0678,60.7066 48.3089,59.9757C 47.55,59.2449 47.1706,58.3019 47.1706,57.1467C 47.1706,55.9803 47.6201,54.9728 48.5192,54.1242C 49.4183,53.2755 50.58,52.8512 52.0044,52.8512L 53.2408,52.9746L 54,53.25L 54,41.2251L 45,44.3472L 45,59C 45,60.4131 44.8131,61.5141 44.4392,62.3029C 44.0654,63.0917 43.2099,63.7151 42.2996,64.1824C 41.3893,64.6497 40.3958,64.8833 39.3191,64.8833C 38.0481,64.8833 37.0415,64.5245 36.2994,63.8067C 35.5573,63.0889 35.1863,62.1506 35.1863,60.9916C 35.1863,59.7654 35.6302,58.7346 36.5181,57.899C 37.406,57.0635 38.5677,56.6457 40.0033,56.6457C 40.6818,56.6457 41.2918,56.5962 42,56.8778L 42,41L 57,35.5808 Z M 20,17L 43.25,17L 56,29.75L 56,34.75L 52,36.25L 52,34L 39,34L 39,21L 24,21L 24,55L 37.5,55L 34,59L 20,59L 20,17 Z M 43,22.25L 43,30L 50.75,30L 43,22.25 Z "/>
+</svg>
diff --git a/navit/icons/media_playlist_no_offline.svg b/navit/icons/media_playlist_no_offline.svg
new file mode 100644
index 000000000..49bc927f1
--- /dev/null
+++ b/navit/icons/media_playlist_no_offline.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="red" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 57,35.5808L 57,55C 57,56.4468 56.8159,57.5384 56.4476,58.2749C 56.0794,59.0113 55.2259,59.8757 54.3455,60.3543C 53.4651,60.8328 52.4698,61.072 51.3595,61.072C 50.0847,61.072 49.0678,60.7066 48.3089,59.9757C 47.55,59.2449 47.1706,58.3019 47.1706,57.1467C 47.1706,55.9803 47.6201,54.9728 48.5192,54.1242C 49.4183,53.2755 50.58,52.8512 52.0044,52.8512L 53.2408,52.9746L 54,53.25L 54,41.2251L 45,44.3472L 45,59C 45,60.4131 44.8131,61.5141 44.4392,62.3029C 44.0654,63.0917 43.2099,63.7151 42.2996,64.1824C 41.3893,64.6497 40.3958,64.8833 39.3191,64.8833C 38.0481,64.8833 37.0415,64.5245 36.2994,63.8067C 35.5573,63.0889 35.1863,62.1506 35.1863,60.9916C 35.1863,59.7654 35.6302,58.7346 36.5181,57.899C 37.406,57.0635 38.5677,56.6457 40.0033,56.6457C 40.6818,56.6457 41.2918,56.5962 42,56.8778L 42,41L 57,35.5808 Z M 20,17L 43.25,17L 56,29.75L 56,34.75L 52,36.25L 52,34L 39,34L 39,21L 24,21L 24,55L 37.5,55L 34,59L 20,59L 20,17 Z M 43,22.25L 43,30L 50.75,30L 43,22.25 Z "/>
+</svg>
diff --git a/navit/icons/media_playlist_offline.svg b/navit/icons/media_playlist_offline.svg
new file mode 100644
index 000000000..0750b8758
--- /dev/null
+++ b/navit/icons/media_playlist_offline.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="green" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 57,35.5808L 57,55C 57,56.4468 56.8159,57.5384 56.4476,58.2749C 56.0794,59.0113 55.2259,59.8757 54.3455,60.3543C 53.4651,60.8328 52.4698,61.072 51.3595,61.072C 50.0847,61.072 49.0678,60.7066 48.3089,59.9757C 47.55,59.2449 47.1706,58.3019 47.1706,57.1467C 47.1706,55.9803 47.6201,54.9728 48.5192,54.1242C 49.4183,53.2755 50.58,52.8512 52.0044,52.8512L 53.2408,52.9746L 54,53.25L 54,41.2251L 45,44.3472L 45,59C 45,60.4131 44.8131,61.5141 44.4392,62.3029C 44.0654,63.0917 43.2099,63.7151 42.2996,64.1824C 41.3893,64.6497 40.3958,64.8833 39.3191,64.8833C 38.0481,64.8833 37.0415,64.5245 36.2994,63.8067C 35.5573,63.0889 35.1863,62.1506 35.1863,60.9916C 35.1863,59.7654 35.6302,58.7346 36.5181,57.899C 37.406,57.0635 38.5677,56.6457 40.0033,56.6457C 40.6818,56.6457 41.2918,56.5962 42,56.8778L 42,41L 57,35.5808 Z M 20,17L 43.25,17L 56,29.75L 56,34.75L 52,36.25L 52,34L 39,34L 39,21L 24,21L 24,55L 37.5,55L 34,59L 20,59L 20,17 Z M 43,22.25L 43,30L 50.75,30L 43,22.25 Z "/>
+</svg>
diff --git a/navit/icons/media_playlist_pending.svg b/navit/icons/media_playlist_pending.svg
new file mode 100644
index 000000000..2692ae67f
--- /dev/null
+++ b/navit/icons/media_playlist_pending.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="orange" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 57,35.5808L 57,55C 57,56.4468 56.8159,57.5384 56.4476,58.2749C 56.0794,59.0113 55.2259,59.8757 54.3455,60.3543C 53.4651,60.8328 52.4698,61.072 51.3595,61.072C 50.0847,61.072 49.0678,60.7066 48.3089,59.9757C 47.55,59.2449 47.1706,58.3019 47.1706,57.1467C 47.1706,55.9803 47.6201,54.9728 48.5192,54.1242C 49.4183,53.2755 50.58,52.8512 52.0044,52.8512L 53.2408,52.9746L 54,53.25L 54,41.2251L 45,44.3472L 45,59C 45,60.4131 44.8131,61.5141 44.4392,62.3029C 44.0654,63.0917 43.2099,63.7151 42.2996,64.1824C 41.3893,64.6497 40.3958,64.8833 39.3191,64.8833C 38.0481,64.8833 37.0415,64.5245 36.2994,63.8067C 35.5573,63.0889 35.1863,62.1506 35.1863,60.9916C 35.1863,59.7654 35.6302,58.7346 36.5181,57.899C 37.406,57.0635 38.5677,56.6457 40.0033,56.6457C 40.6818,56.6457 41.2918,56.5962 42,56.8778L 42,41L 57,35.5808 Z M 20,17L 43.25,17L 56,29.75L 56,34.75L 52,36.25L 52,34L 39,34L 39,21L 24,21L 24,55L 37.5,55L 34,59L 20,59L 20,17 Z M 43,22.25L 43,30L 50.75,30L 43,22.25 Z "/>
+</svg>
diff --git a/navit/icons/media_playlists.svg b/navit/icons/media_playlists.svg
new file mode 100644
index 000000000..fa9b28239
--- /dev/null
+++ b/navit/icons/media_playlists.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_playlists.svg Rectangle_3_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#bfbfbf;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>Rectangle_3_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(1.5,-2.25)">
+ <title>Tabelle.1</title>
+ <path d="M0 6 L1.5 6 L1.5 4.5 L0 4.5 L0 6 ZM3 4.5 L3 6 L9 6 L9 4.5 L3 4.5 ZM0 9 L1.5 9 L1.5 7.5 L0 7.5 L0 9 ZM6.75 7.5
+ L3 7.5 L3 9 L6.75 9 L6.75 7.5 ZM0 12 L1.5 12 L1.5 10.5 L0 10.5 L0 12 ZM3 12 L8.25 12 L8.25 10.5 L3 10.5
+ L3 12 Z" class="st1"/>
+ </g>
+ </g>
+</svg>
diff --git a/navit/icons/media_plus.svg b/navit/icons/media_plus.svg
new file mode 100644
index 000000000..2b7a19531
--- /dev/null
+++ b/navit/icons/media_plus.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#bfbfbf;" points="13,7 9,7 9,3 7,3 7,7 3,7 3,9 7,9 7,13 9,13 9,9 13,9 "/>
+</svg>
diff --git a/navit/icons/media_prev.svg b/navit/icons/media_prev.svg
new file mode 100644
index 000000000..fff47b782
--- /dev/null
+++ b/navit/icons/media_prev.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_prev.svg next_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#bfbfbf;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>next_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(10.5,14.25) rotate(180)">
+ <title>Tabelle.1</title>
+ <path d="M0 12 L6 8.25 L0 4.5 L0 12 ZM6.75 4.5 L6.75 12 L9 12 L9 4.5 L6.75 4.5 Z" class="st1"/>
+ </g>
+ </g>
+</svg>
diff --git a/navit/icons/media_prev_artist.svg b/navit/icons/media_prev_artist.svg
new file mode 100644
index 000000000..0e82740ee
--- /dev/null
+++ b/navit/icons/media_prev_artist.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="rew_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#bfbfbf;" points="15,3 9,6.75 9,3 3,6.75 3,3 1,3 1,13 3,13 3,9.25 9,13 9,9.25 15,13 "/>
+</svg>
diff --git a/navit/icons/media_prev_artist_bk.svg b/navit/icons/media_prev_artist_bk.svg
new file mode 100644
index 000000000..0d0e4a3df
--- /dev/null
+++ b/navit/icons/media_prev_artist_bk.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="rew_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#000000;" points="15,3 9,6.75 9,3 3,6.75 3,3 1,3 1,13 3,13 3,9.25 9,13 9,9.25 15,13 "/>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_prev_artist_wh.svg b/navit/icons/media_prev_artist_wh.svg
new file mode 100644
index 000000000..f6d7a7d39
--- /dev/null
+++ b/navit/icons/media_prev_artist_wh.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="rew_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#FFFFFF;" points="15,3 9,6.75 9,3 3,6.75 3,3 1,3 1,13 3,13 3,9.25 9,13 9,9.25 15,13 "/>
+</svg> \ No newline at end of file
diff --git a/navit/icons/media_prev_bk.svg b/navit/icons/media_prev_bk.svg
new file mode 100644
index 000000000..a4aa0f92d
--- /dev/null
+++ b/navit/icons/media_prev_bk.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_prev.svg next_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#000000;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>next_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(10.5,14.25) rotate(180)">
+ <title>Tabelle.1</title>
+ <path d="M0 12 L6 8.25 L0 4.5 L0 12 ZM6.75 4.5 L6.75 12 L9 12 L9 4.5 L6.75 4.5 Z" class="st1"/>
+ </g>
+ </g>
+</svg>
diff --git a/navit/icons/media_prev_wh.svg b/navit/icons/media_prev_wh.svg
new file mode 100644
index 000000000..1956cc2f0
--- /dev/null
+++ b/navit/icons/media_prev_wh.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generiert durch Microsoft Visio, SVG Export media_prev.svg next_1_ -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+ xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="0.166667in" height="0.166667in" viewBox="0 0 12 12"
+ xml:space="preserve" color-interpolation-filters="sRGB" class="st2">
+ <v:documentProperties v:langID="1031" v:viewMarkup="false"/>
+
+ <style type="text/css">
+ <![CDATA[
+ .st1 {fill:#FFFFFF;stroke:none;stroke-linecap:butt;stroke-width:0.75}
+ .st2 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+ ]]>
+ </style>
+
+ <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+ <title>next_1_</title>
+ <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="19" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+ <g id="shape1-1" v:mID="1" v:groupContext="shape" transform="translate(10.5,14.25) rotate(180)">
+ <title>Tabelle.1</title>
+ <path d="M0 12 L6 8.25 L0 4.5 L0 12 ZM6.75 4.5 L6.75 12 L9 12 L9 4.5 L6.75 4.5 Z" class="st1"/>
+ </g>
+ </g>
+</svg>
diff --git a/navit/icons/media_rating_empty.svg b/navit/icons/media_rating_empty.svg
new file mode 100644
index 000000000..a3f8fc1e2
--- /dev/null
+++ b/navit/icons/media_rating_empty.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M8,4.199L8.922,7h2.902L9.478,8.64l0.925,2.762L8,9.69L5.599,11.4L6.523,8.64L4.177,7h2.901L8,4.199
+ M8,1L6.354,6H1l4.338,3.031L3.674,14L8,10.918L12.326,14l-1.663-4.969L15,6H9.646L8,1L8,1z"/>
+</svg>
diff --git a/navit/icons/media_rating_full.svg b/navit/icons/media_rating_full.svg
new file mode 100644
index 000000000..e430a14a5
--- /dev/null
+++ b/navit/icons/media_rating_full.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#bfbfbf;" points="15,6 9.646,6 8,1 6.354,6 1,6 5.338,9.031 3.674,14 8,10.918 12.326,14 10.663,9.031 "/>
+</svg>
diff --git a/navit/icons/media_rating_half.svg b/navit/icons/media_rating_half.svg
new file mode 100644
index 000000000..80b5204fc
--- /dev/null
+++ b/navit/icons/media_rating_half.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M15,6H9.646L8,1L6.354,6H1l4.338,3.031L3.674,14L8,10.918L12.326,14l-1.663-4.969L15,6z M8,9.69
+ V4.199L8.922,7h2.902L9.478,8.64l0.925,2.762L8,9.69z"/>
+</svg>
diff --git a/navit/icons/media_refresh.svg b/navit/icons/media_refresh.svg
new file mode 100644
index 000000000..790764744
--- /dev/null
+++ b/navit/icons/media_refresh.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Shape_2_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M3.775,3.775C2.688,4.859,2,6.343,2,8c0,2.972,2.164,5.433,5,5.91v-2.052C5.278,11.412,4,9.861,4,8
+ c0-1.105,0.458-2.095,1.185-2.815L7,7V2H2L3.775,3.775z M14,8c0-2.972-2.164-5.433-5-5.91v2.052C10.722,4.588,12,6.139,12,8
+ c0,1.105-0.458,2.095-1.185,2.815l-1.67-1.67C9.052,9.052,9,9,9,9v5h5c0,0-0.81-0.81-1.775-1.775C13.312,11.14,14,9.657,14,8z"/>
+</svg>
diff --git a/navit/icons/media_repeat_off.svg b/navit/icons/media_repeat_off.svg
new file mode 100644
index 000000000..790764744
--- /dev/null
+++ b/navit/icons/media_repeat_off.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Shape_2_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M3.775,3.775C2.688,4.859,2,6.343,2,8c0,2.972,2.164,5.433,5,5.91v-2.052C5.278,11.412,4,9.861,4,8
+ c0-1.105,0.458-2.095,1.185-2.815L7,7V2H2L3.775,3.775z M14,8c0-2.972-2.164-5.433-5-5.91v2.052C10.722,4.588,12,6.139,12,8
+ c0,1.105-0.458,2.095-1.185,2.815l-1.67-1.67C9.052,9.052,9,9,9,9v5h5c0,0-0.81-0.81-1.775-1.775C13.312,11.14,14,9.657,14,8z"/>
+</svg>
diff --git a/navit/icons/media_repeat_playlist.svg b/navit/icons/media_repeat_playlist.svg
new file mode 100644
index 000000000..25fed0000
--- /dev/null
+++ b/navit/icons/media_repeat_playlist.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Shape_2_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#008EBE;" d="M3.775,3.775C2.688,4.859,2,6.343,2,8c0,2.972,2.164,5.433,5,5.91v-2.052C5.278,11.412,4,9.861,4,8
+ c0-1.105,0.458-2.095,1.185-2.815L7,7V2H2L3.775,3.775z M14,8c0-2.972-2.164-5.433-5-5.91v2.052C10.722,4.588,12,6.139,12,8
+ c0,1.105-0.458,2.095-1.185,2.815l-1.67-1.67C9.052,9.052,9,9,9,9v5h5c0,0-0.81-0.81-1.775-1.775C13.312,11.14,14,9.657,14,8z"/>
+</svg>
diff --git a/navit/icons/media_repeat_track.svg b/navit/icons/media_repeat_track.svg
new file mode 100644
index 000000000..89e80de89
--- /dev/null
+++ b/navit/icons/media_repeat_track.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Shape_2_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#0E5C13;" d="M3.775,3.775C2.688,4.859,2,6.343,2,8c0,2.972,2.164,5.433,5,5.91v-2.052C5.278,11.412,4,9.861,4,8
+ c0-1.105,0.458-2.095,1.185-2.815L7,7V2H2L3.775,3.775z M14,8c0-2.972-2.164-5.433-5-5.91v2.052C10.722,4.588,12,6.139,12,8
+ c0,1.105-0.458,2.095-1.185,2.815l-1.67-1.67C9.052,9.052,9,9,9,9v5h5c0,0-0.81-0.81-1.775-1.775C13.312,11.14,14,9.657,14,8z"/>
+</svg>
diff --git a/navit/icons/media_rewind.svg b/navit/icons/media_rewind.svg
new file mode 100644
index 000000000..b10bae0f0
--- /dev/null
+++ b/navit/icons/media_rewind.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="rew_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<polygon style="fill:#bfbfbf;" points="15,3 9,6.75 9,3 1,8 9,13 9,9.25 15,13 "/>
+</svg>
diff --git a/navit/icons/media_search.svg b/navit/icons/media_search.svg
new file mode 100644
index 000000000..d5f96b7cc
--- /dev/null
+++ b/navit/icons/media_search.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<g>
+ <path style="fill:#bfbfbf;" d="M14,13l-3.866-3.866C10.673,8.393,11,7.487,11,6.5C11,4.015,8.985,2,6.5,2S2,4.015,2,6.5
+ S4.015,11,6.5,11c0.987,0,1.893-0.327,2.634-0.866L13,14L14,13z M3,6.5C3,4.567,4.567,3,6.5,3C8.434,3,10,4.567,10,6.5
+ S8.434,10,6.5,10C4.567,10,3,8.433,3,6.5z"/>
+</g>
+</svg>
diff --git a/navit/icons/media_shuffle_off.svg b/navit/icons/media_shuffle_off.svg
new file mode 100644
index 000000000..a765cdfa2
--- /dev/null
+++ b/navit/icons/media_shuffle_off.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M13,10h-2c-1.104,0-2-0.896-2-2c0-2.209-1.791-4-4-4H2v2h3c1.104,0,2,0.896,2,2c0,2.209,1.791,4,4,4
+ h2v1l2-2l-2-2V10z M11,6h2v1l2-2l-2-2v1h-2c-0.843,0-1.622,0.263-2.267,0.708c0.447,0.506,0.786,1.1,1,1.756
+ C10.079,6.179,10.517,6,11,6z M5,10H2v2h3c0.843,0,1.622-0.264,2.267-0.708c-0.447-0.506-0.786-1.1-1-1.756
+ C5.921,9.821,5.483,10,5,10z"/>
+</svg>
diff --git a/navit/icons/media_shuffle_playlists.svg b/navit/icons/media_shuffle_playlists.svg
new file mode 100644
index 000000000..a00b0d450
--- /dev/null
+++ b/navit/icons/media_shuffle_playlists.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#1E4BBC;;" d="M13,10h-2c-1.104,0-2-0.896-2-2c0-2.209-1.791-4-4-4H2v2h3c1.104,0,2,0.896,2,2c0,2.209,1.791,4,4,4
+ h2v1l2-2l-2-2V10z M11,6h2v1l2-2l-2-2v1h-2c-0.843,0-1.622,0.263-2.267,0.708c0.447,0.506,0.786,1.1,1,1.756
+ C10.079,6.179,10.517,6,11,6z M5,10H2v2h3c0.843,0,1.622-0.264,2.267-0.708c-0.447-0.506-0.786-1.1-1-1.756
+ C5.921,9.821,5.483,10,5,10z"/>
+</svg>
diff --git a/navit/icons/media_shuffle_tracks.svg b/navit/icons/media_shuffle_tracks.svg
new file mode 100644
index 000000000..00efc90e5
--- /dev/null
+++ b/navit/icons/media_shuffle_tracks.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#008EBE;;" d="M13,10h-2c-1.104,0-2-0.896-2-2c0-2.209-1.791-4-4-4H2v2h3c1.104,0,2,0.896,2,2c0,2.209,1.791,4,4,4
+ h2v1l2-2l-2-2V10z M11,6h2v1l2-2l-2-2v1h-2c-0.843,0-1.622,0.263-2.267,0.708c0.447,0.506,0.786,1.1,1,1.756
+ C10.079,6.179,10.517,6,11,6z M5,10H2v2h3c0.843,0,1.622-0.264,2.267-0.708c-0.447-0.506-0.786-1.1-1-1.756
+ C5.921,9.821,5.483,10,5,10z"/>
+</svg>
diff --git a/navit/icons/media_shuffle_tracks_playlists.svg b/navit/icons/media_shuffle_tracks_playlists.svg
new file mode 100644
index 000000000..fd0af0b1a
--- /dev/null
+++ b/navit/icons/media_shuffle_tracks_playlists.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#0E5C13;;" d="M13,10h-2c-1.104,0-2-0.896-2-2c0-2.209-1.791-4-4-4H2v2h3c1.104,0,2,0.896,2,2c0,2.209,1.791,4,4,4
+ h2v1l2-2l-2-2V10z M11,6h2v1l2-2l-2-2v1h-2c-0.843,0-1.622,0.263-2.267,0.708c0.447,0.506,0.786,1.1,1,1.756
+ C10.079,6.179,10.517,6,11,6z M5,10H2v2h3c0.843,0,1.622-0.264,2.267-0.708c-0.447-0.506-0.786-1.1-1-1.756
+ C5.921,9.821,5.483,10,5,10z"/>
+</svg>
diff --git a/navit/icons/media_spotify.svg b/navit/icons/media_spotify.svg
new file mode 100644
index 000000000..97006e3c1
--- /dev/null
+++ b/navit/icons/media_spotify.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<path fill="#bfbfbf" d="M8,1C4.1,1,1,4.1,1,8c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7C15,4.1,11.9,1,8,1z M10.8,11.3c-0.1,0-0.2,0-0.3-0.1
+ c-1-0.6-2.3-0.9-3.6-0.9c-0.7,0-1.5,0.1-2.2,0.2c-0.1,0-0.3,0.1-0.3,0.1c-0.3,0-0.4-0.2-0.4-0.4c0-0.3,0.2-0.4,0.4-0.5
+ C5.2,9.5,6,9.3,6.9,9.3c1.5,0,2.9,0.4,4.1,1.1c0.2,0.1,0.3,0.2,0.3,0.5C11.3,11.1,11.1,11.3,10.8,11.3z M11.6,9.5
+ c-0.2,0-0.2-0.1-0.4-0.1c-1.1-0.7-2.7-1.1-4.4-1.1c-0.9,0-1.7,0.1-2.3,0.3c-0.1,0-0.2,0.1-0.3,0.1C3.9,8.6,3.6,8.3,3.6,8
+ c0-0.3,0.1-0.5,0.4-0.6c0.8-0.2,1.6-0.4,2.8-0.4c1.8,0,3.6,0.5,5,1.3c0.2,0.1,0.3,0.3,0.3,0.6C12.1,9.2,11.9,9.5,11.6,9.5z
+ M12.5,7.3c-0.1,0-0.2,0-0.4-0.1C10.8,6.4,8.9,6,7,6C6,6,5.1,6.1,4.2,6.3c-0.1,0-0.2,0.1-0.4,0.1c-0.4,0-0.7-0.3-0.7-0.7
+ c0-0.4,0.2-0.6,0.5-0.7c1-0.3,2.1-0.4,3.3-0.4c2.1,0,4.2,0.4,5.8,1.4c0.2,0.1,0.4,0.3,0.4,0.6C13.1,7,12.8,7.3,12.5,7.3z"/>
+</svg>
diff --git a/navit/icons/media_standard.svg b/navit/icons/media_standard.svg
new file mode 100644
index 000000000..936837134
--- /dev/null
+++ b/navit/icons/media_standard.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<g>
+ <path style="fill:#bfbfbf;" d="M4,5v1h8V5H4z M9,7H4v1h5V7z M10,8h2V7h-2V8z M7,10h5V9H7V10z M6,9H4v1h2V9z M4,12h6v-1H4V12z"/>
+</g>
+</svg>
diff --git a/navit/icons/media_stop.svg b/navit/icons/media_stop.svg
new file mode 100644
index 000000000..b2de00c37
--- /dev/null
+++ b/navit/icons/media_stop.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="stop_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<rect x="4" y="4" style="fill:#bfbfbf;" width="8" height="8"/>
+</svg>
diff --git a/navit/icons/media_track_downloading.svg b/navit/icons/media_track_downloading.svg
new file mode 100644
index 000000000..8f8ccfc62
--- /dev/null
+++ b/navit/icons/media_track_downloading.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="orange" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 26.9167,47.5L 28.5,47.6496L 28.5,23.75L 53.8333,19L 53.8333,47.5C 53.8333,50.1234 50.9978,52.25 47.5,52.25C 44.0022,52.25 41.1667,50.1234 41.1667,47.5C 41.1667,44.8766 44.0022,42.75 47.5,42.75C 48.0467,42.75 48.5773,42.802 49.0833,42.8997L 49.0833,24.6406L 33.25,27.6094L 33.25,52.25C 33.25,54.8734 30.4145,57 26.9167,57C 23.4189,57 20.5833,54.8734 20.5833,52.25C 20.5833,49.6266 23.4189,47.5 26.9167,47.5 Z "/>
+</svg>
diff --git a/navit/icons/media_track_offline.svg b/navit/icons/media_track_offline.svg
new file mode 100644
index 000000000..650a3cd4d
--- /dev/null
+++ b/navit/icons/media_track_offline.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="red" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 26.9167,47.5L 28.5,47.6496L 28.5,23.75L 53.8333,19L 53.8333,47.5C 53.8333,50.1234 50.9978,52.25 47.5,52.25C 44.0022,52.25 41.1667,50.1234 41.1667,47.5C 41.1667,44.8766 44.0022,42.75 47.5,42.75C 48.0467,42.75 48.5773,42.802 49.0833,42.8997L 49.0833,24.6406L 33.25,27.6094L 33.25,52.25C 33.25,54.8734 30.4145,57 26.9167,57C 23.4189,57 20.5833,54.8734 20.5833,52.25C 20.5833,49.6266 23.4189,47.5 26.9167,47.5 Z "/>
+</svg>
diff --git a/navit/icons/media_track_offline_done.svg b/navit/icons/media_track_offline_done.svg
new file mode 100644
index 000000000..ddf3edda8
--- /dev/null
+++ b/navit/icons/media_track_offline_done.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="green" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 26.9167,47.5L 28.5,47.6496L 28.5,23.75L 53.8333,19L 53.8333,47.5C 53.8333,50.1234 50.9978,52.25 47.5,52.25C 44.0022,52.25 41.1667,50.1234 41.1667,47.5C 41.1667,44.8766 44.0022,42.75 47.5,42.75C 48.0467,42.75 48.5773,42.802 49.0833,42.8997L 49.0833,24.6406L 33.25,27.6094L 33.25,52.25C 33.25,54.8734 30.4145,57 26.9167,57C 23.4189,57 20.5833,54.8734 20.5833,52.25C 20.5833,49.6266 23.4189,47.5 26.9167,47.5 Z "/>
+</svg>
diff --git a/navit/icons/media_track_pending.svg b/navit/icons/media_track_pending.svg
new file mode 100644
index 000000000..99ea6d7e2
--- /dev/null
+++ b/navit/icons/media_track_pending.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
+ <path fill="blue" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 26.9167,47.5L 28.5,47.6496L 28.5,23.75L 53.8333,19L 53.8333,47.5C 53.8333,50.1234 50.9978,52.25 47.5,52.25C 44.0022,52.25 41.1667,50.1234 41.1667,47.5C 41.1667,44.8766 44.0022,42.75 47.5,42.75C 48.0467,42.75 48.5773,42.802 49.0833,42.8997L 49.0833,24.6406L 33.25,27.6094L 33.25,52.25C 33.25,54.8734 30.4145,57 26.9167,57C 23.4189,57 20.5833,54.8734 20.5833,52.25C 20.5833,49.6266 23.4189,47.5 26.9167,47.5 Z "/>
+</svg>
diff --git a/navit/icons/media_trash.svg b/navit/icons/media_trash.svg
new file mode 100644
index 000000000..f547546e8
--- /dev/null
+++ b/navit/icons/media_trash.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Group_1_copy_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
+ y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<g style="fill:#bfbfbf;">
+ <path d="M10,4c0-0.553-0.447-1-1-1H6C5.448,3,5,3.447,5,4L3,5v1h1h1h1h1h1h1h1h1h1V5L10,4z M8.5,5h-2C6.224,5,6,4.776,6,4.5
+ S6.224,4,6.5,4h2C8.776,4,9,4.224,9,4.5S8.776,5,8.5,5z"/>
+ <path d="M10,6.5V12H9V6.5H8V12H7V6.5H6V12H5V6.5H4V12c0,0.553,0.448,1,1,1h5c0.553,0,1-0.447,1-1V6.5H10z"/>
+</g>
+</svg>
diff --git a/navit/icons/media_warning.svg b/navit/icons/media_warning.svg
new file mode 100644
index 000000000..fdb7114ca
--- /dev/null
+++ b/navit/icons/media_warning.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<path style="fill:#bfbfbf;" d="M15.594,11.297L9.66,1.633c-0.913-1.487-2.407-1.487-3.32,0l-5.934,9.664
+ C-0.508,12.783,0.174,14,1.918,14h12.164C15.826,14,16.508,12.783,15.594,11.297z M8,12c-0.552,0-1-0.447-1-1s0.448-1,1-1
+ c0.553,0,1,0.447,1,1S8.553,12,8,12z M8.697,9H7.3L6.9,4h2.2L8.697,9z"/>
+</svg>
diff --git a/navit/navit.c b/navit/navit.c
index 1d1b8e664..875a20c82 100644
--- a/navit/navit.c
+++ b/navit/navit.c
@@ -69,6 +69,7 @@
#include "vehicleprofile.h"
#include "sunriset.h"
#include "bookmarks.h"
+#include "audio.h"
#ifdef HAVE_API_WIN32_BASE
#include <windows.h>
#include "util.h"
@@ -109,6 +110,13 @@ struct navit_vehicle {
int animate_cursor;
};
+struct navit_audio_plugin {
+ int active;
+ struct attr callback;
+ struct audio *audio;
+};
+
+
struct navit {
NAVIT_OBJECT
struct attr self;
@@ -154,6 +162,8 @@ struct navit {
struct log *textfile_debug_log;
struct pcoord destination;
int destination_valid;
+ GList *audio_plugins;
+ struct navit_audio_plugin *audio;
int blocked; /**< Whether draw operations are currently blocked. This can be a combination of the
following flags:
1: draw operations are blocked
@@ -195,6 +205,7 @@ struct attr_iter {
static void navit_vehicle_update_position(struct navit *this_, struct navit_vehicle *nv);
static void navit_vehicle_draw(struct navit *this_, struct navit_vehicle *nv, struct point *pnt);
static int navit_add_vehicle(struct navit *this_, struct vehicle *v);
+static int navit_add_audio(struct navit *this_, struct audio *a);
static int navit_set_attr_do(struct navit *this_, struct attr *attr, int init);
static int navit_get_cursor_pnt(struct navit *this_, struct point *p, int keep_orientation, int *dir);
static void navit_set_cursors(struct navit *this_);
@@ -1420,6 +1431,282 @@ static void navit_cmd_spawn(struct navit *this, char *function, struct attr **in
}
}
+static void
+audio_volume_do(struct navit *this, int direction)
+{
+ GList *l;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.volume && a->meth.volume != 0xffffffff ) {
+ dbg(lvl_info,"Found a volume method at %p in plugin %s\n", a->meth.volume, a->name);
+ int (*f)(struct audio_priv *this, const int direction)=a->meth.volume;
+ f(a->priv, direction);
+ }
+ l=g_list_next(l);
+ }
+}
+
+static void
+audio_volume_down(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ dbg(lvl_info,"Volume down\n");
+ audio_volume_do(this,-1);
+}
+
+static void
+audio_volume_toggle(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ dbg(lvl_info,"Volume toggle\n");
+ audio_volume_do(this,0);
+}
+
+static void
+audio_volume_up(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ dbg(lvl_info,"Volume up\n");
+ audio_volume_do(this,1);
+}
+
+void
+audio_set_volume(struct navit *this, int action){
+ audio_volume_do(this, action);
+}
+
+gboolean audio_get_status(struct navit *this){
+ return TRUE;
+}
+
+void
+audio_do_action(struct navit *this, int action)
+{
+ GList *l;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.action_do && a->meth.action_do != 0xffffffff ) {
+ dbg(lvl_info,"Found a action method at %p in plugin %s\n", a->meth.action_do, a->name);
+ int (*f)(struct audio_priv *this, const int action)=a->meth.action_do;
+ f(a->priv, action);
+ }
+ l=g_list_next(l);
+ }
+
+}
+
+
+static void
+audio_playback_do(struct navit *this, int action)
+{
+ GList *l;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.playback && a->meth.playback != 0xffffffff ) {
+ dbg(lvl_info,"Found a playback method at %p in plugin %s\n", a->meth.playback, a->name);
+ int (*f)(struct audio_priv *this, const int action)=a->meth.playback;
+ f(a->priv, action);
+ }
+ l=g_list_next(l);
+ }
+
+}
+
+static void
+audio_playback_previous(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_PREVIOUS_TRACK);
+}
+
+static void
+audio_playback_toggle(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_playback_do(this,AUDIO_PLAYBACK_TOGGLE);
+}
+
+static void
+audio_playback_play(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_TOGGLE);
+}
+
+static void
+audio_playback_pause(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_TOGGLE);
+}
+
+static void
+audio_playback_next(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_NEXT_TRACK);
+}
+static void
+audio_playback_previous_playlist(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_PREVIOUS_PLAYLIST);
+}
+
+static void
+audio_playback_next_playlist(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_NEXT_PLAYLIST);
+}
+
+static void
+audio_playback_previous_artist(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_PREVIOUS_ARTIST);
+}
+
+static void
+audio_playback_next_artist(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_PLAYBACK_NEXT_ARTIST);
+}
+
+static void
+audio_playback_toggle_repeat(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_MODE_TOGGLE_REPEAT);
+}
+
+static void
+audio_playback_toggle_shuffle(struct navit *this, char *function, struct attr **in, struct attr ***out, int *valid)
+{
+ audio_do_action(this,AUDIO_MODE_TOGGLE_SHUFFLE);
+}
+
+void
+audio_play_track(struct navit *this, int track_index)
+{
+ audio_playback_do(this,track_index);
+}
+
+GList *
+audio_get_actions(struct navit *this)
+{
+ GList *l;
+ GList *ret = NULL;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.actions && a->meth.actions != 0xffffffff ) {
+ dbg(lvl_info,"Found a actions method at %p in plugin %s\n", a->meth.actions, a->name);
+ GList * (*f)(struct audio_priv *this)=a->meth.actions+0;
+ dbg(lvl_info,"Relocating at %p \n", f);
+ ret=f(a->priv);
+ }
+ l=g_list_next(l);
+ }
+ dbg(lvl_info,"Actions method enumeration done\n");
+ return(ret);
+}
+
+GList *
+audio_get_playlists(struct navit *this)
+{
+ GList *l;
+ GList *ret = NULL;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.playlists && a->meth.playlists != 0xffffffff ) {
+ dbg(lvl_info,"Found a playlists method at %p in plugin %s\n", a->meth.playlists, a->name);
+ GList * (*f)(struct audio_priv *this)=a->meth.playlists+0;
+ dbg(lvl_info,"Relocating at %p \n", f);
+ ret=f(a->priv);
+ }
+ l=g_list_next(l);
+ }
+ dbg(lvl_info,"Playlists method enumeration done\n");
+ return(ret);
+}
+
+char *
+audio_get_current_playlist(struct navit *this)
+{
+ GList *l;
+ char *ret = 0;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.current_playlist && a->meth.current_playlist != 0xffffffff ) {
+ dbg(lvl_info,"Found a current_playlist method at %p in plugin %s\n", a->meth.current_playlist, a->name);
+ GList * (*f)(struct audio_priv *this)=a->meth.current_playlist;
+ ret=f(a->priv);
+ }
+ l=g_list_next(l);
+ }
+ dbg(lvl_info,"current_playlist method enumeration done\n");
+ if(ret)
+ return(ret);
+ else
+ return "";
+}
+
+char *
+audio_get_current_track(struct navit *this)
+{
+ GList *l;
+ char *ret = 0;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+ dbg(lvl_info,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.current_track && a->meth.current_track != 0xffffffff ) {
+ dbg(lvl_info,"Found a current_track method at %p in plugin %s\n", a->meth.current_track, a->name);
+ GList * (*f)(struct audio_priv *this)=a->meth.current_track+0;
+ dbg(lvl_info,"Relocating at %p \n", f);
+ ret=f(a->priv);
+ }
+ l=g_list_next(l);
+ }
+ dbg(lvl_info,"current_track method enumeration done %p\n", ret);
+ if(ret)
+ return(ret);
+ else
+ return "";
+}
+
+
+GList *
+audio_get_tracks(struct navit *this, const int playlist_index)
+{
+ GList *l;
+ GList *ret = NULL;
+ l=this->audio_plugins;
+ while(l) {
+ struct navit_audio_plugin * na=l->data;
+ struct audio *a=na->audio;
+
+ //dbg(lvl_error,"Went thru one plugin at %p with name %s\n", a, a->name);
+ if(a->meth.tracks && a->meth.tracks != 0xffffffff ) {
+ dbg(lvl_info,"Found a tracks method at %p in plugin %s\n", a->meth.tracks, a->name);
+ GList * (*f)(struct audio_priv *this, const int playlist_index)=a->meth.tracks+0;
+ dbg(lvl_info,"Relocating at %p \n", f);
+ ret=f(a->priv, playlist_index);
+ }
+ l=g_list_next(l);
+ }
+
+ dbg(lvl_info,"Playlists method enumeration done\n");
+ return(ret);
+}
+
static struct command_table commands[] = {
{"zoom_in",command_cast(navit_cmd_zoom_in)},
@@ -1448,6 +1735,20 @@ static struct command_table commands[] = {
{"set_attr_var",command_cast(navit_cmd_set_attr_var)},
{"get_attr_var",command_cast(navit_cmd_get_attr_var)},
{"switch_layout_day_night",command_cast(navit_cmd_switch_layout_day_night)},
+ {"volume_down",command_cast(audio_volume_down)},
+ {"volume_toggle",command_cast(audio_volume_toggle)},
+ {"volume_up",command_cast(audio_volume_up)},
+ {"audio_playback_previous",command_cast(audio_playback_previous)},
+ {"audio_playback_toggle",command_cast(audio_playback_toggle)},
+ {"audio_playback_play",command_cast(audio_playback_play)},
+ {"audio_playback_pause",command_cast(audio_playback_pause)},
+ {"audio_playback_next",command_cast(audio_playback_next)},
+ {"audio_playback_previous_playlist",command_cast(audio_playback_previous_playlist)},
+ {"audio_playback_next_playlist",command_cast(audio_playback_next_playlist)},
+ {"audio_playback_previous_artist",command_cast(audio_playback_previous_artist)},
+ {"audio_playback_next_artist",command_cast(audio_playback_next_artist)},
+ {"audio_playback_toggle_repeat",command_cast(audio_playback_toggle_repeat)},
+ {"audio_playback_toggle_shuffle",command_cast(audio_playback_toggle_shuffle)},
};
void navit_command_add_table(struct navit*this_, struct command_table *commands, int count) {
@@ -3063,6 +3364,9 @@ int navit_add_attr(struct navit *this_, struct attr *attr) {
case attr_script:
case attr_traffic:
break;
+ case attr_audio:
+ ret=navit_add_audio(this_, attr->u.audio);
+ break;
default:
return 0;
}
@@ -3256,6 +3560,45 @@ static void navit_vehicle_update_position(struct navit *this_, struct navit_vehi
profile(0,"return 5\n");
}
+
+/**
+ * @brief Called when a status attribute of a audio changes.
+ *
+ * This function is called when the {@code playing}, {@code shuffle} or {@code repeat}
+ * attribute of any configured audio changes.
+ *
+ * The function checks if {@code na} refers to the active audio and if {@code type} is one of the above types.
+ * If this is the case, it invokes the callback functions for {@code navit}'s respective attributes.
+ *
+ * Future actions that need to happen when one of these three attribute changes for any audio should be
+ * implemented here.
+ *
+ * @param this_ The navit object
+ * @param na The {@code navit_audio_plugin} which reported a new status attribute
+ * @param type The type of attribute with has changed
+ */
+static void
+navit_audio_update_status(struct navit *this_, struct navit_audio_plugin *na, enum attr_type type) {
+ /*
+ if (this_->audio != na){
+ dbg(lvl_error, "Wrong Plugin!\n");
+ //return;
+ }
+ */
+ switch(type) {
+ case attr_playing:
+ case attr_shuffle:
+ case attr_repeat:
+ callback_list_call_attr_2(this_->attr_cbl, type, this_, na->audio);
+ break;
+ default:
+ dbg(lvl_error, "Bad Audio Callback!\n");
+ return;
+ }
+}
+
+
+
/**
* @brief Called when a status attribute of a vehicle changes.
*
@@ -3354,6 +3697,38 @@ static void navit_set_vehicle(struct navit *this_, struct navit_vehicle *nv) {
}
/**
+ * @brief Registers a new audio plugin.
+ *
+ * @param this_ The navit instance
+ * @param a The audio to register
+ * @return True for success
+ */
+static int
+navit_add_audio(struct navit *this_, struct audio *a)
+{
+
+ dbg(lvl_debug,"starting navit_add_audio\n");
+ if(!a) return 0;
+ if(!this_) return 0;
+ struct navit_audio_plugin *na=g_new0(struct navit_audio_plugin, 1);
+ struct attr active;
+ na->audio=a;
+ na->active=0;
+ na->callback.type=attr_callback;
+ na->callback.u.callback=callback_new_attr_3(callback_cast(navit_audio_update_status), attr_playing, this_, na, attr_playing);
+ audio_add_attr(na->audio, &na->callback);
+ na->callback.u.callback=callback_new_attr_3(callback_cast(navit_audio_update_status), attr_shuffle, this_, na, attr_shuffle);
+ audio_add_attr(na->audio, &na->callback);
+ na->callback.u.callback=callback_new_attr_3(callback_cast(navit_audio_update_status), attr_repeat, this_, na, attr_repeat);
+ audio_add_attr(na->audio, &na->callback);
+ dbg(lvl_debug,"Adding one plugin at %p\n", a);
+ this_->audio_plugins=g_list_append(this_->audio_plugins, na);
+ audio_set_attr(na->audio, &this_->self);
+ return 1;
+}
+
+
+/**
* @brief Registers a new vehicle.
*
* @param this_ The navit instance
diff --git a/navit/plugin.h b/navit/plugin.h
index 5c9636aa2..05c390e33 100644
--- a/navit/plugin.h
+++ b/navit/plugin.h
@@ -51,6 +51,8 @@ enum plugin_category {
plugin_category_font,
/** Category for plugins which retrieve traffic information. */
plugin_category_traffic,
+ /** Category for plugins which play audio */
+ plugin_category_audio,
/** Dummy for last entry. */
plugin_category_last,
};
diff --git a/navit/plugin/j1850/j1850.c b/navit/plugin/j1850/j1850.c
index 08b9c3ff0..9ec608ba4 100644
--- a/navit/plugin/j1850/j1850.c
+++ b/navit/plugin/j1850/j1850.c
@@ -315,9 +315,6 @@ static void osd_j1850_init(struct j1850 *this, struct navit *nav) {
graphics_gc_set_foreground(this->white, &c);
graphics_gc_set_linewidth(this->white, this->width);
-
- graphics_gc_set_linewidth(this->osd_item.graphic_fg_white, this->width);
-
event_add_timeout(500, 1, callback_new_1(callback_cast(osd_j1850_draw), this));
j1850_init_serial_port(this);
diff --git a/navit/plugin_def.h b/navit/plugin_def.h
index 3140bddc3..d342340ab 100644
--- a/navit/plugin_def.h
+++ b/navit/plugin_def.h
@@ -30,5 +30,6 @@ PLUGIN_CATEGORY(osd, (struct navit *nav, struct osd_methods *meth, struct attr *
PLUGIN_CATEGORY(speech, (struct speech_methods *meth, struct attr **attrs, struct attr *parent))
PLUGIN_CATEGORY(vehicle, (struct vehicle_methods *meth, struct callback_list *cbl, struct attr **attrs))
PLUGIN_CATEGORY(event, (struct event_methods *meth))
+PLUGIN_CATEGORY(audio, (struct audio_methods *meth, struct callback_list * cbl, struct attr **attrs, struct attr *parent))
PLUGIN_CATEGORY(font, (void *meth))
PLUGIN_CATEGORY(traffic, (struct navit *nav, struct traffic_methods *meth, struct attr **attrs, struct callback_list *cbl))
diff --git a/navit/xmlconfig.c b/navit/xmlconfig.c
index 476997c2d..0a44a43cc 100644
--- a/navit/xmlconfig.c
+++ b/navit/xmlconfig.c
@@ -285,6 +285,8 @@ object_func_lookup(enum attr_type type) {
return &vehicle_func;
case attr_vehicleprofile:
return &vehicleprofile_func;
+ case attr_audio:
+ return &audio_func;
default:
for (i = 0 ; i < sizeof(object_funcs)/sizeof(struct object_func); i++) {
if (object_funcs[i].type == type)
@@ -334,7 +336,7 @@ static char *element_fixmes[]= {
};
static void initStatic(void) {
- elements=g_new0(struct element_func, 45); //44 is a number of elements + ending NULL element
+ elements=g_new0(struct element_func, 46); //45 is a number of elements + ending NULL element
elements[0].name="config";
elements[0].parent=NULL;
@@ -554,6 +556,11 @@ static void initStatic(void) {
elements[43].parent="navit";
elements[43].func=NULL;
elements[43].type=attr_traffic;
+
+ elements[44].name="audio";
+ elements[44].parent="navit";
+ elements[44].func=NULL;
+ elements[44].type=attr_audio;
}
/**
diff --git a/navit/xmlconfig.h b/navit/xmlconfig.h
index 56f118eb2..d1c544a3f 100644
--- a/navit/xmlconfig.h
+++ b/navit/xmlconfig.h
@@ -112,7 +112,7 @@ struct object_func {
* default behavior, can be NULL for some object types */
};
-extern struct object_func map_func, mapset_func, navit_func, osd_func, tracking_func, vehicle_func, maps_func, layout_func, roadprofile_func, vehicleprofile_func, layer_func, config_func, profile_option_func, script_func, log_func, speech_func, navigation_func, route_func, traffic_func;
+extern struct object_func map_func, mapset_func, navit_func, osd_func, tracking_func, vehicle_func, maps_func, layout_func, roadprofile_func, vehicleprofile_func, layer_func, config_func, profile_option_func, script_func, log_func, speech_func, navigation_func, route_func, traffic_func, audio_func;
#define HAS_OBJECT_FUNC(x) ((x) == attr_map || (x) == attr_mapset || (x) == attr_navit || (x) == attr_osd || (x) == attr_trackingo || (x) == attr_vehicle || (x) == attr_maps || (x) == attr_layout || (x) == attr_roadprofile || (x) == attr_vehicleprofile || (x) == attr_layer || (x) == attr_config || (x) == attr_profile_option || (x) == attr_script || (x) == attr_log || (x) == attr_speech || (x) == attr_navigation || (x) == attr_route)