player.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <thread.h>
  4. #include <fcall.h>
  5. #include "pool.h"
  6. #include "playlist.h"
  7. enum {
  8. Pac,
  9. Mp3,
  10. Pcm,
  11. Ogg,
  12. };
  13. typedef struct Playfd Playfd;
  14. struct Playfd {
  15. /* Describes a file to play for starting up pac4dec/mp3,... */
  16. char *filename; /* mallocated */
  17. int fd; /* filedesc to use */
  18. int cfd; /* fildesc to close */
  19. };
  20. Channel *full, *empty, *playout, *spare;
  21. Channel *playc, *pacc;
  22. char *playprog[] = {
  23. [Pac] = "/bin/games/pac4dec",
  24. [Mp3] = "/bin/games/mp3dec",
  25. [Pcm] = "/bin/cp",
  26. [Ogg] = "/bin/games/vorbisdec",
  27. };
  28. ulong totbytes, totbuffers;
  29. static char curfile[8192];
  30. void
  31. pac4dec(void *a)
  32. {
  33. Playfd *pfd;
  34. Pacbuf *pb;
  35. int fd, type;
  36. char *ext, buf[256];
  37. static char args[6][32];
  38. char *argv[6] = {args[0], args[1], args[2], args[3], args[4], args[5]};
  39. threadsetname("pac4dec");
  40. pfd = a;
  41. close(pfd->cfd); /* read fd */
  42. ext = strrchr(pfd->filename, '.');
  43. fd = open(pfd->filename, OREAD);
  44. if (fd < 0 && ext == nil){
  45. // Try the alternatives
  46. ext = buf + strlen(pfd->filename);
  47. snprint(buf, sizeof buf, "%s.pac", pfd->filename);
  48. fd = open(buf, OREAD);
  49. if (fd < 0){
  50. snprint(buf, sizeof buf, "%s.mp3", pfd->filename);
  51. fd = open(buf, OREAD);
  52. }
  53. if (fd < 0){
  54. snprint(buf, sizeof buf, "%s.ogg", pfd->filename);
  55. fd = open(buf, OREAD);
  56. }
  57. if (fd < 0){
  58. snprint(buf, sizeof buf, "%s.pcm", pfd->filename);
  59. fd = open(buf, OREAD);
  60. }
  61. }
  62. if (fd < 0){
  63. if (debug & DbgPlayer)
  64. fprint(2, "pac4dec: %s: %r", pfd->filename);
  65. pb = nbrecvp(spare);
  66. pb->cmd = Error;
  67. pb->off = 0;
  68. pb->len = snprint(pb->data, sizeof(pb->data), "startplay: %s: %r", pfd->filename);
  69. sendp(full, pb);
  70. threadexits("open");
  71. }
  72. dup(pfd->fd, 1);
  73. close(pfd->fd);
  74. if(ext == nil || strcmp(ext, ".pac") == 0){
  75. type = Pac;
  76. snprint(args[0], sizeof args[0], "pac4dec");
  77. snprint(args[1], sizeof args[1], "/fd/%d", fd);
  78. snprint(args[2], sizeof args[2], "/fd/1");
  79. argv[3] = nil;
  80. }else if(strcmp(ext, ".mp3") == 0){
  81. type = Mp3;
  82. snprint(args[0], sizeof args[0], "mp3dec");
  83. snprint(args[1], sizeof args[1], "-q");
  84. snprint(args[2], sizeof args[1], "-s");
  85. snprint(args[3], sizeof args[1], "/fd/%d", fd);
  86. argv[4] = nil;
  87. }else if(strcmp(ext, ".ogg") == 0){
  88. type = Ogg;
  89. snprint(args[0], sizeof args[0], "vorbisdec");
  90. argv[1] = nil;
  91. argv[2] = nil;
  92. argv[3] = nil;
  93. dup(fd, 0);
  94. }else{
  95. type = Pcm;
  96. snprint(args[0], sizeof args[0], "cat");
  97. snprint(args[1], sizeof args[1], "/fd/%d", fd);
  98. argv[2] = nil;
  99. argv[3] = nil;
  100. }
  101. free(pfd->filename);
  102. free(pfd);
  103. if (debug & DbgPlayer)
  104. fprint(2, "procexecl %s %s %s %s\n",
  105. playprog[type], argv[0], argv[1], argv[2]);
  106. procexec(nil, playprog[type], argv);
  107. if((pb = nbrecvp(spare)) == nil)
  108. pb = malloc(sizeof(Pacbuf));
  109. pb->cmd = Error;
  110. pb->off = 0;
  111. pb->len = snprint(pb->data, sizeof(pb->data), "startplay: %s: exec", playprog[type]);
  112. sendp(full, pb);
  113. threadexits(playprog[type]);
  114. }
  115. static int
  116. startplay(ushort n)
  117. {
  118. int fd[2];
  119. Playfd *pfd;
  120. char *file;
  121. file = getplaylist(n);
  122. if(file == nil)
  123. return Undef;
  124. if (debug & DbgPlayer)
  125. fprint(2, "startplay: file is `%s'\n", file);
  126. if(pipe(fd) < 0)
  127. sysfatal("pipe: %r");
  128. pfd = malloc(sizeof(Playfd));
  129. pfd->filename = file; /* mallocated already */
  130. pfd->fd = fd[1];
  131. pfd->cfd = fd[0];
  132. procrfork(pac4dec, pfd, 4096, RFFDG);
  133. close(fd[1]); /* write fd, for pac4dec */
  134. return fd[0]; /* read fd */
  135. }
  136. static void
  137. rtsched(void)
  138. {
  139. int fd;
  140. char *ctl;
  141. ctl = smprint("/proc/%ud/ctl", getpid());
  142. if((fd = open(ctl, ORDWR)) < 0)
  143. sysfatal("%s: %r", ctl);
  144. if(fprint(fd, "period 20ms") < 0)
  145. sysfatal("%s: %r", ctl);
  146. if(fprint(fd, "cost 100µs") < 0)
  147. sysfatal("%s: %r", ctl);
  148. if(fprint(fd, "sporadic") < 0)
  149. sysfatal("%s: %r", ctl);
  150. if(fprint(fd, "admit") < 0)
  151. sysfatal("%s: %r", ctl);
  152. close(fd);
  153. free(ctl);
  154. }
  155. void
  156. pacproc(void*)
  157. {
  158. Pmsg playstate, newstate;
  159. int fd;
  160. Pacbuf *pb;
  161. Alt a[3] = {
  162. {empty, &pb, CHANNOP},
  163. {playc, &newstate.m, CHANRCV},
  164. {nil, nil, CHANEND},
  165. };
  166. threadsetname("pacproc");
  167. close(srvfd[1]);
  168. newstate.cmd = playstate.cmd = Stop;
  169. newstate.off = playstate.off = 0;
  170. fd = -1;
  171. for(;;){
  172. switch(alt(a)){
  173. case 0:
  174. /* Play out next buffer (pb points to one already) */
  175. assert(fd >= 0); /* Because we must be in Play mode */
  176. pb->m = playstate.m;
  177. pb->len = read(fd, pb->data, sizeof pb->data);
  178. if(pb->len > 0){
  179. sendp(full, pb);
  180. break;
  181. }
  182. if(pb->len < 0){
  183. if(debug & DbgPlayer)
  184. fprint(2, "pac, error: %d\n", playstate.off);
  185. pb->cmd = Error;
  186. pb->len = snprint(pb->data, sizeof pb->data, "%s: %r", curfile);
  187. sendp(full, pb);
  188. }else{
  189. /* Simple end of file */
  190. sendp(empty, pb); /* Don't need buffer after all */
  191. }
  192. close(fd);
  193. fd = -1;
  194. if(debug & DbgPlayer)
  195. fprint(2, "pac, eof: %d\n", playstate.off);
  196. /* End of file, do next by falling through */
  197. newstate.cmd = playstate.cmd;
  198. newstate.off = playstate.off + 1;
  199. case 1:
  200. if((debug & DbgPac) && newstate.cmd)
  201. fprint(2, "Pacproc: newstate %s-%d, playstate %s-%d\n",
  202. statetxt[newstate.cmd], newstate.off,
  203. statetxt[playstate.cmd], playstate.off);
  204. /* Deal with an incoming command */
  205. if(newstate.cmd == Pause || newstate.cmd == Resume){
  206. /* Just pass them on, don't change local state */
  207. pb = recvp(spare);
  208. pb->m = newstate.m;
  209. sendp(full, pb);
  210. break;
  211. }
  212. /* Stop whatever we're doing */
  213. if(fd >= 0){
  214. if(debug & DbgPlayer)
  215. fprint(2, "pac, stop\n");
  216. /* Stop any active (pac) decoders */
  217. close(fd);
  218. fd = -1;
  219. }
  220. a[0].op = CHANNOP;
  221. switch(newstate.cmd){
  222. default:
  223. sysfatal("pacproc: unexpected newstate %d", newstate.cmd);
  224. case Stop:
  225. /* Wait for state to change */
  226. break;
  227. case Skip:
  228. case Play:
  229. fd = startplay(newstate.off);
  230. if(fd >=0){
  231. playstate = newstate;
  232. a[0].op = CHANRCV;
  233. continue; /* Start reading */
  234. }
  235. newstate.cmd = Stop;
  236. }
  237. pb = recvp(spare);
  238. pb->m = newstate.m;
  239. sendp(full, pb);
  240. playstate = newstate;
  241. }
  242. }
  243. }
  244. void
  245. pcmproc(void*)
  246. {
  247. Pmsg localstate, newstate, prevstate;
  248. int fd, n;
  249. Pacbuf *pb, *b;
  250. Alt a[3] = {
  251. {full, &pb, CHANRCV},
  252. {playout, &pb, CHANRCV},
  253. {nil, nil, CHANEND},
  254. };
  255. /*
  256. * This is the real-time proc.
  257. * It gets its input from two sources, full data/control buffers from the pacproc
  258. * which mixes decoded data with control messages, and data buffers from the pcmproc's
  259. * (*this* proc's) own internal playout buffer.
  260. * When a command is received on the `full' channel containing a command that warrants
  261. * an immediate change of audio source (e.g., to silence or to another number), we just
  262. * toss everything in the pipeline -- i.e., the playout channel
  263. * Finally, we report all state changes using `playupdate' (another message channel)
  264. */
  265. threadsetname("pcmproc");
  266. close(srvfd[1]);
  267. fd = -1;
  268. localstate.cmd = 0; /* Force initial playupdate */
  269. newstate.cmd = Stop;
  270. newstate.off = 0;
  271. // rtsched();
  272. for(;;){
  273. if(newstate.m != localstate.m){
  274. playupdate(newstate, nil);
  275. localstate = newstate;
  276. }
  277. switch(alt(a)){
  278. case 0:
  279. /* buffer received from pacproc */
  280. if((debug & DbgPcm) && localstate.m != prevstate.m){
  281. fprint(2, "pcm, full: %s-%d, local state is %s-%d\n",
  282. statetxt[pb->cmd], pb->off,
  283. statetxt[localstate.cmd], localstate.off);
  284. prevstate.m = localstate.m;
  285. }
  286. switch(pb->cmd){
  287. default:
  288. sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);
  289. case Resume:
  290. a[1].op = CHANRCV;
  291. newstate.cmd = Play;
  292. break;
  293. case Pause:
  294. a[1].op = CHANNOP;
  295. newstate.cmd = Pause;
  296. if(fd >= 0){
  297. close(fd);
  298. fd = -1;
  299. }
  300. break;
  301. case Stop:
  302. /* Dump all data in the buffer */
  303. while(b = nbrecvp(playout))
  304. if(b->cmd == Error){
  305. playupdate(b->Pmsg, b->data);
  306. sendp(spare, b);
  307. }else
  308. sendp(empty, b);
  309. newstate.m = pb->m;
  310. a[1].op = CHANRCV;
  311. if(fd >= 0){
  312. close(fd);
  313. fd = -1;
  314. }
  315. break;
  316. case Skip:
  317. /* Dump all data in the buffer, then fall through */
  318. while(b = nbrecvp(playout))
  319. if(b->cmd == Error){
  320. playupdate(pb->Pmsg, pb->data);
  321. sendp(spare, pb);
  322. }else
  323. sendp(empty, b);
  324. a[1].op = CHANRCV;
  325. newstate.cmd = Play;
  326. case Error:
  327. case Play:
  328. /* deal with at playout, just requeue */
  329. sendp(playout, pb);
  330. pb = nil;
  331. localstate = newstate;
  332. break;
  333. }
  334. /* If we still have a buffer, free it */
  335. if(pb)
  336. sendp(spare, pb);
  337. break;
  338. case 1:
  339. /* internal buffer */
  340. if((debug & DbgPlayer) && localstate.m != prevstate.m){
  341. fprint(2, "pcm, playout: %s-%d, local state is %s-%d\n",
  342. statetxt[pb->cmd], pb->off,
  343. statetxt[localstate.cmd], localstate.off);
  344. prevstate.m = localstate.m;
  345. }
  346. switch(pb->cmd){
  347. default:
  348. sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);
  349. case Error:
  350. playupdate(pb->Pmsg, pb->data);
  351. localstate = newstate;
  352. sendp(spare, pb);
  353. break;
  354. case Play:
  355. if(fd < 0 && (fd = open("/dev/audio", OWRITE)) < 0){
  356. a[1].op = CHANNOP;
  357. newstate.cmd = Pause;
  358. pb->cmd = Error;
  359. snprint(pb->data, sizeof(pb->data),
  360. "/dev/audio: %r");
  361. playupdate(pb->Pmsg, pb->data);
  362. sendp(empty, pb);
  363. break;
  364. }
  365. /* play out this buffer */
  366. totbytes += pb->len;
  367. totbuffers++;
  368. n = write(fd, pb->data, pb->len);
  369. if (n != pb->len){
  370. if (debug & DbgPlayer)
  371. fprint(2, "pcmproc: file %d: %r\n", pb->off);
  372. if (n < 0)
  373. sysfatal("pcmproc: write: %r");
  374. }
  375. newstate.m = pb->m;
  376. sendp(empty, pb);
  377. break;
  378. }
  379. break;
  380. }
  381. }
  382. }
  383. void
  384. playinit(void)
  385. {
  386. int i;
  387. full = chancreate(sizeof(Pacbuf*), 1);
  388. empty = chancreate(sizeof(Pacbuf*), NPacbuf);
  389. spare = chancreate(sizeof(Pacbuf*), NSparebuf);
  390. playout = chancreate(sizeof(Pacbuf*), NPacbuf+NSparebuf);
  391. for(i = 0; i < NPacbuf; i++)
  392. sendp(empty, malloc(sizeof(Pacbuf)));
  393. for(i = 0; i < NSparebuf; i++)
  394. sendp(spare, malloc(sizeof(Pacbuf)));
  395. playc = chancreate(sizeof(Pmsg), 1);
  396. procrfork(pacproc, nil, 32*1024, RFFDG);
  397. procrfork(pcmproc, nil, 32*1024, RFFDG);
  398. }
  399. char *
  400. getplaystat(char *p, char *e)
  401. {
  402. p = seprint(p, e, "empty buffers %d of %d\n", empty->n, empty->s);
  403. p = seprint(p, e, "full buffers %d of %d\n", full->n, full->s);
  404. p = seprint(p, e, "playout buffers %d of %d\n", playout->n, playout->s);
  405. p = seprint(p, e, "spare buffers %d of %d\n", spare->n, spare->s);
  406. p = seprint(p, e, "bytes %lud / buffers %lud played\n", totbytes, totbuffers);
  407. return p;
  408. }