1 /**********************************************************
2  *
3  * mp3splt-gtk -- utility based on mp3splt,
4  *                for mp3/ogg splitting without decoding
5  *
6  * Copyright: (C) 2005-2014 Alexandru Munteanu
7  * Contact: m@ioalex.net
8  *
9  * http://mp3splt.sourceforge.net/
10  *
11  *********************************************************/
12 
13 /**********************************************************
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
28  * USA.
29  *
30  *********************************************************/
31 
32 /*!********************************************************
33  * \file
34  * Snackamp control
35  *
36  * this file contains functions to control the snackamp
37  * player
38  **********************************************************/
39 
40 #define _WIN32_WINNT 0x0501
41 
42 #include "snackamp_control.h"
43 
44 /*!connecting to the player to the port port
45 
46 Might possibley return an error
47 */
connect_snackamp(gint port,ui_state * ui)48 gint connect_snackamp(gint port, ui_state *ui)
49 {
50   gint return_err = 0;
51 
52 #ifdef __WIN32__
53   long winsockinit;
54   WSADATA winsock;
55   winsockinit = WSAStartup(0x0101,&winsock);
56 #endif
57 
58   struct addrinfo hints;
59   memset(&hints, 0, sizeof(struct addrinfo));
60   hints.ai_family = AF_UNSPEC;
61   hints.ai_socktype = SOCK_STREAM;
62 
63   struct addrinfo *result;
64 
65   char *port_as_string = alloca(16);
66   snprintf(port_as_string, 16, "%d", port);
67 
68   int return_code = getaddrinfo("localhost", port_as_string, &hints, &result);
69   if (return_code != 0) {
70     return_err = 1;
71   }
72 
73   if (return_err == 0)
74   {
75     struct addrinfo *result_p;
76     for (result_p = result; result_p != NULL; result_p = result_p->ai_next)
77     {
78       return_err = 0;
79 
80       ui->pi->socket_id = socket(result_p->ai_family, result_p->ai_socktype, result_p->ai_protocol);
81       if (ui->pi->socket_id == -1)
82       {
83         return_err = 2;
84         continue;
85       }
86 
87       if (connect(ui->pi->socket_id, result_p->ai_addr, result_p->ai_addrlen) != -1)
88       {
89         break;
90       }
91 
92       return_err = 3;
93       close(ui->pi->socket_id);
94     }
95 
96     freeaddrinfo(result);
97   }
98 
99   if (return_err == 0)
100   {
101 #ifdef __WIN32__
102 #else
103     if (NULL == (ui->pi->in = fdopen(ui->pi->socket_id, "r")) ||
104         NULL == (ui->pi->out = fdopen(ui->pi->socket_id, "w")))
105     {
106       return_err = 4;
107     }
108 #endif
109   }
110 
111   if (return_err == 0)
112   {
113     setvbuf(ui->pi->out, NULL, _IOLBF, 0);
114     ui->pi->connected = TRUE;
115   }
116 
117   if (return_err >= 2)
118   {
119     disconnect_snackamp(ui);
120   }
121 
122   return return_err;
123 }
124 
125 /*gint connect_snackamp(gint port, ui_state *ui)
126 {
127   struct sockaddr_in host;
128   struct hostent *h;
129   gint return_err = 0;
130 #ifdef __WIN32__
131   long winsockinit;
132   WSADATA winsock;
133   winsockinit = WSAStartup(0x0101,&winsock);
134 #endif
135   if((h = gethostbyname("localhost"))==NULL)
136   {
137     return_err = 1;
138   }
139   host.sin_family = AF_INET;
140   host.sin_addr.s_addr = ((struct in_addr *) (h->h_addr)) ->s_addr;
141   host.sin_port=htons(port);
142   if (return_err == 0)
143   {
144     if ((ui->pi->socket_id = socket(AF_INET, SOCK_STREAM, 0))==-1)
145     {
146       return_err = 2;
147     }
148   }
149   if (return_err == 0)
150   {
151     if ((connect(ui->pi->socket_id, (void *)&host, sizeof(host)))==-1)
152     {
153       return_err = 3;
154     }
155   }
156   if (return_err == 0)
157   {
158 #ifdef __WIN32__
159 #else
160     if (NULL == (ui->pi->in = fdopen(ui->pi->socket_id, "r")) ||
161       NULL == (ui->pi->out = fdopen(ui->pi->socket_id, "w")))
162     {
163       return_err = 4;
164     }
165 #endif
166   }
167   if (return_err == 0)
168   {
169     setvbuf(ui->pi->out, NULL, _IOLBF, 0);
170     ui->pi->connected = TRUE;
171   }
172   if (return_err >= 2)
173   {
174     disconnect_snackamp(ui);
175   }
176   return return_err;
177 }*/
178 
cut_begin_end(gchar * result)179 static gchar *cut_begin_end(gchar *result)
180 {
181   if (strchr(result,' ') != NULL)
182   {
183     gchar *test = strchr(result, ' ');
184     g_snprintf(result, strlen(result), "%s",test+1);
185   }
186 
187   //cut the \n at the end
188   gint result_str = strlen(result);
189   if (result_str >= 2)
190   {
191     result[result_str - 2] = '\0';
192   }
193 
194   return result;
195 }
196 
197 /*! disconnecting with the player
198 possibly returns an error
199 */
disconnect_snackamp(ui_state * ui)200 gint disconnect_snackamp(ui_state *ui)
201 {
202   ui->pi->connected = FALSE;
203 #ifdef __WIN32__
204   gint result = closesocket(ui->pi->socket_id);
205   WSACleanup();
206   return result;
207 #else
208   return close(ui->pi->socket_id);
209 #endif
210 }
211 
212 /*! send a message to snackamp through the socket interface
213 
214 \return the result; must be g_freed after use
215 \todo rewrite this function
216 */
snackamp_socket_send_message(gchar * message,ui_state * ui)217 static gchar *snackamp_socket_send_message(gchar *message, ui_state *ui)
218 {
219   gchar *result = malloc(1024 * sizeof(gchar *));
220   strcpy(result,"disconnected");
221 
222 #ifdef __WIN32__
223   gboolean r = TRUE;
224 
225   gint err1 = send(ui->pi->socket_id, message, strlen(message), 0);
226   if (err1 <= 0)
227   {
228     disconnect_snackamp(ui);
229   }
230   else
231   {
232     gint err = recv(ui->pi->socket_id, result, 1024, 0);
233     if (err <= 0)
234     {
235       disconnect_snackamp(ui);
236       r = FALSE;
237     }
238   }
239 #else
240   fputs(message, ui->pi->out);
241   fgets(result, 1024, ui->pi->in);
242 #endif
243 
244   //if on win32 we put the \0 when we find \n because no line buffering
245 #ifdef __WIN32__
246   if (r)
247   {
248     if (strchr(result,'\n') != NULL)
249     {
250       gchar *line_end;
251       line_end = strchr(result,'\n') + 1;
252       *line_end = '\0';
253     }
254   }
255 #endif
256 
257   return result;
258 }
259 
260 //!gets an integer from the string
get_integer_from_string(gchar * result)261 static gint get_integer_from_string(gchar *result)
262 {
263   gint our_integer = 0;
264   gint i = 0;
265   gchar *number = NULL;
266   while ((isdigit(result[i])==0) && (result[i]!='\0') && (result[i]!='-'))
267   {
268     i++;
269     number = result + i;
270   }
271   if (! (number == (result + strlen(result))))
272   {
273     our_integer = atoi (number);
274   }
275 
276   return our_integer;
277 }
278 
279 //!Test if we are connected to snackamp
snackamp_is_connected(ui_state * ui)280 static gboolean snackamp_is_connected(ui_state *ui)
281 {
282   return ui->pi->connected;
283 }
284 
285 //!gets informations about the song
snackamp_get_song_infos(gchar * total_infos,ui_state * ui)286 void snackamp_get_song_infos(gchar *total_infos, ui_state *ui)
287 {
288   gchar *result = snackamp_socket_send_message("xmms_remote_get_info\n", ui);
289   result = cut_begin_end(result);
290 
291   //stereo/mono
292   char *a = strstr(result, " ");
293   if (a == NULL)
294   {
295     g_snprintf(total_infos, 512, "disconnected");
296     g_free(result);
297     return;
298   }
299 
300   gchar rate_str[32] = { '\0' };
301   gchar freq_str[32] = { '\0' };
302   gchar nch_str[32] = { '\0' };
303   gchar *ptr = NULL;
304 
305   if (strstr(a+1, " ") != NULL)
306   {
307     ptr = strstr(a+1, " ") + 1;
308   }
309 
310   //rate
311   gint i = 0;
312   while (result[i] != ' ' && isdigit(result[i]) && i < 16)
313   {
314     g_sprintf(rate_str, "%s%c",rate_str,result[i]);
315     i++;
316   }
317 
318   //cut the beginning
319   if (strchr(result, ' ') != NULL)
320   {
321     gchar *test = strchr(result,' ');
322     g_snprintf(result, strlen(result), "%s",test+1);
323   }
324 
325   //freq
326   i = 0;
327   while (result[i] != ' ' && isdigit(result[i]) && i < 16)
328   {
329     g_sprintf(freq_str, "%s%c",freq_str,result[i]);
330     i++;
331   }
332 
333   //channels int
334   gint nch = atoi(ptr);
335   if (nch == 2)
336   {
337     snprintf(nch_str, 32, "%s", _("stereo"));
338   }
339   else
340   {
341     snprintf(nch_str, 32, "%s", _("mono"));
342   }
343 
344   gchar *_Kbps = _("Kbps");
345   gchar *_Khz = _("Khz");
346   g_snprintf(total_infos, 512, "%s %s     %s %s    %s", rate_str, _Kbps, freq_str, _Khz, nch_str);
347 
348   g_free(result);
349 }
350 
351 //!returns current song position in the playlist
snackamp_get_playlist_pos(ui_state * ui)352 static gint snackamp_get_playlist_pos(ui_state *ui)
353 {
354   gchar *result = snackamp_socket_send_message("xmms_remote_get_playlist_pos\n", ui);
355   gint number = get_integer_from_string(result);
356   g_free(result);
357   return number;
358 }
359 
360 /*!returns the filename
361 
362 The result of this query must be freed after use.
363 */
snackamp_get_filename(ui_state * ui)364 gchar *snackamp_get_filename(ui_state *ui)
365 {
366   gint playlist_pos = snackamp_get_playlist_pos(ui);
367 
368   //we get the current file
369   gchar temp[100];
370   g_snprintf(temp, 100, "%s %d\n", "xmms_remote_get_playlist_file", playlist_pos);
371 
372   gchar *result = snackamp_socket_send_message(temp, ui);
373   result = cut_begin_end(result);
374 
375   return result;
376 }
377 
378 //!stops playing a song
snackamp_stop(ui_state * ui)379 void snackamp_stop(ui_state *ui)
380 {
381   gchar *result = snackamp_socket_send_message("xmms_remote_stop\n", ui);
382   g_free(result);
383 }
384 
385 //!returns the number of songs of the playlist
snackamp_get_playlist_number(ui_state * ui)386 gint snackamp_get_playlist_number(ui_state *ui)
387 {
388   gchar *result = snackamp_socket_send_message("xmms_remote_get_playlist_length\n", ui);
389   gint number = get_integer_from_string(result);
390   g_free(result);
391 
392   if (number == -1)
393   {
394     snackamp_stop(ui);
395   }
396 
397   return number;
398 }
399 
400 /*!returns the title of the song
401 
402 The return value must be g_free'd after use.
403 */
snackamp_get_title_song(ui_state * ui)404 gchar *snackamp_get_title_song(ui_state *ui)
405 {
406   gint playlist_pos = snackamp_get_playlist_pos(ui);
407 
408   gchar temp[100];
409   g_snprintf(temp, 100,"%s %d\n", "xmms_remote_get_playlist_title",playlist_pos);
410 
411   gchar *result = snackamp_socket_send_message(temp, ui);
412   result = cut_begin_end(result);
413 
414   return result;
415 }
416 
417 //!returns elapsed time
snackamp_get_time_elapsed(ui_state * ui)418 gint snackamp_get_time_elapsed(ui_state *ui)
419 {
420   gchar *result = snackamp_socket_send_message("xmms_remote_get_output_time\n", ui);
421   gint pos = get_integer_from_string(result);
422   g_free(result);
423 
424   return pos;
425 }
426 
427 //!starts snackamp
snackamp_start(ui_state * ui)428 void snackamp_start(ui_state *ui)
429 {
430   static gchar *exec_command = "snackAmp";
431   gchar *exec_this = g_strdup_printf("%s &", exec_command);
432   system(exec_this);
433 
434   time_t lt;
435   gint timer = time(&lt);
436   while ((!snackamp_is_running(ui)) && ((time(&lt) - timer) < 8))
437   {
438     usleep(0);
439   }
440 
441   g_free(exec_this);
442 }
443 
444 //!jumps to the position pos in the playlist
snackamp_set_playlist_pos(gint pos,ui_state * ui)445 static void snackamp_set_playlist_pos(gint pos, ui_state *ui)
446 {
447   gchar temp[100];
448   g_snprintf(temp, 100, "%s %d\n", "xmms_remote_set_playlist_pos",pos);
449   gchar *result = snackamp_socket_send_message(temp, ui);
450   g_free(result);
451 }
452 
453 //!selects the last file in the playlist
snackamp_select_last_file(ui_state * ui)454 void snackamp_select_last_file(ui_state *ui)
455 {
456   gint last_song = snackamp_get_playlist_number(ui);
457   snackamp_set_playlist_pos(last_song - 1, ui);
458 }
459 
460 //!plays a song
snackamp_play(ui_state * ui)461 void snackamp_play(ui_state *ui)
462 {
463   gchar *result = snackamp_socket_send_message("xmms_remote_play\n", ui);
464   g_free(result);
465 }
466 
467 //!plays the last file of the playlist
snackamp_play_last_file(ui_state * ui)468 void snackamp_play_last_file(ui_state *ui)
469 {
470   snackamp_select_last_file(ui);
471   snackamp_play(ui);
472 }
473 
474 //!add files to the snackamp playlist
snackamp_add_files(GList * list,ui_state * ui)475 void snackamp_add_files(GList *list, ui_state *ui)
476 {
477   gint i = 0;
478   gchar *song = NULL;
479   while ((song = g_list_nth_data(list, i)) != NULL)
480   {
481     gint malloc_int = strlen(song) + 30;
482     gchar *local = malloc(malloc_int * sizeof(gchar *));
483     g_snprintf(local, malloc_int, "%s {%s}\n", "xmms_remote_playlist_add ", song);
484 
485     gchar *result = snackamp_socket_send_message(local, ui);
486     g_free(result);
487 
488     g_free(local);
489     i++;
490   }
491 }
492 
493 //!sets volume
snackamp_set_volume(gint volume,ui_state * ui)494 void snackamp_set_volume(gint volume, ui_state *ui)
495 {
496   gchar temp[100];
497   g_snprintf(temp, 100, "%s %d\n", "xmms_remote_set_main_volume", volume);
498   gchar *result = snackamp_socket_send_message(temp, ui);
499   g_free(result);
500 }
501 
502 //!returns volume
snackamp_get_volume(ui_state * ui)503 gint snackamp_get_volume(ui_state *ui)
504 {
505   gchar *result = snackamp_socket_send_message("xmms_remote_get_main_volume\n", ui);
506   gint vol = get_integer_from_string(result);
507   g_free(result);
508 
509   return vol;
510 }
511 
512 //!starts snackamp with songs
snackamp_start_with_songs(GList * list,ui_state * ui)513 void snackamp_start_with_songs(GList *list, ui_state *ui)
514 {
515   snackamp_start(ui);
516   snackamp_add_files(list, ui);
517 }
518 
519 //!returns TRUE if snackamp is running; if not, FALSE
snackamp_is_running(ui_state * ui)520 gint snackamp_is_running(ui_state *ui)
521 {
522   if (ui->pi->connected)
523   {
524     return TRUE;
525   }
526 
527   if (connect_snackamp(8775, ui) == 0)
528   {
529     return TRUE;
530   }
531 
532   return FALSE;
533 }
534 
535 //!pause a song
snackamp_pause(ui_state * ui)536 void snackamp_pause(ui_state *ui)
537 {
538   gchar *result = snackamp_socket_send_message("xmms_remote_pause\n", ui);
539   g_free(result);
540 }
541 
542 //!changes to next song
snackamp_next(ui_state * ui)543 void snackamp_next(ui_state *ui)
544 {
545   gchar *result = snackamp_socket_send_message("xmms_remote_playlist_next\n", ui);
546   g_free(result);
547 }
548 
549 //!changes to previous song
snackamp_prev(ui_state * ui)550 void snackamp_prev(ui_state *ui)
551 {
552   gint playlist_pos = snackamp_get_playlist_pos(ui);
553 
554   if (playlist_pos > 0)
555   {
556     gchar *result = snackamp_socket_send_message("xmms_remote_playlist_prev\n", ui);
557     g_free(result);
558     return;
559   }
560 
561   snackamp_play_last_file(ui);
562 }
563 
564 //!jump to time
snackamp_jump(gint position,ui_state * ui)565 void snackamp_jump(gint position, ui_state *ui)
566 {
567   gint hundr_secs_pos = position / 10;
568   gint hundr_secs = hundr_secs_pos % 100;
569   if (hundr_secs == 1)
570   {
571     hundr_secs = 0;
572   }
573 
574   gint secs = hundr_secs_pos / 100;
575   gfloat total_pos = hundr_secs + secs * 100;
576   total_pos /= 100;
577 
578   gchar temp[100];
579   g_snprintf(temp, 100, "%s %f\n", "xmms_remote_jump_to_time", total_pos);
580 
581   gchar *result = snackamp_socket_send_message(temp, ui);
582   g_free(result);
583 }
584 
585 //!returns total time of the current song
snackamp_get_total_time(ui_state * ui)586 gint snackamp_get_total_time(ui_state *ui)
587 {
588   gchar *result = snackamp_socket_send_message("xmms_remote_get_playlist_time\n", ui);
589   gint hundr_secs = get_integer_from_string(result) * 1000;
590   g_free(result);
591 
592   return hundr_secs;
593 }
594 
595 //!returns TRUE if snackamp is playing, else FALSE
snackamp_is_playing(ui_state * ui)596 gint snackamp_is_playing(ui_state *ui)
597 {
598   if (!snackamp_is_connected(ui))
599   {
600     return FALSE;
601   }
602 
603   gchar *result = snackamp_socket_send_message("xmms_remote_is_playing\n", ui);
604   gint i = atoi(result);
605   g_free(result);
606 
607   if (i == 0)
608   {
609     return FALSE;
610   }
611 
612   return TRUE;
613 }
614 
615 /*! returns TRUE if snackamp is paused, else FALSE
616 
617 not yet implemented in snackamp
618 */
snackamp_is_paused(ui_state * ui)619 gint snackamp_is_paused(ui_state *ui)
620 {
621   if (!snackamp_is_connected(ui))
622   {
623     return FALSE;
624   }
625 
626   gchar *result = snackamp_socket_send_message("xmms_remote_is_paused\n", ui);
627   result = cut_begin_end(result);
628 
629   gint i = atoi(result);
630   g_free(result);
631 
632   if (i == 1)
633   {
634     return TRUE;
635   }
636 
637   return FALSE;
638 }
639 
640