mpris2-client: integrate basic voice commands
authorJanos Kovacs <jankovac503@gmail.com>
Thu, 6 Jun 2013 13:40:10 +0000 (16:40 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Thu, 6 Jun 2013 13:52:56 +0000 (16:52 +0300)
src/plugins/mpris2-client/clients.c
src/plugins/mpris2-client/clients.h
src/plugins/mpris2-client/dbusif.c
src/plugins/mpris2-client/dbusif.h
src/plugins/mpris2-client/mpris2-plugin.c
src/plugins/mpris2-client/mpris2-plugin.h

index 9aa949e..ad94440 100644 (file)
@@ -2,6 +2,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 
 #include <murphy/common/debug.h>
 
@@ -37,8 +38,12 @@ static uint64_t get_current_time(void);
 static char *commands[] = {
     "play music",
     "stop music",
+    "play louder",
+    "play quieter",
+    "play next",
+    "play previous",
     "show player",
-    "hide player",
+    "quit player",
     NULL
 };
 static int ncommand = (sizeof(commands) / sizeof(commands[0])) - 1;
@@ -46,25 +51,14 @@ static int ncommand = (sizeof(commands) / sizeof(commands[0])) - 1;
 int clients_create(context_t *ctx)
 {
     clients_t *clients;
-    srs_plugin_t *pl;
-    srs_context_t *srs;
-    srs_client_ops_t callbacks;
     mrp_htbl_config_t cfg;
 
-    if (!ctx || !(pl = ctx->plugin) || !(srs = pl->srs))
+    if (!ctx)
         return -1;
 
     if (!(clients = mrp_allocz(sizeof(clients_t))))
         return -1;
 
-    callbacks.notify_focus = notify_focus;
-    callbacks.notify_command = notify_command;
-
-    clients->srs_client = client_create(srs, SRS_CLIENT_TYPE_BUILTIN,
-                                        PLUGIN_NAME, "player",
-                                        commands, ncommand,
-                                        PLUGIN_NAME, &callbacks, NULL);
-
     memset(&cfg, 0, sizeof(cfg));
     cfg.nentry = 10;
     cfg.comp = mrp_string_comp;
@@ -101,14 +95,28 @@ void clients_destroy(context_t *ctx)
 
 int clients_start(context_t *ctx)
 {
+    srs_plugin_t *pl;
+    srs_context_t *srs;
     clients_t *clients;
     player_t *player;
+    srs_client_ops_t callbacks;
 
-    if (!ctx || !(clients = ctx->clients))
+    if (!ctx || !(pl = ctx->plugin) || !(srs = pl->srs) ||
+        !(clients = ctx->clients))
         return -1;
 
+    callbacks.notify_focus = notify_focus;
+    callbacks.notify_command = notify_command;
+
+    clients->srs_client = client_create(srs, SRS_CLIENT_TYPE_BUILTIN,
+                                        PLUGIN_NAME, "player",
+                                        commands, ncommand,
+                                        PLUGIN_NAME, &callbacks, ctx);
+
     mrp_htbl_foreach(clients->player.name, player_register, (void *)ctx);
 
+    client_request_focus(clients->srs_client, SRS_VOICE_FOCUS_SHARED);
+
     return 0;
 }
 
@@ -143,6 +151,11 @@ int clients_register_player(context_t *ctx,
                  player->name, player->service ? player->service : "none",
                  player->object ? player->object : "none");
 
+    if (!clients->current) {
+        clients->current = player;
+        mrp_log_info("'%s' become the default player", player->name);
+    }
+
     return 0;
 }
 
@@ -190,9 +203,6 @@ void clients_player_appeared(context_t *ctx,
             mrp_log_info("mrpis2 client '%s' appeared (address %s)",
                          name, address);
 
-            if (!clients->current)
-                clients->current = player;
-
             dbusif_query_player_properties(player);
         }
     }
@@ -224,10 +234,6 @@ void clients_player_disappeared(context_t *ctx, const char *name)
                 player->active_list = NULL;
 
                 mrp_log_info("mrpis2 client '%s' disappeared", name);
-
-                if (player == clients->current) {
-                    clients->current = NULL;
-                }
             }
         }
     }
@@ -249,6 +255,16 @@ void clients_player_status_changed(player_t *player, bool ready)
     }
 }
 
+void clients_player_volume_changed(player_t *player, double volume)
+{
+    if (player) {
+        if (volume < 0.00001)  volume = 0.00001;
+        else if (volume > 1.0) volume = 1.0;
+
+        player->volume = log10(volume) * 20.0;
+    }
+}
+
 void clients_playlist_changed(player_t *player, size_t nlist,playlist_t *lists)
 {
     context_t *ctx;
@@ -295,6 +311,55 @@ void clients_player_request_state(player_t *player, player_state_t state)
         dbusif_introspect_player(player);
 }
 
+void clients_player_request_track(player_t *player, track_t track)
+{
+    if (!player || (track != NEXT_TRACK && track != PREVIOUS_TRACK))
+        return;
+
+    if (!player->address)
+        clients_player_request_state(player, PLAY);
+    else {
+        if (player->state != PLAY)
+            dbusif_set_player_state(player, PLAY);
+
+        if (track == PREVIOUS_TRACK)
+            dbusif_change_track(player, PREVIOUS_TRACK);
+
+        dbusif_change_track(player, track);
+    }
+}
+
+void clients_player_adjust_volume(player_t *player, double adjust)
+{
+    double volume;
+
+    if (player && player->address) {
+        volume = pow(10, (player->volume + adjust) / 20.0);
+
+        if (volume < 0.0) volume = 0;
+        else if (volume > 1.0) volume = 1.0;
+
+        dbusif_set_player_property(player, "Volume", "d", &volume);
+    }
+}
+
+void clients_player_show(player_t *player)
+{
+    if (player) {
+        if (player->address)
+            dbusif_raise_player(player);
+        else
+            /* this supposed to launch the player */
+            dbusif_introspect_player(player);
+    }
+}
+
+void clients_player_quit(player_t *player)
+{
+    if (player && player->address)
+        dbusif_quit_player(player);
+}
+
 static int notify_focus(srs_client_t *srs_client, srs_voice_focus_t focus)
 {
     return TRUE;
@@ -302,6 +367,46 @@ static int notify_focus(srs_client_t *srs_client, srs_voice_focus_t focus)
 
 static int notify_command(srs_client_t *srs_client, int ntoken, char **tokens)
 {
+    context_t *ctx;
+    clients_t *clients;
+    player_t *player;
+    char cmd[2048];
+    char *e, *p, *sep;
+    int i;
+
+    if (!srs_client || !(ctx = srs_client->user_data) ||
+        !(clients = ctx->clients))
+        return FALSE;
+
+    e = (p = cmd) + (sizeof(cmd) - 1);
+
+    for (i = 0, sep = "", *p = 0;   i < ntoken && p < e;   i++, sep = " ")
+        p += snprintf(p, e-p, "%s%s", sep, tokens[i]);
+
+    if (!(player = clients->current)) {
+        mrp_log_info("no player to execute command '%s'", cmd);
+        return FALSE;
+    }
+
+    mrp_log_info("Mpris2 client got command '%s'\n", cmd);
+
+    if (!strcmp(cmd, "play music"))
+        clients_player_request_state(player, PLAY);
+    else if (!strcmp(cmd, "stop music"))
+        clients_player_request_state(player, PAUSE);
+    else if (!strcmp(cmd, "play next"))
+        clients_player_request_track(player, NEXT_TRACK);
+    else if (!strcmp(cmd, "play previous"))
+        clients_player_request_track(player, PREVIOUS_TRACK);
+    else if (!strcmp(cmd, "play louder"))
+        clients_player_adjust_volume(player, +2);
+    else if (!strcmp(cmd, "play quieter"))
+        clients_player_adjust_volume(player, -2);
+    else if (!strcmp(cmd, "show player"))
+        clients_player_show(player);
+    else if (!strcmp(cmd, "quit player"))
+        clients_player_quit(player);
+
     return TRUE;
 }
 
index a2bce01..d585780 100644 (file)
@@ -25,6 +25,7 @@ struct player_s {
     size_t nlist;
     playlist_t *lists;
     playlist_t *active_list;
+    double volume;
     pa_time_event *timer;
 };
 
@@ -33,6 +34,12 @@ struct playlist_s {
     const char *name;
 };
 
+enum track_e {
+    TRACK_UNKNOWN = 0,
+    NEXT_TRACK,
+    PREVIOUS_TRACK
+};
+
 
 int  clients_create(context_t *ctx);
 void clients_destroy(context_t *ctx);
@@ -51,9 +58,16 @@ void clients_player_appeared(context_t *ctx, const char *name,
 void clients_player_disappeared(context_t *ctx, const char *name);
 void clients_player_state_changed(player_t *player, player_state_t state);
 void clients_player_status_changed(player_t *player, bool ready);
+void clients_player_volume_changed(player_t *player, double volume);
 void clients_playlist_changed(player_t *player,size_t nlist,playlist_t *lists);
 
 void clients_player_request_state(player_t *player, player_state_t state);
+void clients_player_request_track(player_t *player, track_t track);
+
+void clients_player_adjust_volume(player_t *player, double adjust);
+
+void clients_player_show(player_t *player);
+void clients_player_quit(player_t *player);
 
 
 #endif /* __SRS_MPRIS2_CLIENT_H__ */
index 44edd90..02333aa 100644 (file)
@@ -13,7 +13,7 @@
 #define MAKE_DBUS_VERSION(major, minor, patch)  \
     (((major) << 16) | ((minor) << 8) | (patch))
 
-#if DBUS_VERSION < MAKE_DBUS_VERSION(1, 7, 0)
+#if DBUS_VERSION < MAKE_DBUS_VERSION(1, 6, 8)
 /* For old versions, we define DBusBasicValue with the member we use... */
 typedef union {
     char        *str;
@@ -134,6 +134,44 @@ void dbusif_query_player_properties(player_t *player)
     dbus_message_unref(msg);
 }
 
+void dbusif_set_player_property(player_t *player,
+                                const char *name,
+                                const char *type,
+                                void *value)
+{
+    const char *interface = "org.mpris.MediaPlayer2.Player";
+
+    context_t *ctx;
+    dbusif_t *dbusif;
+    DBusMessage *msg;
+    DBusMessageIter mit;
+    DBusMessageIter vit;
+    int success;
+
+    if (!player || !player->name || !player->address ||
+        !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+        return;
+
+    msg = dbus_message_new_method_call(player->address,
+                                       "/org/mpris/MediaPlayer2",
+                                       "org.freedesktop.DBus.Properties",
+                                       "Set");
+
+    if (!msg)
+        return;
+
+    dbus_message_iter_init_append(msg, &mit);
+    dbus_message_iter_append_basic(&mit, DBUS_TYPE_STRING, &interface);
+    dbus_message_iter_append_basic(&mit, DBUS_TYPE_STRING, &name);
+    dbus_message_iter_open_container(&mit, DBUS_TYPE_VARIANT, type, &vit);
+    dbus_message_iter_append_basic(&vit, type[0], value);
+    dbus_message_iter_close_container(&mit, &vit);
+
+    mrp_dbus_send_msg(dbusif->dbus, msg);
+
+    dbus_message_unref(msg);
+}
+
 void dbusif_introspect_player(player_t *player)
 {
     context_t *ctx;
@@ -167,8 +205,6 @@ void dbusif_set_player_state(player_t *player, player_state_t state)
     DBusMessage *msg;
     const char *member;
 
-    printf("address: %s\n", player->address ? player->address : "unknown");
-
     if (!player || !player->address ||
         !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
         return;
@@ -180,8 +216,32 @@ void dbusif_set_player_state(player_t *player, player_state_t state)
     default:                                                            return;
     }
 
-    printf("member: %s\n", member);
+    msg = dbus_message_new_method_call(player->address,
+                                       "/org/mpris/MediaPlayer2",
+                                       "org.mpris.MediaPlayer2.Player",
+                                       member);
+    if (msg) {
+        mrp_dbus_send_msg(dbusif->dbus, msg);
+        dbus_message_unref(msg);
+    }
+}
 
+void dbusif_change_track(player_t *player, track_t track)
+{
+    context_t *ctx;
+    dbusif_t *dbusif;
+    DBusMessage *msg;
+    const char *member;
+
+    if (!player || !player->address ||
+        !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+        return;
+
+    switch (track) {
+    case NEXT_TRACK:       member = "Next";         break;
+    case PREVIOUS_TRACK:   member = "Previous";     break;
+    default:                                        return;
+    }
 
     msg = dbus_message_new_method_call(player->address,
                                        "/org/mpris/MediaPlayer2",
@@ -265,6 +325,48 @@ void dbusif_query_playlists(player_t *player)
     dbus_message_unref(msg);
 }
 
+void dbusif_raise_player(player_t *player)
+{
+    context_t *ctx;
+    dbusif_t *dbusif;
+    DBusMessage *msg;
+    const char *member;
+
+    if (!player || !player->address ||
+        !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+        return;
+
+    msg = dbus_message_new_method_call(player->address,
+                                       "/org/mpris/MediaPlayer2",
+                                       "org.mpris.MediaPlayer2",
+                                       "Raise");
+    if (msg) {
+        mrp_dbus_send_msg(dbusif->dbus, msg);
+        dbus_message_unref(msg);
+    }
+}
+
+void dbusif_quit_player(player_t *player)
+{
+    context_t *ctx;
+    dbusif_t *dbusif;
+    DBusMessage *msg;
+    const char *member;
+
+    if (!player || !player->address ||
+        !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+        return;
+
+    msg = dbus_message_new_method_call(player->address,
+                                       "/org/mpris/MediaPlayer2",
+                                       "org.mpris.MediaPlayer2",
+                                       "Quit");
+    if (msg) {
+        mrp_dbus_send_msg(dbusif->dbus, msg);
+        dbus_message_unref(msg);
+    }
+}
+
 static void name_follow_cb(mrp_dbus_t *dbus,
                            const char *dbus_name,
                            int error,
@@ -454,16 +556,17 @@ static int parse_properties(context_t *ctx,
                 else
                     state = UNKNOWN;
 
-                printf("*** state %d\n", state);
+                // printf("*** state %d\n", state);
 
                 if (state != UNKNOWN)
                     clients_player_state_changed(player, state);
             }
             else if (!strcmp(prop, "Volume") && type == DBUS_TYPE_DOUBLE) {
                 printf("*** volume %.4lf\n", value.dbl);
+                clients_player_volume_changed(player, value.dbl);
             }
             else if (!strcmp(prop, "CanPlay") && type == DBUS_TYPE_BOOLEAN) {
-                printf("*** %s play\n", value.bool_val ? "can" : "unable to");
+                //printf("*** %s play\n", value.bool_val ? "can":"unable to");
                 clients_player_status_changed(player, value.bool_val);
             }
         }
index 13d6bb0..f154c6f 100644 (file)
@@ -15,13 +15,19 @@ int  dbusif_register_player(context_t *ctx, const char *name);
 void dbusif_unregister_player(context_t *ctx, const char *name);
 
 void dbusif_query_player_properties(player_t *player);
+void dbusif_set_player_property(player_t *player, const char *name,
+                                const char *type, void *value);
 void dbusif_introspect_player(player_t *player);
 
 void dbusif_set_player_state(player_t *player, player_state_t state);
+void dbusif_change_track(player_t *player, track_t track);
 
 void dbusif_query_playlists(player_t *player);
 void dbusif_set_playlist(player_t *player, const char *id);
 
+void dbusif_raise_player(player_t *player);
+void dbusif_quit_player(player_t *player);
+
 
 #endif /* __SRS_MPRIS2_DBUS_INTERFACE_H__ */
 
index eb718f1..d068c28 100644 (file)
 #define PLUGIN_VERSION     "0.0.1"
 
 
-/*************************************************************/
-pa_io_event *ioev;
-
-static void handle_input(pa_mainloop_api *api, pa_io_event *e, int fd,
-                         pa_io_event_flags_t events, void *ud)
-{
-    context_t *ctx = (context_t *)ud;
-    player_t *player;
-    int cnt;
-    char cmd;
-
-    for (;;) {
-        if ((cnt = read(0, &cmd, 1)) != 1) {
-            if (errno == EINTR)
-                continue;
-            return;
-        }
-
-        if (!(player = clients_find_player_by_name(ctx, "rhythmbox"))) {
-            printf("can't find rhythmbox player\n");
-            return;
-        }
-
-        switch (cmd) {
-        case 'p':   clients_player_request_state(player, PLAY);   break;
-        case 's':   clients_player_request_state(player, PAUSE);  break;
-        case 'e':   clients_player_request_state(player, STOP);   break;
-        case 'a':   printf("Show\n");                             break; 
-        default:                                                  break;
-        }
-
-        return;
-    }
-}
-
-static void input_create(context_t *ctx)
-{
-    srs_plugin_t *pl = ctx->plugin;
-    srs_context_t *srs = pl->srs;
-    pa_mainloop_api *api = pa_mainloop_get_api(srs->pa);
-
-    ioev = api->io_new(api, 0, PA_IO_EVENT_INPUT, handle_input, ctx);
-}
-
-/*************************************************************/
-
-
 
 static int create_mpris2(srs_plugin_t *plugin)
 {
@@ -185,8 +138,6 @@ static int start_mpris2(srs_plugin_t *plugin)
 
     clients_start(ctx);
 
-    // input_create(ctx);
-
     return TRUE;
 }
 
index fa0ff7e..a9052e5 100644 (file)
@@ -11,6 +11,7 @@
 #define MPRIS2_PREFIX  "mpris2."
 
 typedef enum   player_state_e  player_state_t;
+typedef enum   track_e         track_t;
 
 typedef struct context_s       context_t;
 typedef struct dbusif_s        dbusif_t;