Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / vorbis-tools / ogg123 / remote.c
blob30f9787599ba00c34f8aec093352265fc5da3b9a
1 /* remote.c by Richard van Paasen <rvpaasen@dds.nl> */
3 /********************************************************************
4 * *
5 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
6 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
7 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
8 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
9 * *
10 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001 *
11 * by Kenneth C. Arnold <ogg@arnoldnet.net> AND OTHER CONTRIBUTORS *
12 * http://www.xiph.org/ *
13 * *
14 ********************************************************************
16 last mod: $Id$
18 ********************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <pthread.h>
29 #include <semaphore.h>
31 #if HAVE_SELECT
32 #include <sys/select.h>
33 #endif
35 #include "ogg123.h"
36 #include "format.h"
38 /* Maximum size of the input buffer */
39 #define MAXBUF 1024
40 /* Undefine logfile if you don't want it */
41 //#define LOGFILE "/tmp/ogg123.log"
43 /* The play function in ogg123.c */
44 extern void play (char *source_string);
45 extern ogg123_options_t options;
46 extern void set_seek_opt(ogg123_options_t *ogg123_opts, char *buf);
48 /* Status */
49 typedef enum { PLAY, STOP, PAUSE, NEXT, QUIT} Status;
50 static Status status = STOP;
52 /* Threading is introduced to reduce the
53 amount of processor time that ogg123
54 will take if it is in idle state */
56 /* Thread control locks */
57 static pthread_mutex_t main_lock;
58 static sem_t sem_command;
59 static sem_t sem_processed;
60 static pthread_mutex_t output_lock;
62 #ifdef LOGFILE
63 void send_log(const char* fmt, ...) {
65 FILE* fp;
66 va_list ap;
67 pthread_mutex_lock (&output_lock);
68 fp=fopen(LOGFILE,"a");
69 va_start(ap, fmt);
70 vfprintf(fp, fmt, ap);
71 va_end(ap);
72 fprintf(fp, "\n");
73 fclose(fp);
74 pthread_mutex_unlock (&output_lock);
75 return;
77 #else
78 #define send_log(...)
79 #endif
81 static void send_msg(const char* fmt, ...) {
83 va_list ap;
84 pthread_mutex_lock (&output_lock);
85 fprintf(stdout, "@");
86 va_start(ap, fmt);
87 vfprintf(stdout, fmt, ap);
88 va_end(ap);
89 fprintf(stdout, "\n");
90 pthread_mutex_unlock (&output_lock);
91 return;
94 static void send_err(const char* fmt, ...) {
95 va_list ap;
96 pthread_mutex_lock (&output_lock);
97 fprintf(stderr, "@");
98 va_start(ap, fmt);
99 vfprintf(stderr, fmt, ap);
100 va_end(ap);
101 fprintf(stderr, "\n");
102 pthread_mutex_unlock (&output_lock);
103 return;
106 static Status getstatus() {
108 return status;
111 static void setstatus(Status s) {
113 status = s;
116 static void invertpause() {
118 if (status==PLAY) {
119 status = PAUSE;
121 else if (status==PAUSE) {
122 status = PLAY;
124 return;
127 static void * remotethread(void * arg) {
129 int done = 0;
130 int error = 0;
131 int ignore = 0;
132 char buf[MAXBUF+1];
133 char *b;
135 #if HAVE_SELECT
136 fd_set fd;
137 #endif
139 buf[MAXBUF]=0;
141 while(!done) {
142 /* Read a line */
143 buf[0] = 0;
144 send_log("Waiting for input: ...");
146 #if HAVE_SELECT
147 FD_ZERO(&fd);
148 FD_SET(0,&fd);
149 select (1, &fd, NULL, NULL, NULL);
150 #endif
152 fgets(buf, MAXBUF, stdin);
153 buf[strlen(buf)-1] = 0;
155 /* Lock on */
156 pthread_mutex_lock (&main_lock);
158 send_log("Input: %s", buf);
159 error = 0;
161 if (!strncasecmp(buf,"l",1)) {
162 /* prepare to load */
163 if ((b=strchr(buf,' ')) != NULL) {
164 /* Prepare to load a new song */
165 strcpy((char*)arg, b+1);
166 setstatus(NEXT);
168 else {
169 /* Invalid load command */
170 error = 1;
173 else
174 if (!strncasecmp(buf,"p",1)) {
175 /* Prepare to (un)pause */
176 invertpause();
178 else
179 if (!strncasecmp(buf,"j",1)) {
180 /* Prepare to seek */
181 if ((b=strchr(buf,' ')) != NULL) {
182 set_seek_opt(&options, b+1);
184 ignore = 1;
186 else
187 if (!strncasecmp(buf,"s",1)) {
188 /* Prepare to stop */
189 setstatus(STOP);
191 else
192 if (!strncasecmp(buf,"r",1)) {
193 /* Prepare to reload */
194 setstatus(NEXT);
196 else
197 if (!strncasecmp(buf,"h",1)) {
198 /* Send help */
199 send_msg("H +----------------------------------------------------+");
200 send_msg("H | Ogg123 remote interface |");
201 send_msg("H |----------------------------------------------------|");
202 send_msg("H | Load <file> - load a file and starts playing |");
203 send_msg("H | Pause - pause or unpause playing |");
204 send_msg("H | Jump [+|-]<f> - jump <f> seconds forth or back |");
205 send_msg("H | Stop - stop playing |");
206 send_msg("H | Reload - reload last song |");
207 send_msg("H | Quit - quit ogg123 |");
208 send_msg("H |----------------------------------------------------|");
209 send_msg("H | refer to README.remote for documentation |");
210 send_msg("H +----------------------------------------------------+");
211 ignore = 1;
213 else
214 if (!strncasecmp(buf,"q",1)) {
215 /* Prepare to quit */
216 setstatus(QUIT);
217 done = 1;
219 else {
220 /* Unknown input received */
221 error = 1;
224 if (ignore) {
225 /* Unlock */
226 pthread_mutex_unlock (&main_lock);
227 ignore = 0;
228 } else {
229 if (error) {
230 /* Send the error and unlock */
231 send_err("E Unknown command '%s'", buf);
232 send_log("Unknown command '%s'", buf);
233 /* Unlock */
234 pthread_mutex_unlock (&main_lock);
236 else {
237 /* Signal the main thread */
238 sem_post(&sem_command);
239 /* Unlock */
240 pthread_mutex_unlock (&main_lock);
241 /* Wait until the change has been noticed */
242 sem_wait(&sem_processed);
247 return NULL;
250 void remote_mainloop(void) {
252 int r;
253 pthread_t th;
254 Status s;
255 char fname[MAXBUF+1];
257 /* Need to output line by line! */
258 setlinebuf(stdout);
260 /* Send a greeting */
261 send_msg("R ogg123 from " PACKAGE " " VERSION);
263 /* Initialize the thread controlling variables */
264 pthread_mutex_init(&main_lock, NULL);
265 sem_init(&sem_command, 0, 0);
266 sem_init(&sem_processed, 0, 0);
268 /* Start the thread */
269 r = pthread_create(&th, NULL, remotethread, (void*)fname);
270 if (r != 0) {
271 send_err("E Could not create a thread (code %d)", r);
272 return;
275 send_log("Start");
277 /* The thread may already have processed some input,
278 get the current status
280 pthread_mutex_lock(&main_lock);
281 s = getstatus();
282 pthread_mutex_unlock(&main_lock);
284 while (s != QUIT) {
286 /* wait for a new command */
287 if (s != NEXT) {
289 /* Wait until a new status is available,
290 This puts the main tread asleep and
291 saves resources
294 sem_wait(&sem_command);
296 pthread_mutex_lock(&main_lock);
297 s = getstatus();
298 pthread_mutex_unlock(&main_lock);
301 send_log("Status: %d", s);
303 if (s == NEXT) {
305 /* The status is to play a new song. Set
306 the status to PLAY and signal the thread
307 that the status has been processed.
310 send_msg("I %s", fname);
311 send_msg("S 0.0 0 00000 xxxxxx 0 0 0 0 0 0 0 0");
312 send_msg("P 2");
313 pthread_mutex_lock(&main_lock);
314 setstatus(PLAY);
315 s = getstatus();
316 send_log("mainloop s=%d", s);
317 sem_post(&sem_processed);
318 s = getstatus();
319 send_log("mainloop s=%d", s);
320 pthread_mutex_unlock(&main_lock);
322 /* Start the player. The player calls the playloop
323 frequently to check for a new status (e.g. NEXT,
324 STOP or PAUSE.
327 pthread_mutex_lock(&main_lock);
328 s = getstatus();
329 pthread_mutex_unlock(&main_lock);
330 send_log("mainloop s=%d", s);
331 play(fname);
333 /* Retrieve the new status */
334 pthread_mutex_lock(&main_lock);
335 s = getstatus();
336 pthread_mutex_unlock(&main_lock);
338 /* don't know why this was here, sending "play stoped" on NEXT wasn't good idea... */
339 // if (s == NEXT) {
341 /* Send "play stopped" */
342 // send_msg("P 0");
343 // send_log("P 0");
344 // } else {
346 /* Send "play stopped at eof" */
347 // send_msg("P 0 EOF");
348 // send_log("P 0 EOF");
349 // }
352 else {
354 /* Irrelevent status, notice the thread that
355 it has been processed.
358 sem_post(&sem_processed);
362 /* Send "Quit" */
363 send_msg("Q");
364 send_log("Quit");
366 /* Cleanup the semaphores */
367 sem_destroy(&sem_command);
368 sem_destroy(&sem_processed);
370 return;
373 int remote_playloop(void) {
375 Status s;
377 /* Check the status. If the player should pause,
378 then signal that the command has been processed
379 and wait for a new status. A new status will end
380 the player and return control to the main loop.
381 The main loop will signal that the new command
382 has been processed.
385 pthread_mutex_lock (&main_lock);
386 s = getstatus();
387 pthread_mutex_unlock (&main_lock);
389 send_log("playloop entry s=%d", s);
391 if (s == PAUSE) {
393 /* Send "pause on" */
394 send_msg("P 1");
396 while (s == PAUSE) {
398 sem_post(&sem_processed);
399 sem_wait(&sem_command);
400 pthread_mutex_lock (&main_lock);
401 s = getstatus();
402 pthread_mutex_unlock (&main_lock);
405 /* Send "pause off" */
406 send_msg("P 2");
409 /* Send stop msg to the frontend */
410 /* this probably should be done after the audio buffer is flushed and no audio is actually playing, but don't know how */
411 if ((s == STOP) || (s == QUIT)) send_msg("P 0");
413 send_log("playloop exit s=%d", s);
415 return ((s == NEXT) || (s == STOP) || (s == QUIT));
418 void remote_time(double current, double total) {
420 /* Send the frame (not implemented yet) and the time */
421 send_msg("F 0 0 %.2f %.2f", current, (total-current));
423 return;