wmmemfree: Parse modern /proc/meminfo
[dockapps.git] / ascd / libworkman / cddaslave.c
blob492ceeb09f70a5f74f0ca1c3f2ee3aca1b80658c
1 /*
2 * $id$
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 ******************************************************************
26 * Digital audio manipulator for WorkMan.
28 * The CDDA architecture looks like this:
30 * WorkMan (or another UI!)
31 * ^^^
32 * ||| (separate processes connected by pipe)
33 * vvv
34 * +------------- cddaslave -------------+
35 * | | |
36 * command module CDDA reader audio output
37 * (portable) (per platform) (per platform)
39 * This source file has the command module and some of the scaffolding
40 * to hold cddaslave together, plus some non-system-dependent audio
41 * processing code. Look in plat_*_cdda.c for system-specific stuff.
45 #include "config.h"
47 #ifdef BUILD_CDDA
49 static char cddaslave_id[] = "$Id: cddaslave.c,v 1.3 1999/02/14 22:10:24 dirk Exp $";
51 #include <stdio.h>
52 #include <sys/types.h>
53 #include <sys/time.h>
54 #include "include/wm_cdda.h"
55 #include "include/wm_platform.h"
58 int playing = 0; /* Should the CD be playing now? */
61 * Loudness setting, plus the floating volume multiplier and decaying-average
62 * volume level.
64 int loudness = 0;
65 unsigned int volume = 32768;
66 unsigned int level;
69 * Playback speed (0 = slow)
71 int speed = 128;
74 * This is non-null if we're saving audio to a file.
76 FILE *output = NULL;
79 * Audio file header format.
81 typedef unsigned long u_32;
82 struct auheader {
83 u_32 magic;
84 u_32 hdr_size;
85 u_32 data_size;
86 u_32 encoding;
87 u_32 sample_rate;
88 u_32 channels;
91 /* had to change #ifdef to #if -> see wm_cdda.h */
92 #if WM_BIG_ENDIAN
93 # ifndef htonl
94 # define htonl(x) (x)
95 # endif
96 #else
97 extern unsigned long htonl(x);
98 #endif
100 void *malloc();
101 long cdda_transform();
104 * Send status information upstream.
106 void
107 send_status(blk)
108 struct cdda_block *blk;
110 write(1, blk, sizeof(*blk));
114 * Accept a command from our master.
116 * The protocol is byte-oriented:
117 * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
118 * xyz is the msf of the start of this chunk, i.e., the
119 * ending boundary for reverse play.
120 * S Stop.
121 * E Eject. This means we just close the CD device and
122 * open it again later.
123 * Q Quit.
124 * Vn Set volume level (0-255).
125 * Bn Set balance level (0-255).
126 * EnL Set an equalizer level (n = 0 for bass, 255 for treble)
127 * G Get current status.
128 * sn Set speed multiplier to n.
129 * dn Set direction to forward (n = 0) or reverse.
130 * Fllllx... Start saving to a file (length = l, followed by name)
131 * F0000 Stop saving.
132 * Ln Set loudness level (0-255).
135 command(cd_fd, blk)
136 int cd_fd;
137 struct cdda_block *blk;
139 unsigned char inbuf[10];
140 char *filename;
141 int namelen;
142 struct auheader hdr;
144 if (read(0, inbuf, 1) <= 0) /* Parent died. */
146 wmcdda_close();
147 wmaudio_close();
148 exit(0);
151 switch (inbuf[0]) {
152 case 'E':
153 playing = 0;
154 wmaudio_stop();
155 wmcdda_close(cd_fd);
156 cd_fd = -1;
157 blk->status = WMCDDA_ACK;
158 send_status(blk);
159 break;
160 case 'P':
161 read(0, inbuf, 9);
162 playing = 1;
164 wmaudio_stop();
166 wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
167 inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
168 inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
170 wmaudio_ready();
172 level = 2500;
173 volume = 1 << 15;
175 blk->status = WMCDDA_ACK;
176 send_status(blk);
177 break;
179 case 'S':
180 playing = 0;
181 wmaudio_stop();
182 blk->status = WMCDDA_ACK;
183 send_status(blk);
184 blk->status = WMCDDA_STOPPED;
185 send_status(blk);
186 break;
188 case 'B':
189 read(0, inbuf, 1);
190 wmaudio_balance(inbuf[0]);
191 blk->status = WMCDDA_ACK;
192 send_status(blk);
193 break;
195 case 'V':
196 read(0, inbuf, 1);
197 wmaudio_volume(inbuf[0]);
198 blk->status = WMCDDA_ACK;
199 send_status(blk);
200 break;
202 case 'G':
203 blk->status = WMCDDA_ACK;
204 send_status(blk);
206 if (playing)
207 blk->status = WMCDDA_PLAYED;
208 else
209 blk->status = WMCDDA_STOPPED;
210 wmaudio_state(blk);
211 send_status(blk);
212 break;
214 case 'Q':
215 blk->status = WMCDDA_ACK;
216 send_status(blk);
217 wmcdda_close();
218 wmaudio_close();
219 exit(0);
221 case 's':
222 read(0, inbuf, 1);
223 speed = inbuf[0];
224 wmcdda_speed(speed);
225 blk->status = WMCDDA_ACK;
226 send_status(blk);
227 break;
229 case 'd':
230 read(0, inbuf, 1);
231 wmcdda_direction(inbuf[0]);
232 blk->status = WMCDDA_ACK;
233 send_status(blk);
234 break;
236 case 'L':
237 read(0, inbuf, 1);
238 loudness = inbuf[0];
239 blk->status = WMCDDA_ACK;
240 send_status(blk);
241 break;
243 case 'F':
244 read(0, &namelen, sizeof(namelen));
245 if (output != NULL)
247 fclose(output);
248 output = NULL;
250 if (namelen)
252 filename = malloc(namelen + 1);
253 if (filename == NULL)
255 perror("cddaslave");
256 wmcdda_close();
257 wmaudio_close();
258 exit(1);
261 read(0, filename, namelen);
262 filename[namelen] = '\0';
263 output = fopen(filename, "w");
264 if (output == NULL)
265 perror(filename);
266 else
268 /* Write an .au file header. */
269 hdr.magic = htonl(0x2e736e64);
270 hdr.hdr_size = htonl(sizeof(hdr) + 28);
271 hdr.data_size = htonl(~0);
272 hdr.encoding = htonl(3); /* linear-16 */
273 hdr.sample_rate = htonl(44100);
274 hdr.channels = htonl(2);
276 fwrite(&hdr, sizeof(hdr), 1, output);
277 fwrite("Recorded from CD by WorkMan", 28, 1,
278 output);
281 free(filename);
284 blk->status = WMCDDA_ACK;
285 send_status(blk);
287 return(cd_fd);
291 * Transform some CDDA data.
293 long
294 wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
296 long i;
297 long *buf32 = (long *)rawbuf;
298 register short *buf16 = (short *)rawbuf;
299 register int aval;
302 * Loudness transformation. Basically this is a self-adjusting
303 * volume control; our goal is to keep the average output level
304 * around a certain value (2500 seems to be pleasing.) We do this
305 * by maintaining a decaying average of the recent output levels
306 * (where "recent" is some fraction of a second.) All output levels
307 * are multiplied by the inverse of the decaying average; this has
308 * the volume-leveling effect we desire, and isn't too CPU-intensive.
310 * This is done by modifying the digital data, rather than adjusting
311 * the system volume control, because (at least on some systems)
312 * tweaking the system volume can generate little pops and clicks.
314 * There's probably a more elegant way to achieve this effect, but
315 * what the heck, I never took a DSP class and am making this up as
316 * I go along, with a little help from some friends.
318 * This is all done with fixed-point math, oriented around powers
319 * of two, which with luck will keep the CPU usage to a minimum.
320 * More could probably be done, for example using lookup tables to
321 * replace multiplies and divides; whether the memory hit (128K
322 * for each table) is worthwhile is unclear.
324 if (loudness)
326 /* We aren't really going backwards, but i > 0 is fast */
327 for (i = buflen / 2; i > 0; i--)
330 * Adjust this sample to the current level.
332 aval = (*buf16 = (((long)*buf16) * volume) >> 15);
333 buf16++;
336 * Don't adjust the decaying average for each sample;
337 * that just spends CPU time for very little benefit.
339 if (i & 127)
340 continue;
343 * We want to use absolute values to compute the
344 * decaying average; otherwise it'd sit around 0.
346 if (aval < 0)
347 aval = -aval;
350 * Adjust more quickly when we start hitting peaks,
351 * or we'll get clipping when there's a sudden loud
352 * section after lots of quiet.
354 if (aval & ~8191)
355 aval <<= 3;
358 * Adjust the decaying average.
360 level = ((level << 11) - level + aval) >> 11;
363 * Let *really* quiet sounds play softly, or we'll
364 * amplify background hiss to full volume and blast
365 * the user's speakers when real sound starts up.
367 if (! (level & ~511))
368 level = 512;
371 * And adjust the volume setting using the inverse
372 * of the decaying average.
374 volume = (2500 << 15) / level;
378 if (speed == 128)
379 return (buflen);
382 * Half-speed play. Stretch things out.
384 if (speed == 0)
386 buflen /= 2; /* Since we're moving 32 bits at a time */
388 for (i = buflen - 1; i > 0; i--)
390 buf32[i] = buf32[i / 2];
393 buflen *= 4; /* 2 for doubling the buffer, 2 from above */
397 * Slow play; can't optimize it as well as half-speed.
399 if (speed && speed < 128)
401 int multiplier = ((speed + 128) * 128) / 256;
402 int newlen;
403 int tally = 0, pos;
405 buflen /= 4; /* Get the number of 32-bit values */
408 * Buffer length doubles when speed is 0, stays the same
409 * when speed is 128.
411 newlen = (buflen * 128) / multiplier;
413 pos = buflen - 1;
414 for (i = newlen - 1; i > 0; i--)
416 buf32[i] = buf32[pos];
417 tally += multiplier;
418 if (tally & 128)
420 pos--;
421 tally ^= 128;
425 buflen = newlen * 4;
428 return (buflen);
432 main(argc, argv)
433 char **argv;
435 int cd_fd = 3;
436 fd_set readfd, dummyfd;
437 struct timeval timeout;
438 char *cddabuf;
439 long cddabuflen;
440 struct cdda_block blockinfo;
441 long result;
442 int nfds;
443 char *devname;
446 * Device name should be the command-line argument.
448 if (argc < 2)
449 devname = "";
450 else
451 devname = argv[1];
454 * If we're running setuid root, bump up our priority then lose
455 * superuser access.
457 nice(-14);
458 setuid(getuid());
460 FD_ZERO(&dummyfd);
461 FD_ZERO(&readfd);
463 timerclear(&timeout);
465 cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
466 if (cd_fd < 0)
467 exit(1);
468 wmaudio_init();
470 blockinfo.status = WMCDDA_ACK;
471 send_status(&blockinfo);
472 blockinfo.status = WMCDDA_STOPPED;
475 * Accept commands as they come in, and play some sound if we're
476 * supposed to be doing that.
478 while (1)
480 FD_SET(0, &readfd);
483 * If we're playing, we don't want select to block. Otherwise,
484 * wait a little while for the next command.
486 if (playing)
487 timeout.tv_usec = 0;
488 else
489 timeout.tv_usec = 500000;
491 nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
493 if (nfds < 0) /* Broken pipe; our GUI exited. */
495 wmcdda_close(cd_fd);
496 wmaudio_close();
497 exit(0);
500 if (FD_ISSET(0, &readfd))
502 /* If this doesn't work, just hope for the best */
503 if(cd_fd == -1)
504 cd_fd = wmcdda_open(devname);
505 cd_fd = command(cd_fd, &blockinfo);
507 * Process all commands in rapid succession, rather
508 * than possibly waiting for a CDDA read.
510 continue;
513 if (playing)
515 result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
516 &blockinfo);
517 if (result <= 0)
519 /* Let the output queue drain. */
520 if (blockinfo.status == WMCDDA_DONE)
522 wmaudio_mark_last();
523 if (wmaudio_send_status())
525 /* queue drained, stop polling*/
526 playing = 0;
529 else
531 playing = 0;
532 send_status(&blockinfo);
535 else
537 result = wmcdda_normalize(cddabuf, result,
538 &blockinfo);
539 result = wmcdda_transform(cddabuf, result,
540 &blockinfo);
541 if (output)
542 fwrite(cddabuf, result, 1, output);
543 result = wmaudio_convert(cddabuf, result,
544 &blockinfo);
545 if (wmaudio_play(cddabuf, result, &blockinfo))
547 playing = 0;
548 wmaudio_stop();
549 send_status(&blockinfo);
553 else
554 send_status(&blockinfo);
558 #else /* BUILD_CDDA */
560 main()
562 exit(0);
565 #endif /* BUILD_CDDA */