control_generic.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. /*
  2. control_generic.c: control interface for frontends and real console warriors
  3. copyright 1997-99,2004-8 by the mpg123 project - free software under the terms of the LGPL 2.1
  4. see COPYING and AUTHORS files in distribution or http://mpg123.org
  5. initially written by Andreas Neuhaus and Michael Hipp
  6. reworked by Thomas Orgis - it was the entry point for eventually becoming maintainer...
  7. */
  8. #include "config.h"
  9. /* _BSD_SOURCE needed for setlinebuf, erm, but that's deprecated
  10. so trying _DEFAULT_SOURCE */
  11. #ifndef _DEFAULT_SOURCE
  12. #define _DEFAULT_SOURCE
  13. #endif
  14. /* Defining that after _DEFAULT_SOURCE seems fine and is still
  15. needed for older glibc. I guess I need a configure check
  16. about setlinebuf()/setvbuf() if I really care about old
  17. systems. */
  18. #ifndef _BSD_SOURCE
  19. #define _BSD_SOURCE
  20. #endif
  21. #include "compat.h"
  22. #include "mpg123app.h"
  23. #include "out123.h"
  24. #include <stdarg.h>
  25. #include <ctype.h>
  26. #if !defined (WIN32) || defined (__CYGWIN__)
  27. #include <sys/wait.h>
  28. #include <sys/socket.h>
  29. #endif
  30. #include <errno.h>
  31. #include <string.h>
  32. #include "common.h"
  33. #include "genre.h"
  34. #include "playlist.h"
  35. #include "audio.h"
  36. #define MODE_STOPPED 0
  37. #define MODE_PLAYING 1
  38. #define MODE_PAUSED 2
  39. extern out123_handle *ao;
  40. #ifdef FIFO
  41. #include <sys/stat.h>
  42. int control_file = STDIN_FILENO;
  43. #else
  44. #define control_file STDIN_FILENO
  45. #ifdef WANT_WIN32_FIFO
  46. #error Control interface does not work on win32 stdin
  47. #endif /* WANT_WIN32_FIFO */
  48. #endif
  49. FILE *outstream;
  50. static int mode = MODE_STOPPED;
  51. static int init = 0;
  52. #include "debug.h"
  53. void generic_sendmsg (const char *fmt, ...)
  54. {
  55. va_list ap;
  56. fprintf(outstream, "@");
  57. va_start(ap, fmt);
  58. vfprintf(outstream, fmt, ap);
  59. va_end(ap);
  60. fprintf(outstream, "\n");
  61. }
  62. /* Split up a number of lines separated by \n, \r, both or just zero byte
  63. and print out each line with specified prefix. */
  64. static void generic_send_lines(const char* fmt, mpg123_string *inlines)
  65. {
  66. size_t i;
  67. int hadcr = 0, hadlf = 0;
  68. char *lines = NULL;
  69. char *line = NULL;
  70. size_t len = 0;
  71. if(inlines != NULL && inlines->fill)
  72. {
  73. lines = inlines->p;
  74. len = inlines->fill;
  75. }
  76. else return;
  77. line = lines;
  78. for(i=0; i<len; ++i)
  79. {
  80. if(lines[i] == '\n' || lines[i] == '\r' || lines[i] == 0)
  81. {
  82. char save = lines[i]; /* saving, changing, restoring a byte in the data */
  83. if(save == '\n') ++hadlf;
  84. if(save == '\r') ++hadcr;
  85. if((hadcr || hadlf) && hadlf % 2 == 0 && hadcr % 2 == 0) line = "";
  86. if(line)
  87. {
  88. lines[i] = 0;
  89. generic_sendmsg(fmt, line);
  90. line = NULL;
  91. lines[i] = save;
  92. }
  93. }
  94. else
  95. {
  96. hadlf = hadcr = 0;
  97. if(line == NULL) line = lines+i;
  98. }
  99. }
  100. }
  101. void generic_sendstat (mpg123_handle *fr)
  102. {
  103. off_t current_frame, frames_left;
  104. double current_seconds, seconds_left;
  105. if(!mpg123_position(fr, 0, out123_buffered(ao), &current_frame, &frames_left, &current_seconds, &seconds_left))
  106. generic_sendmsg("F %"OFF_P" %"OFF_P" %3.2f %3.2f", (off_p)current_frame, (off_p)frames_left, current_seconds, seconds_left);
  107. }
  108. static void generic_sendv1(mpg123_id3v1 *v1, const char *prefix)
  109. {
  110. int i;
  111. char info[125] = "";
  112. memcpy(info, v1->title, 30);
  113. memcpy(info+30, v1->artist, 30);
  114. memcpy(info+60, v1->album, 30);
  115. memcpy(info+90, v1->year, 4);
  116. memcpy(info+94, v1->comment, 30);
  117. for(i=0;i<124; ++i) if(info[i] == 0) info[i] = ' ';
  118. info[i] = 0;
  119. generic_sendmsg("%s ID3:%s%s", prefix, info, (v1->genre<=genre_count) ? genre_table[v1->genre] : "Unknown");
  120. generic_sendmsg("%s ID3.genre:%i", prefix, v1->genre);
  121. if(v1->comment[28] == 0 && v1->comment[29] != 0)
  122. generic_sendmsg("%s ID3.track:%i", prefix, (unsigned char)v1->comment[29]);
  123. }
  124. static void generic_sendinfoid3(mpg123_handle *mh)
  125. {
  126. mpg123_id3v1 *v1;
  127. mpg123_id3v2 *v2;
  128. if(MPG123_OK != mpg123_id3(mh, &v1, &v2))
  129. {
  130. error1("Cannot get ID3 data: %s", mpg123_strerror(mh));
  131. return;
  132. }
  133. if(v1 != NULL)
  134. {
  135. generic_sendv1(v1, "I");
  136. }
  137. if(v2 != NULL)
  138. {
  139. generic_send_lines("I ID3v2.title:%s", v2->title);
  140. generic_send_lines("I ID3v2.artist:%s", v2->artist);
  141. generic_send_lines("I ID3v2.album:%s", v2->album);
  142. generic_send_lines("I ID3v2.year:%s", v2->year);
  143. generic_send_lines("I ID3v2.comment:%s", v2->comment);
  144. generic_send_lines("I ID3v2.genre:%s", v2->genre);
  145. }
  146. }
  147. void generic_sendalltag(mpg123_handle *mh)
  148. {
  149. mpg123_id3v1 *v1;
  150. mpg123_id3v2 *v2;
  151. generic_sendmsg("T {");
  152. if(MPG123_OK != mpg123_id3(mh, &v1, &v2))
  153. {
  154. error1("Cannot get ID3 data: %s", mpg123_strerror(mh));
  155. v2 = NULL;
  156. v1 = NULL;
  157. }
  158. if(v1 != NULL) generic_sendv1(v1, "T");
  159. if(v2 != NULL)
  160. {
  161. size_t i;
  162. for(i=0; i<v2->texts; ++i)
  163. {
  164. char id[5];
  165. memcpy(id, v2->text[i].id, 4);
  166. id[4] = 0;
  167. generic_sendmsg("T ID3v2.%s:", id);
  168. generic_send_lines("T =%s", &v2->text[i].text);
  169. }
  170. for(i=0; i<v2->extras; ++i)
  171. {
  172. char id[5];
  173. memcpy(id, v2->extra[i].id, 4);
  174. id[4] = 0;
  175. generic_sendmsg("T ID3v2.%s desc(%s):",
  176. id,
  177. v2->extra[i].description.fill ? v2->extra[i].description.p : "" );
  178. generic_send_lines("T =%s", &v2->extra[i].text);
  179. }
  180. for(i=0; i<v2->comments; ++i)
  181. {
  182. char id[5];
  183. char lang[4];
  184. memcpy(id, v2->comment_list[i].id, 4);
  185. id[4] = 0;
  186. memcpy(lang, v2->comment_list[i].lang, 3);
  187. lang[3] = 0;
  188. generic_sendmsg("T ID3v2.%s lang(%s) desc(%s):",
  189. id, lang,
  190. v2->comment_list[i].description.fill ? v2->comment_list[i].description.p : "");
  191. generic_send_lines("T =%s", &v2->comment_list[i].text);
  192. }
  193. }
  194. generic_sendmsg("T }");
  195. }
  196. void generic_sendinfo (char *filename)
  197. {
  198. char *s, *t;
  199. s = strrchr(filename, '/');
  200. if (!s)
  201. s = filename;
  202. else
  203. s++;
  204. t = strrchr(s, '.');
  205. if (t)
  206. *t = 0;
  207. generic_sendmsg("I %s", s);
  208. }
  209. static void generic_load(mpg123_handle *fr, char *arg, int state)
  210. {
  211. out123_drop(ao);
  212. if(mode != MODE_STOPPED)
  213. {
  214. close_track();
  215. mode = MODE_STOPPED;
  216. }
  217. if(!open_track(arg))
  218. {
  219. generic_sendmsg("E Error opening stream: %s", arg);
  220. generic_sendmsg("P 0");
  221. return;
  222. }
  223. mpg123_seek(fr, 0, SEEK_SET); /* This finds ID3v2 at beginning. */
  224. if(mpg123_meta_check(fr) & MPG123_NEW_ID3)
  225. {
  226. generic_sendinfoid3(fr);
  227. }
  228. else generic_sendinfo(arg);
  229. if(htd.icy_name.fill) generic_sendmsg("I ICY-NAME: %s", htd.icy_name.p);
  230. if(htd.icy_url.fill) generic_sendmsg("I ICY-URL: %s", htd.icy_url.p);
  231. mode = state;
  232. init = 1;
  233. generic_sendmsg(mode == MODE_PAUSED ? "P 1" : "P 2");
  234. }
  235. static void generic_loadlist(mpg123_handle *fr, char *arg)
  236. {
  237. /* arguments are two: first the index to play, then the URL */
  238. long entry;
  239. long i = 0;
  240. char *file = NULL;
  241. char *thefile = NULL;
  242. /* I feel retarted with string parsing outside Perl. */
  243. while(*arg && isspace(*arg)) ++arg;
  244. entry = atol(arg);
  245. while(*arg && !isspace(*arg)) ++arg;
  246. while(*arg && isspace(*arg)) ++arg;
  247. if(!*arg)
  248. {
  249. generic_sendmsg("E empty list name");
  250. return;
  251. }
  252. /* Now got the plain playlist path in arg. On to evil manupulation of mpg123's playlist code. */
  253. param.listname = arg;
  254. param.listentry = 0; /* The playlist shall not filter. */
  255. param.loop = 1;
  256. param.shuffle = 0;
  257. prepare_playlist(0, NULL);
  258. while((file = get_next_file()))
  259. {
  260. ++i;
  261. /* semantics: 0 brings you to the last track */
  262. if(entry == 0 || entry == i) thefile = file;
  263. generic_sendmsg("I LISTENTRY %li: %s", i, file);
  264. }
  265. if(!i) generic_sendmsg("I LIST EMPTY");
  266. /* If we have something to play, play it. */
  267. if(thefile) generic_load(fr, thefile, MODE_PLAYING);
  268. free_playlist(); /* Free memory after it is not needed anymore. */
  269. }
  270. int control_generic (mpg123_handle *fr)
  271. {
  272. struct timeval tv;
  273. fd_set fds;
  274. int n;
  275. /* ThOr */
  276. char alive = 1;
  277. char silent = 0;
  278. /* responses to stderr for frontends needing audio data from stdout */
  279. if (param.remote_err)
  280. outstream = stderr;
  281. else
  282. outstream = stdout;
  283. #ifndef WIN32
  284. setlinebuf(outstream);
  285. #else /* perhaps just use setvbuf as it's C89 */
  286. /*
  287. fprintf(outstream, "You are on Win32 and want to use the control interface... tough luck: We need a replacement for select on STDIN first.\n");
  288. return 0;
  289. setvbuf(outstream, (char*)NULL, _IOLBF, 0);
  290. */
  291. #endif
  292. /* the command behaviour is different, so is the ID */
  293. /* now also with version for command availability */
  294. fprintf(outstream, "@R MPG123 (ThOr) v8\n");
  295. #ifdef FIFO
  296. if(param.fifo)
  297. {
  298. if(param.fifo[0] == 0)
  299. {
  300. error("You wanted an empty FIFO name??");
  301. return 1;
  302. }
  303. #ifndef WANT_WIN32_FIFO
  304. unlink(param.fifo);
  305. if(mkfifo(param.fifo, 0666) == -1)
  306. {
  307. error2("Failed to create FIFO at %s (%s)", param.fifo, strerror(errno));
  308. return 1;
  309. }
  310. debug("going to open named pipe ... blocking until someone gives command");
  311. #endif /* WANT_WIN32_FIFO */
  312. #ifdef WANT_WIN32_FIFO
  313. control_file = win32_fifo_mkfifo(param.fifo);
  314. #else
  315. control_file = open(param.fifo,O_RDONLY);
  316. #endif /* WANT_WIN32_FIFO */
  317. debug("opened");
  318. }
  319. #endif
  320. while (alive)
  321. {
  322. tv.tv_sec = 0;
  323. tv.tv_usec = 0;
  324. FD_ZERO(&fds);
  325. FD_SET(control_file, &fds);
  326. /* play frame if no command needs to be processed */
  327. if (mode == MODE_PLAYING) {
  328. #ifdef WANT_WIN32_FIFO
  329. n = win32_fifo_read_peek(&tv);
  330. #else
  331. n = select(32, &fds, NULL, NULL, &tv);
  332. #endif
  333. if (n == 0) {
  334. if (!play_frame())
  335. {
  336. out123_pause(ao);
  337. /* When the track ended, user may want to keep it open (to seek back),
  338. so there is a decision between stopping and pausing at the end. */
  339. if(param.keep_open)
  340. {
  341. mode = MODE_PAUSED;
  342. /* Hm, buffer should be stopped already, shouldn't it? */
  343. generic_sendmsg("P 1");
  344. }
  345. else
  346. {
  347. mode = MODE_STOPPED;
  348. close_track();
  349. generic_sendmsg("P 0");
  350. }
  351. continue;
  352. }
  353. if (init) {
  354. print_remote_header(fr);
  355. init = 0;
  356. }
  357. if(silent == 0)
  358. {
  359. generic_sendstat(fr);
  360. if(mpg123_meta_check(fr) & MPG123_NEW_ICY)
  361. {
  362. char *meta;
  363. if(mpg123_icy(fr, &meta) == MPG123_OK)
  364. generic_sendmsg("I ICY-META: %s", meta != NULL ? meta : "<nil>");
  365. }
  366. }
  367. }
  368. }
  369. else {
  370. /* wait for command */
  371. while (1) {
  372. #ifdef WANT_WIN32_FIFO
  373. n = win32_fifo_read_peek(NULL);
  374. #else
  375. n = select(32, &fds, NULL, NULL, NULL);
  376. #endif
  377. if (n > 0)
  378. break;
  379. }
  380. }
  381. /* on error */
  382. if (n < 0) {
  383. fprintf(stderr, "Error waiting for command: %s\n", strerror(errno));
  384. return 1;
  385. }
  386. /* read & process commands */
  387. if (n > 0)
  388. {
  389. short int len = 1; /* length of buffer */
  390. char *cmd, *arg; /* variables for parsing, */
  391. char *comstr = NULL; /* gcc thinks that this could be used uninitialited... */
  392. char buf[REMOTE_BUFFER_SIZE];
  393. short int counter;
  394. char *next_comstr = buf; /* have it initialized for first command */
  395. /* read as much as possible, maybe multiple commands */
  396. /* When there is nothing to read (EOF) or even an error, it is the end */
  397. #ifdef WANT_WIN32_FIFO
  398. len = win32_fifo_read(buf,REMOTE_BUFFER_SIZE);
  399. #else
  400. len = read(control_file, buf, REMOTE_BUFFER_SIZE);
  401. #endif
  402. if(len < 1)
  403. {
  404. #ifdef FIFO
  405. if(len == 0 && param.fifo)
  406. {
  407. debug("fifo ended... reopening");
  408. #ifdef WANT_WIN32_FIFO
  409. win32_fifo_mkfifo(param.fifo);
  410. #else
  411. close(control_file);
  412. control_file = open(param.fifo,O_RDONLY|O_NONBLOCK);
  413. #endif
  414. if(control_file < 0){ error1("open of fifo failed... %s", strerror(errno)); break; }
  415. continue;
  416. }
  417. #endif
  418. if(len < 0) error1("command read error: %s", strerror(errno));
  419. break;
  420. }
  421. debug1("read %i bytes of commands", len);
  422. /* one command on a line - separation by \n -> C strings in a row */
  423. for(counter = 0; counter < len; ++counter)
  424. {
  425. /* line end is command end */
  426. if( (buf[counter] == '\n') || (buf[counter] == '\r') )
  427. {
  428. debug1("line end at counter=%i", counter);
  429. buf[counter] = 0; /* now it's a properly ending C string */
  430. comstr = next_comstr;
  431. /* skip the additional line ender of \r\n or \n\r */
  432. if( (counter < (len - 1)) && ((buf[counter+1] == '\n') || (buf[counter+1] == '\r')) ) buf[++counter] = 0;
  433. /* next "real" char is first of next command */
  434. next_comstr = buf + counter+1;
  435. /* directly process the command now */
  436. debug1("interpreting command: %s", comstr);
  437. if(strlen(comstr) == 0) continue;
  438. /* PAUSE */
  439. if (!strcasecmp(comstr, "P") || !strcasecmp(comstr, "PAUSE")) {
  440. if(mode != MODE_STOPPED)
  441. {
  442. if (mode == MODE_PLAYING) {
  443. mode = MODE_PAUSED;
  444. out123_pause(ao);
  445. generic_sendmsg("P 1");
  446. } else {
  447. mode = MODE_PLAYING;
  448. out123_continue(ao);
  449. generic_sendmsg("P 2");
  450. }
  451. } else generic_sendmsg("P 0");
  452. continue;
  453. }
  454. /* STOP */
  455. if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) {
  456. if (mode != MODE_STOPPED) {
  457. /* Do we want to drop here? */
  458. out123_drop(ao);
  459. out123_pause(ao);
  460. close_track();
  461. mode = MODE_STOPPED;
  462. generic_sendmsg("P 0");
  463. } else generic_sendmsg("P 0");
  464. continue;
  465. }
  466. /* SILENCE */
  467. if(!strcasecmp(comstr, "SILENCE")) {
  468. silent = 1;
  469. generic_sendmsg("silence");
  470. continue;
  471. }
  472. if(!strcasecmp(comstr, "T") || !strcasecmp(comstr, "TAG")) {
  473. generic_sendalltag(fr);
  474. continue;
  475. }
  476. if(!strcasecmp(comstr, "SCAN"))
  477. {
  478. if(mode != MODE_STOPPED)
  479. {
  480. if(mpg123_scan(fr) == MPG123_OK)
  481. generic_sendmsg("SCAN done");
  482. else
  483. generic_sendmsg("E %s", mpg123_strerror(fr));
  484. }
  485. else generic_sendmsg("E No track loaded!");
  486. continue;
  487. }
  488. if(!strcasecmp(comstr, "SAMPLE"))
  489. {
  490. off_t pos = mpg123_tell(fr);
  491. off_t len = mpg123_length(fr);
  492. /* I need to have portable printf specifiers that do not truncate the type... more autoconf... */
  493. if(len < 0) generic_sendmsg("E %s", mpg123_strerror(fr));
  494. else generic_sendmsg("SAMPLE %li %li", (long)pos, (long)len);
  495. continue;
  496. }
  497. if(!strcasecmp(comstr, "FORMAT"))
  498. {
  499. long rate;
  500. int ch;
  501. int ret = mpg123_getformat2(fr, &rate, &ch, NULL, 0);
  502. /* I need to have portable printf specifiers that do not truncate the type... more autoconf... */
  503. if(ret < 0) generic_sendmsg("E %s", mpg123_strerror(fr));
  504. else generic_sendmsg("FORMAT %li %i", rate, ch);
  505. continue;
  506. }
  507. if(!strcasecmp(comstr, "SHOWEQ"))
  508. {
  509. int i;
  510. generic_sendmsg("SHOWEQ {");
  511. for(i=0; i<32; ++i)
  512. {
  513. generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_LEFT, i, mpg123_geteq(fr, MPG123_LEFT, i));
  514. generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_RIGHT, i, mpg123_geteq(fr, MPG123_RIGHT, i));
  515. }
  516. generic_sendmsg("SHOWEQ }");
  517. continue;
  518. }
  519. if(!strcasecmp(comstr, "STATE"))
  520. {
  521. long val;
  522. generic_sendmsg("STATE {");
  523. /* Get some state information bits and display them. */
  524. if(mpg123_getstate(fr, MPG123_ACCURATE, &val, NULL) == MPG123_OK)
  525. generic_sendmsg("STATE accurate %li", val);
  526. generic_sendmsg("STATE }");
  527. continue;
  528. }
  529. /* QUIT */
  530. if (!strcasecmp(comstr, "Q") || !strcasecmp(comstr, "QUIT")){
  531. alive = FALSE; continue;
  532. }
  533. /* some HELP */
  534. if (!strcasecmp(comstr, "H") || !strcasecmp(comstr, "HELP")) {
  535. generic_sendmsg("H {");
  536. generic_sendmsg("H HELP/H: command listing (LONG/SHORT forms), command case insensitve");
  537. generic_sendmsg("H LOAD/L <trackname>: load and start playing resource <trackname>");
  538. generic_sendmsg("H LOADPAUSED/LP <trackname>: load but do not start playing resource <trackname>");
  539. generic_sendmsg("H LOADLIST/LL <entry> <url>: load a playlist from given <url>, and display its entries, optionally load and play one of these specificed by the integer <entry> (<0: just list, 0: play last track, >0:play track with that position in list)");
  540. generic_sendmsg("H PAUSE/P: pause playback");
  541. generic_sendmsg("H STOP/S: stop playback (closes file)");
  542. generic_sendmsg("H JUMP/J <frame>|<+offset>|<-offset>|<[+|-]seconds>s: jump to mpeg frame <frame> or change position by offset, same in seconds if number followed by \"s\"");
  543. generic_sendmsg("H VOLUME/V <percent>: set volume in % (0..100...); float value");
  544. generic_sendmsg("H RVA off|(mix|radio)|(album|audiophile): set rva mode");
  545. generic_sendmsg("H EQ/E <channel> <band> <value>: set equalizer value for frequency band 0 to 31 on channel %i (left) or %i (right) or %i (both)", MPG123_LEFT, MPG123_RIGHT, MPG123_LR);
  546. generic_sendmsg("H EQFILE <filename>: load EQ settings from a file");
  547. generic_sendmsg("H SHOWEQ: show all equalizer settings (as <channel> <band> <value> lines in a SHOWEQ block (like TAG))");
  548. generic_sendmsg("H SEEK/K <sample>|<+offset>|<-offset>: jump to output sample position <samples> or change position by offset");
  549. generic_sendmsg("H SCAN: scan through the file, building seek index");
  550. generic_sendmsg("H SAMPLE: print out the sample position and total number of samples");
  551. generic_sendmsg("H FORMAT: print out sampling rate in Hz and channel count");
  552. generic_sendmsg("H SEQ <bass> <mid> <treble>: simple eq setting...");
  553. generic_sendmsg("H PITCH <[+|-]value>: adjust playback speed (+0.01 is 1 %% faster)");
  554. generic_sendmsg("H SILENCE: be silent during playback (meaning silence in text form)");
  555. generic_sendmsg("H STATE: Print auxiliary state info in several lines (just try it to see what info is there).");
  556. generic_sendmsg("H TAG/T: Print all available (ID3) tag info, for ID3v2 that gives output of all collected text fields, using the ID3v2.3/4 4-character names. NOTE: ID3v2 data will be deleted on non-forward seeks.");
  557. generic_sendmsg("H The output is multiple lines, begin marked by \"@T {\", end by \"@T }\".");
  558. generic_sendmsg("H ID3v1 data is like in the @I info lines (see below), just with \"@T\" in front.");
  559. generic_sendmsg("H An ID3v2 data field is introduced via ([ ... ] means optional):");
  560. generic_sendmsg("H @T ID3v2.<NAME>[ [lang(<LANG>)] desc(<description>)]:");
  561. generic_sendmsg("H The lines of data follow with \"=\" prefixed:");
  562. generic_sendmsg("H @T =<one line of content in UTF-8 encoding>");
  563. generic_sendmsg("H meaning of the @S stream info:");
  564. generic_sendmsg("H %s", remote_header_help);
  565. generic_sendmsg("H The @I lines after loading a track give some ID3 info, the format:");
  566. generic_sendmsg("H @I ID3:artist album year comment genretext");
  567. generic_sendmsg("H where artist,album and comment are exactly 30 characters each, year is 4 characters, genre text unspecified.");
  568. generic_sendmsg("H You will encounter \"@I ID3.genre:<number>\" and \"@I ID3.track:<number>\".");
  569. generic_sendmsg("H Then, there is an excerpt of ID3v2 info in the structure");
  570. generic_sendmsg("H @I ID3v2.title:Blabla bla Bla");
  571. generic_sendmsg("H for every line of the \"title\" data field. Likewise for other fields (author, album, etc).");
  572. generic_sendmsg("H }");
  573. continue;
  574. }
  575. /* commands with arguments */
  576. cmd = NULL;
  577. arg = NULL;
  578. cmd = strtok(comstr," \t"); /* get the main command */
  579. arg = strtok(NULL,""); /* get the args */
  580. if (cmd && strlen(cmd) && arg && strlen(arg))
  581. {
  582. #ifndef NO_EQUALIZER
  583. /* Simple EQ: SEQ <BASS> <MID> <TREBLE> */
  584. if (!strcasecmp(cmd, "SEQ")) {
  585. double b,m,t;
  586. int cn;
  587. if(sscanf(arg, "%lf %lf %lf", &b, &m, &t) == 3)
  588. {
  589. /* Consider adding mpg123_seq()... but also, on could define a nicer courve for that. */
  590. if ((t >= 0) && (t <= 3))
  591. for(cn=0; cn < 1; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, b);
  592. if ((m >= 0) && (m <= 3))
  593. for(cn=1; cn < 2; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, m);
  594. if ((b >= 0) && (b <= 3))
  595. for(cn=2; cn < 32; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, t);
  596. generic_sendmsg("bass: %f mid: %f treble: %f", b, m, t);
  597. }
  598. else generic_sendmsg("E invalid arguments for SEQ: %s", arg);
  599. continue;
  600. }
  601. /* Equalizer control :) (JMG) */
  602. if (!strcasecmp(cmd, "E") || !strcasecmp(cmd, "EQ")) {
  603. double e; /* ThOr: equalizer is of type real... whatever that is */
  604. int c, v;
  605. /*generic_sendmsg("%s",updown);*/
  606. if(sscanf(arg, "%i %i %lf", &c, &v, &e) == 3)
  607. {
  608. if(mpg123_eq(fr, c, v, e) == MPG123_OK)
  609. generic_sendmsg("%i : %i : %f", c, v, e);
  610. else
  611. generic_sendmsg("E failed to set eq: %s", mpg123_strerror(fr));
  612. }
  613. else generic_sendmsg("E invalid arguments for EQ: %s", arg);
  614. continue;
  615. }
  616. if(!strcasecmp(cmd, "EQFILE"))
  617. {
  618. equalfile = arg;
  619. if(load_equalizer(fr) == 0)
  620. generic_sendmsg("EQFILE done");
  621. else
  622. generic_sendmsg("E failed to parse given eq file");
  623. continue;
  624. }
  625. #endif
  626. /* SEEK to a sample offset */
  627. if(!strcasecmp(cmd, "K") || !strcasecmp(cmd, "SEEK"))
  628. {
  629. off_t soff;
  630. off_t oldpos;
  631. off_t newpos;
  632. char *spos = arg;
  633. int whence = SEEK_SET;
  634. if(mode == MODE_STOPPED)
  635. {
  636. generic_sendmsg("E No track loaded!");
  637. continue;
  638. }
  639. oldpos = mpg123_tell(fr);
  640. soff = (off_t) atobigint(spos);
  641. if(spos[0] == '-' || spos[0] == '+') whence = SEEK_CUR;
  642. if(0 > (soff = mpg123_seek(fr, soff, whence)))
  643. {
  644. generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr));
  645. mpg123_seek(fr, 0, SEEK_SET);
  646. }
  647. out123_drop(ao);
  648. newpos = mpg123_tell(fr);
  649. if(newpos <= oldpos) mpg123_meta_free(fr);
  650. generic_sendmsg("K %"OFF_P, (off_p)newpos);
  651. continue;
  652. }
  653. /* JUMP */
  654. if (!strcasecmp(cmd, "J") || !strcasecmp(cmd, "JUMP")) {
  655. char *spos;
  656. off_t offset;
  657. off_t oldpos;
  658. double secs;
  659. spos = arg;
  660. if(mode == MODE_STOPPED)
  661. {
  662. generic_sendmsg("E No track loaded!");
  663. continue;
  664. }
  665. oldpos = framenum;
  666. if(spos[strlen(spos)-1] == 's' && sscanf(arg, "%lf", &secs) == 1) offset = mpg123_timeframe(fr, secs);
  667. else offset = atol(spos);
  668. /* totally replaced that stuff - it never fully worked
  669. a bit usure about why +pos -> spos+1 earlier... */
  670. if (spos[0] == '-' || spos[0] == '+') offset += framenum;
  671. if(0 > (framenum = mpg123_seek_frame(fr, offset, SEEK_SET)))
  672. {
  673. generic_sendmsg("E Error while seeking");
  674. mpg123_seek_frame(fr, 0, SEEK_SET);
  675. }
  676. out123_drop(ao);
  677. if(framenum <= oldpos) mpg123_meta_free(fr);
  678. generic_sendmsg("J %d", framenum);
  679. continue;
  680. }
  681. /* VOLUME in percent */
  682. if(!strcasecmp(cmd, "V") || !strcasecmp(cmd, "VOLUME"))
  683. {
  684. double v;
  685. mpg123_volume(fr, atof(arg)/100);
  686. mpg123_getvolume(fr, &v, NULL, NULL); /* Necessary? */
  687. generic_sendmsg("V %f%%", v * 100);
  688. continue;
  689. }
  690. /* PITCH (playback speed) in percent */
  691. if(!strcasecmp(cmd, "PITCH"))
  692. {
  693. double p;
  694. if(sscanf(arg, "%lf", &p) == 1)
  695. {
  696. set_pitch(fr, ao, p);
  697. generic_sendmsg("PITCH %f", param.pitch);
  698. }
  699. else generic_sendmsg("E invalid arguments for PITCH: %s", arg);
  700. continue;
  701. }
  702. /* RVA mode */
  703. if(!strcasecmp(cmd, "RVA"))
  704. {
  705. if(!strcasecmp(arg, "off")) param.rva = MPG123_RVA_OFF;
  706. else if(!strcasecmp(arg, "mix") || !strcasecmp(arg, "radio")) param.rva = MPG123_RVA_MIX;
  707. else if(!strcasecmp(arg, "album") || !strcasecmp(arg, "audiophile")) param.rva = MPG123_RVA_ALBUM;
  708. mpg123_volume_change(fr, 0.);
  709. generic_sendmsg("RVA %s", rva_name[param.rva]);
  710. continue;
  711. }
  712. /* LOAD - actually play */
  713. if (!strcasecmp(cmd, "L") || !strcasecmp(cmd, "LOAD")){ generic_load(fr, arg, MODE_PLAYING); continue; }
  714. if (!strcasecmp(cmd, "LL") || !strcasecmp(cmd, "LOADLIST")){ generic_loadlist(fr, arg); continue; }
  715. /* LOADPAUSED */
  716. if (!strcasecmp(cmd, "LP") || !strcasecmp(cmd, "LOADPAUSED")){ generic_load(fr, arg, MODE_PAUSED); continue; }
  717. /* no command matched */
  718. generic_sendmsg("E Unknown command: %s", cmd); /* unknown command */
  719. } /* end commands with arguments */
  720. else generic_sendmsg("E Unknown command or no arguments: %s", comstr); /* unknown command */
  721. } /* end of single command processing */
  722. } /* end of scanning the command buffer */
  723. /*
  724. when last command had no \n... should I discard it?
  725. Ideally, I should remember the part and wait for next
  726. read() to get the rest up to a \n. But that can go
  727. to infinity. Too long commands too quickly are just
  728. bad. Cannot/Won't change that. So, discard the unfinished
  729. command and have fingers crossed that the rest of this
  730. unfinished one qualifies as "unknown".
  731. */
  732. if(buf[len-1] != 0)
  733. {
  734. char lasti = buf[len-1];
  735. buf[len-1] = 0;
  736. generic_sendmsg("E Unfinished command: %s%c", comstr, lasti);
  737. }
  738. } /* end command reading & processing */
  739. } /* end main (alive) loop */
  740. debug("going to end");
  741. /* quit gracefully */
  742. debug("closing control");
  743. #ifdef FIFO
  744. #if WANT_WIN32_FIFO
  745. win32_fifo_close();
  746. #else
  747. close(control_file); /* be it FIFO or STDIN */
  748. if(param.fifo) unlink(param.fifo);
  749. #endif /* WANT_WIN32_FIFO */
  750. #endif
  751. debug("control_generic returning");
  752. return 0;
  753. }
  754. /* EOF */