1 /*
2  *  flac123 a command-line flac player
3  *  Copyright (C) 2003-2007  Jake Angerman
4  *
5  *  This remote.c module heavily based upon:
6  *  mpg321 - a fully free clone of mpg123.  Copyright (C) 2001 Joe Drew
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #include <sys/time.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include "flac123.h"
28 
29 #define BUF_SIZE (PATH_MAX + 5)
30 
31 char remote_input_buf[BUF_SIZE];
32 
trim_whitespace(char * str)33 static void trim_whitespace(char *str)
34 {
35     char *ptr = str;
36     register int pos = strlen(str)-1;
37 
38     while(isspace(ptr[pos]))
39         ptr[pos--] = '\0';
40 
41     while(isspace(*ptr))
42         ptr++;
43 
44     strncpy(str, ptr, pos+1);
45 }
46 
47 /* returns 0 on success (keep decoding), or -1 on QUIT (stop decoding) */
remote_parse_input(void)48 static int remote_parse_input(void)
49 {
50     char input[BUF_SIZE]; /* for filename as well as input and space */
51     char new_remote_input_buf[BUF_SIZE];
52     char *arg, *newline;
53     int numread;
54     int alreadyread;
55     int linelen;
56 
57     fd_set fd;
58     struct timeval tv = { 0, 0 };
59     FD_ZERO(&fd);
60     FD_SET(0,&fd);
61 
62     alreadyread = strlen (remote_input_buf);
63 
64     if (select (1, &fd, NULL, NULL, &tv))  /* return immediately */
65     {
66 	if (!(numread = read(0, remote_input_buf + alreadyread, (sizeof(input)-1)-alreadyread)) > 0)
67 	{
68 	    numread = 0; /* should never happen.  read() blocks */
69 	}
70     } else {
71 	numread = 0;
72     }
73 
74     remote_input_buf[numread+alreadyread] = '\0';
75 
76     if ((newline = strchr(remote_input_buf, '\n')))
77     {
78         *(newline) = '\0';
79     }
80 
81     linelen = strlen (remote_input_buf);
82 
83     strcpy (input, remote_input_buf);
84     /* input[linelen] = '\0'; */
85     strcpy (new_remote_input_buf, remote_input_buf + linelen + 1);
86     /* don't copy the \0... */
87     strcpy (remote_input_buf, new_remote_input_buf);
88 
89     trim_whitespace(input); /* from left and right */
90 
91     if (strlen(input) == 0)
92         return 0;
93 
94     arg = strchr(input, ' ');
95 
96     if (arg)
97     {
98         *(arg++) = '\0';  /* separate command from argument */
99         arg = strdup(arg);
100         trim_whitespace(arg);
101     }
102 
103     if (strcasecmp(input, "L") == 0 || strcasecmp(input, "LOAD") == 0)
104     {
105         if (arg)
106         {
107 	    if (file_info.is_loaded == true)
108 		decoder_destructor();
109 
110 	    if (!decoder_constructor(arg))
111 	    {
112 		fprintf(stderr, "@E Error opening %s\n", arg);
113 		free(arg);
114 		return 0;
115 	    }
116         }
117         else
118         {
119             fprintf(stderr, "@E Missing argument to '%s'\n", input);
120             return 0;
121         }
122     }
123 
124     else if (strcasecmp(input, "J") == 0 || strcasecmp(input, "JUMP") == 0)
125     {
126         if (file_info.is_playing && arg)
127         {
128             /* relative seek */
129             if (arg[0] == '-' || arg[0] == '+')
130             {
131 		/* assumption: fixed bit-rate encoding */
132 		signed long delta_time = atol(arg);
133 		signed long delta_frames = delta_time * file_info.ao_fmt.rate;
134 
135 		if ((delta_time < 0) && (labs(delta_time) > file_info.elapsed_time))
136 		{
137 		    file_info.elapsed_time = 0;
138 		    file_info.current_sample = 0;
139 		}
140 		else if ((delta_time > 0) && (delta_time > file_info.total_time - file_info.elapsed_time))
141 		{
142 		    /* don't do anything */
143 		    if (arg)
144 			free (arg);
145 		    return 0;
146 		}
147 		else
148 		{
149 		    file_info.elapsed_time += delta_time;
150 		    file_info.current_sample += delta_frames;
151 		}
152 
153 #ifdef LEGACY_FLAC
154 		FLAC__file_decoder_seek_absolute(file_info.decoder,
155 						 file_info.current_sample);
156 #else
157 		FLAC__stream_decoder_seek_absolute(file_info.decoder,
158 						 file_info.current_sample);
159 #endif
160             }
161 	    /* absolute seek */
162             else
163             {
164 		long absolute_time = atol(arg);
165 		long absolute_frame = absolute_time * file_info.ao_fmt.rate;
166 		file_info.elapsed_time = absolute_time;
167 		file_info.current_sample = absolute_frame;
168 
169 #ifdef LEGACY_FLAC
170 		FLAC__file_decoder_seek_absolute(file_info.decoder, absolute_frame);
171 #else
172 		FLAC__stream_decoder_seek_absolute(file_info.decoder, absolute_frame);
173 #endif
174             }
175 
176         }
177         else
178         {
179             /* mpg123 does no error checking, so we should emulate that */
180         }
181     }
182 
183     else if (strcasecmp(input, "S") == 0 || strcasecmp(input, "STOP") == 0)
184     {
185 	if (file_info.is_loaded == true)
186 	{
187 	    fprintf(stderr, "@P 0\n");
188 	    decoder_destructor();
189 	}
190     }
191 
192     else if (strcasecmp(input, "V") == 0 || strcasecmp(input, "VOLUME") == 0)
193     {
194         if (arg)
195 	{
196 	    scale = atof(arg);
197 	    fprintf(stderr, "@V %f\n", scale);
198 	}
199     }
200 
201     else if (strcasecmp(input, "P") == 0 || strcasecmp(input, "PAUSE") == 0)
202     {
203 	if (file_info.is_loaded == true)
204 	{
205 	    if (file_info.is_playing == true)
206 	    {
207 		file_info.is_playing = false;
208 		fprintf(stderr, "@P 1\n");
209 	    }
210 	    else
211 	    {
212 		file_info.is_playing = true;
213 		fprintf(stderr, "@P 2\n");
214 	    }
215 	}
216     }
217 
218     else if (strcasecmp(input, "Q") == 0 || strcasecmp(input, "QUIT") == 0)
219     {
220 	if (arg)
221 	    free(arg);
222 
223 	if (file_info.is_loaded)
224 	    decoder_destructor();
225 
226 	return -1;
227     }
228 
229     else
230     {
231         fprintf(stderr, "@E Unknown command '%s'\n", input);
232     }
233 
234     if (arg)
235         free(arg);
236 
237     return 0;
238 }
239 
remote_get_input_wait(void)240 int remote_get_input_wait(void)
241 {
242     fd_set fd;
243     FD_ZERO(&fd);
244     FD_SET(0,&fd);
245 
246     if (strlen(remote_input_buf) == 0)
247     {
248 	select(1, &fd, NULL, NULL, NULL); /* block indefinitely */
249     }
250 
251     return remote_parse_input();
252 }
253 
remote_get_input_nowait(void)254 int remote_get_input_nowait(void)
255 {
256     fd_set fd;
257     struct timeval tv = { 0, 0 };
258     FD_ZERO(&fd);
259     FD_SET(0,&fd);
260 
261     if (strlen(remote_input_buf) == 0)
262     {
263 	if (select(1, &fd, NULL, NULL, &tv)) /* return immediately */
264 	    return remote_parse_input();
265 	else
266 	    return 0;
267     }
268     else
269     {
270 	return remote_parse_input();
271     }
272 }
273 
274