Audio plugin: adapt to new API, re-enable build
[src/app-framework-binder.git] / plugins / audio / audio-api.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Manuel Bachmann"
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #define _GNU_SOURCE
20 #include <stdlib.h>
21
22 #include "audio-api.h"
23 #include "audio-alsa.h"
24
25 #include "afb-plugin.h"
26 #include "afb-req-itf.h"
27
28 /* ------ BACKEND FUNCTIONS ------- */
29
30 void _backend_init (const char *name, audioCtxHandleT *ctx) {
31
32     char *backend_env = getenv ("AFB_AUDIO_OUTPUT");
33     unsigned char res = 0;
34
35 # ifdef HAVE_PULSE
36     if (!backend_env || (strcasecmp (backend_env, "Pulse") == 0))
37         res = _pulse_init (name, ctx);
38     if (!res)
39 #endif
40     res = _alsa_init (name, ctx);
41
42     if (!res && verbose)
43         fprintf (stderr, "Could not initialize Audio backend\n");
44 }
45
46 void _backend_free (audioCtxHandleT *ctx) {
47
48 # ifdef HAVE_PULSE
49     if (ctx->audio_dev) _pulse_free (ctx); else
50 # endif
51     _alsa_free (ctx->name);
52 }
53
54 void _backend_play (audioCtxHandleT *ctx) {
55
56 # ifdef HAVE_PULSE
57     if (ctx->audio_dev) _pulse_play (ctx); else
58 # endif
59     _alsa_play (ctx->idx);
60 }
61
62 void _backend_stop (audioCtxHandleT *ctx) {
63
64 # ifdef HAVE_PULSE
65     if (ctx->audio_dev) _pulse_stop (ctx); else
66 # endif
67     _alsa_stop (ctx->idx);
68 }
69
70 unsigned int _backend_get_volume (audioCtxHandleT *ctx, unsigned int channel) {
71
72 # ifdef HAVE_PULSE
73     if (ctx->audio_dev) return _pulse_get_volume (ctx, channel); else
74 # endif
75     return _alsa_get_volume (ctx->idx, channel);
76 }
77
78 void _backend_set_volume (audioCtxHandleT *ctx, unsigned int channel, unsigned int vol) {
79
80 # ifdef HAVE_PULSE
81     if (ctx->audio_dev) _pulse_set_volume (ctx, channel, vol); else
82 # endif
83     _alsa_set_volume (ctx->idx, channel, vol);
84 }
85
86 void _backend_set_volume_all (audioCtxHandleT *ctx, unsigned int vol) {
87
88 # ifdef HAVE_PULSE
89     if (ctx->audio_dev) _pulse_set_volume_all (ctx, vol); else
90 # endif
91     _alsa_set_volume_all (ctx->idx, vol);
92 }
93
94 unsigned char _backend_get_mute (audioCtxHandleT *ctx) {
95
96 # ifdef HAVE_PULSE
97     if (ctx->audio_dev) return _pulse_get_mute (ctx); else
98 # endif
99     return _alsa_get_mute (ctx->idx);
100 }
101
102 void _backend_set_mute (audioCtxHandleT *ctx, unsigned char mute) {
103
104 # ifdef HAVE_PULSE
105     if (ctx->audio_dev) _pulse_set_mute (ctx, mute); else
106 # endif
107     _alsa_set_mute (ctx->idx, mute);
108 }
109
110 void _backend_set_channels (audioCtxHandleT *ctx, unsigned int channels) {
111
112 # ifdef HAVE_PULSE
113     if (ctx->audio_dev) return; else
114 # endif
115     _alsa_set_channels (ctx->idx, channels);
116 }
117
118 /* ------ LOCAL HELPER FUNCTIONS --------- */
119
120 /* private client context creation ; default values */
121 STATIC audioCtxHandleT* initAudioCtx () {
122
123     audioCtxHandleT *ctx;
124     int i;
125
126     ctx = malloc (sizeof(audioCtxHandleT));
127     ctx->audio_dev = NULL;
128     ctx->idx = -1;
129     for (i = 0; i < 8; i++)
130         ctx->volume[i] = 25;
131     ctx->channels = 2;
132     ctx->mute = 0;
133     ctx->is_playing = 0;
134
135     return ctx;
136 }
137
138 STATIC AFB_error releaseAudio (audioCtxHandleT *ctx) {
139
140     /* power it off */
141     _backend_free (ctx);
142
143     /* clean client context */
144     ctx->idx = -1;
145
146     return AFB_SUCCESS;
147 }
148
149 /* called when client session dies [e.g. client quits for more than 15mns] */
150 STATIC void freeAudio (void *context) {
151     free (context);    
152 }
153
154
155 /* ------ PUBLIC PLUGIN FUNCTIONS --------- */
156
157 STATIC void init (struct afb_req request) {        /* AFB_SESSION_CHECK */
158
159     json_object *jresp;
160
161     /* create a private client context */
162     if (!request.context)
163         request.context = initAudioCtx();
164
165     _backend_init("default", request.context);
166
167     jresp = json_object_new_object();
168     json_object_object_add (jresp, "info", json_object_new_string ("Audio initialized"));
169
170     afb_req_success (request, jresp, "Audio initiliazed");
171 }
172
173 STATIC void volume (struct afb_req request) {      /* AFB_SESSION_CHECK */
174
175     audioCtxHandleT *ctx = (audioCtxHandleT*)request.context;
176     const char *value = afb_req_argument (request, "value");
177     json_object *jresp;
178     unsigned int volume[8], i;
179     char *volume_i;
180     char volume_str[256];
181     size_t len_str = 0;
182
183     /* no "?value=" parameter : return current state */
184     if (!value) {
185         for (i = 0; i < 8; i++) {
186             ctx->volume[i] = _backend_get_volume (ctx, i);
187             snprintf (volume_str+len_str, sizeof(volume_str)-len_str, "%d,", ctx->volume[i]);
188             len_str = strlen(volume_str);
189         }
190         jresp = json_object_new_object();
191         json_object_object_add (jresp, "volume", json_object_new_string(volume_str));
192     }
193
194     /* "?value=" parameter, set volume */
195     else {
196         volume_i = strdup (value);
197         volume_i = strtok (volume_i, ",");
198         volume[0] = (unsigned int) atoi (volume_i);
199
200         if (100 < volume[0]) {
201             free (volume_i);
202             //request.errcode = MHD_HTTP_SERVICE_UNAVAILABLE;
203             afb_req_fail (request, "Failed", "Volume must be between 0 and 100");
204             return;
205         }
206         ctx->volume[0] = volume[0];
207         _backend_set_volume (ctx, 0, ctx->volume[0]);
208         snprintf (volume_str, sizeof(volume_str), "%d,", ctx->volume[0]);
209
210         for (i = 1; i < 8; i++) {
211             volume_i = strtok (NULL, ",");
212             /* if there is only one value, set all channels to this one */
213             if (!volume_i && i == 1)
214                _backend_set_volume_all (ctx, ctx->volume[0]);
215             if (!volume_i || 100 < atoi(volume_i) || atoi(volume_i) < 0) {
216                ctx->volume[i] = _backend_get_volume (ctx, i);
217             } else {
218                ctx->volume[i] = (unsigned int) atoi(volume_i);
219                _backend_set_volume (ctx, i, ctx->volume[i]);
220             }
221             len_str = strlen(volume_str);
222             snprintf (volume_str+len_str, sizeof(volume_str)-len_str, "%d,", ctx->volume[i]);
223         }
224         jresp = json_object_new_object();
225         json_object_object_add (jresp, "volume", json_object_new_string(volume_str));
226     }
227
228     afb_req_success (request, jresp, "Audio - Volume changed");
229 }
230
231 STATIC void channels (struct afb_req request) {    /* AFB_SESSION_CHECK */
232
233     audioCtxHandleT *ctx = (audioCtxHandleT*)request.context;
234     const char *value = afb_req_argument (request, "value");
235     json_object *jresp = json_object_new_object();
236     char channels_str[256];
237
238     /* no "?value=" parameter : return current state */
239     if (!value) {
240         snprintf (channels_str, sizeof(channels_str), "%d", ctx->channels);
241         json_object_object_add (jresp, "channels", json_object_new_string (channels_str));
242     }
243
244     /* "?value=" parameter, set channels */
245     else {
246         ctx->channels = (unsigned int) atoi (value);
247         _backend_set_channels (ctx, ctx->channels);
248
249         snprintf (channels_str, sizeof(channels_str), "%d", ctx->channels);
250         json_object_object_add (jresp, "channels", json_object_new_string (channels_str));
251     }
252
253     afb_req_success (request, jresp, "Audio - Channels set");
254 }
255
256 STATIC void mute (struct afb_req request) {        /* AFB_SESSION_CHECK */
257
258     audioCtxHandleT *ctx = (audioCtxHandleT*)request.context;
259     const char *value = afb_req_argument (request, "value");
260     json_object *jresp = json_object_new_object();
261
262     /* no "?value=" parameter : return current state */
263     if (!value) {
264         ctx->mute = _backend_get_mute (ctx);
265         ctx->mute ?
266             json_object_object_add (jresp, "mute", json_object_new_string ("on"))
267           : json_object_object_add (jresp, "mute", json_object_new_string ("off"));
268     }
269
270     /* "?value=" parameter is "1" or "true" */
271     else if ( atoi(value) == 1 || !strcasecmp(value, "true") ) {
272         ctx->mute = 1;
273         _backend_set_mute (ctx, ctx->mute);
274
275         json_object_object_add (jresp, "mute", json_object_new_string ("on"));
276     }
277
278     /* "?value=" parameter is "0" or "false" */
279     else if ( atoi(value) == 0 || !strcasecmp(value, "false") ) {
280         ctx->mute = 0;
281         _backend_set_mute (ctx, ctx->mute);
282
283         json_object_object_add (jresp, "mute", json_object_new_string ("off"));
284     }
285
286     afb_req_success (request, jresp, "Audio - Mute set");
287 }
288
289 STATIC void play (struct afb_req request) {        /* AFB_SESSION_CHECK */
290
291     audioCtxHandleT *ctx = (audioCtxHandleT*)request.context;
292     const char *value = afb_req_argument (request, "value");
293     json_object *jresp = json_object_new_object();
294
295     /* no "?value=" parameter : return current state */
296     if (!value) {
297         ctx->is_playing ?
298             json_object_object_add (jresp, "play", json_object_new_string ("on"))
299           : json_object_object_add (jresp, "play", json_object_new_string ("off"));
300     }
301
302     /* "?value=" parameter is "1" or "true" */
303     else if ( atoi(value) == 1 || !strcasecmp(value, "true") ) {
304         ctx->is_playing = 1;
305         _backend_play (ctx);
306
307         json_object_object_add (jresp, "play", json_object_new_string ("on"));
308     }
309
310     /* "?value=" parameter is "0" or "false" */
311     else if ( atoi(value) == 0 || !strcasecmp(value, "false") ) {
312         ctx->is_playing = 0;
313         _backend_stop (ctx);
314
315         json_object_object_add (jresp, "play", json_object_new_string ("off"));
316     }
317
318     afb_req_success (request, jresp, "Audio - Play");
319 }
320
321 STATIC void ping (struct afb_req request) {         /* AFB_SESSION_NONE */
322     afb_req_success (request, NULL, "Audio - Ping success");
323 }
324
325 STATIC const struct AFB_restapi pluginApis[]= {
326   {"init"    , AFB_SESSION_CHECK,  init      , "Audio API - init"},
327   {"volume"  , AFB_SESSION_CHECK,  volume    , "Audio API - volume"},
328   {"channels", AFB_SESSION_CHECK,  channels  , "Audio API - channels"},
329   {"mute"    , AFB_SESSION_CHECK,  mute      , "Audio API - mute"},
330   {"play"    , AFB_SESSION_CHECK,  play      , "Audio API - play"},
331   {"ping"    , AFB_SESSION_NONE,   ping      , "Audio API - ping"},
332   {NULL}
333 };
334
335 STATIC const struct AFB_plugin plug_desc = {
336     .type   = AFB_PLUGIN_JSON,
337     .info   = "Application Framework Binder - Audio plugin",
338     .prefix = "audio",
339     .apis   = pluginApis
340 };