2
* @(#)cddaslave.c 1.11 13 Sep 1995
4
* Digital audio manipulator for WorkMan.
6
* The CDDA architecture looks like this:
8
* WorkMan (or another UI!)
10
* ||| (separate processes connected by pipe)
12
* +------------- cddaslave -------------+
14
* command module CDDA reader audio output
15
* (portable) (per platform) (per platform)
17
* This source file has the command module and some of the scaffolding
18
* to hold cddaslave together, plus some non-system-dependent audio
19
* processing code. Look in plat_*_cdda.c for system-specific stuff.
23
#include "libwm/include/wm_config.h"
24
#include "libwm/include/wm_cdda.h"
26
#ifdef BUILD_CDDA /* { */
29
#include <sys/types.h>
32
int playing = 0; /* Should the CD be playing now? */
35
* Loudness setting, plus the floating volume multiplier and decaying-average
39
unsigned int volume = 32768;
43
* Playback speed (0 = slow)
48
* This is non-null if we're saving audio to a file.
53
* Audio file header format.
55
typedef unsigned long u_32;
70
extern unsigned long htonl(x);
74
long cdda_transform();
77
* Send status information upstream.
80
send_status(struct cdda_block *blk)
82
write(1, blk, sizeof(*blk));
86
* Accept a command from our master.
88
* The protocol is byte-oriented:
89
* PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
90
* xyz is the msf of the start of this chunk, i.e., the
91
* ending boundary for reverse play.
94
* Vn Set volume level (0-255).
95
* Bn Set balance level (0-255).
96
* EnL Set an equalizer level (n = 0 for bass, 255 for treble)
97
* G Get current status.
98
* sn Set speed multiplier to n.
99
* dn Set direction to forward (n = 0) or reverse.
100
* Fllllx... Start saving to a file (length = l, followed by name)
102
* Ln Set loudness level (0-255).
105
command(int cd_fd, struct cdda_block *blk)
107
unsigned char inbuf[10];
112
if (read(0, inbuf, 1) <= 0) /* Parent died. */
126
wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
127
inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
128
inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
135
blk->status = WMCDDA_ACK;
142
blk->status = WMCDDA_ACK;
144
blk->status = WMCDDA_STOPPED;
150
wmaudio_balance(inbuf[0]);
151
blk->status = WMCDDA_ACK;
157
wmaudio_volume(inbuf[0]);
158
blk->status = WMCDDA_ACK;
163
blk->status = WMCDDA_ACK;
167
blk->status = WMCDDA_PLAYED;
169
blk->status = WMCDDA_STOPPED;
175
blk->status = WMCDDA_ACK;
185
blk->status = WMCDDA_ACK;
191
wmcdda_direction(inbuf[0]);
192
blk->status = WMCDDA_ACK;
199
blk->status = WMCDDA_ACK;
204
read(0, &namelen, sizeof(namelen));
212
filename = malloc(namelen + 1);
213
if (filename == NULL)
221
read(0, filename, namelen);
222
filename[namelen] = '\0';
223
output = fopen(filename, "w");
228
/* Write an .au file header. */
229
hdr.magic = htonl(0x2e736e64);
230
hdr.hdr_size = htonl(sizeof(hdr) + 28);
231
hdr.data_size = htonl(~0);
232
hdr.encoding = htonl(3); /* linear-16 */
233
hdr.sample_rate = htonl(44100);
234
hdr.channels = htonl(2);
236
fwrite(&hdr, sizeof(hdr), 1, output);
237
fwrite("Recorded from CD by WorkMan", 28, 1,
244
blk->status = WMCDDA_ACK;
250
* Transform some CDDA data.
253
wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
256
long *buf32 = (long *)rawbuf;
257
register short *buf16 = (short *)rawbuf;
261
* Loudness transformation. Basically this is a self-adjusting
262
* volume control; our goal is to keep the average output level
263
* around a certain value (2500 seems to be pleasing.) We do this
264
* by maintaining a decaying average of the recent output levels
265
* (where "recent" is some fraction of a second.) All output levels
266
* are multiplied by the inverse of the decaying average; this has
267
* the volume-leveling effect we desire, and isn't too CPU-intensive.
269
* This is done by modifying the digital data, rather than adjusting
270
* the system volume control, because (at least on some systems)
271
* tweaking the system volume can generate little pops and clicks.
273
* There's probably a more elegant way to achieve this effect, but
274
* what the heck, I never took a DSP class and am making this up as
275
* I go along, with a little help from some friends.
277
* This is all done with fixed-point math, oriented around powers
278
* of two, which with luck will keep the CPU usage to a minimum.
279
* More could probably be done, for example using lookup tables to
280
* replace multiplies and divides; whether the memory hit (128K
281
* for each table) is worthwhile is unclear.
285
/* We aren't really going backwards, but i > 0 is fast */
286
for (i = buflen / 2; i > 0; i--)
289
* Adjust this sample to the current level.
291
aval = (*buf16 = (((long)*buf16) * volume) >> 15);
295
* Don't adjust the decaying average for each sample;
296
* that just spends CPU time for very little benefit.
302
* We want to use absolute values to compute the
303
* decaying average; otherwise it'd sit around 0.
309
* Adjust more quickly when we start hitting peaks,
310
* or we'll get clipping when there's a sudden loud
311
* section after lots of quiet.
317
* Adjust the decaying average.
319
level = ((level << 11) - level + aval) >> 11;
322
* Let *really* quiet sounds play softly, or we'll
323
* amplify background hiss to full volume and blast
324
* the user's speakers when real sound starts up.
326
if (! (level & ~511))
330
* And adjust the volume setting using the inverse
331
* of the decaying average.
333
volume = (2500 << 15) / level;
341
* Half-speed play. Stretch things out.
345
buflen /= 2; /* Since we're moving 32 bits at a time */
347
for (i = buflen - 1; i > 0; i--)
349
buf32[i] = buf32[i / 2];
352
buflen *= 4; /* 2 for doubling the buffer, 2 from above */
356
* Slow play; can't optimize it as well as half-speed.
358
if (speed && speed < 128)
360
int multiplier = ((speed + 128) * 128) / 256;
364
buflen /= 4; /* Get the number of 32-bit values */
367
* Buffer length doubles when speed is 0, stays the same
370
newlen = (buflen * 128) / multiplier;
373
for (i = newlen - 1; i > 0; i--)
375
buf32[i] = buf32[pos];
395
fd_set readfd, dummyfd;
396
struct timeval timeout;
399
struct cdda_block blockinfo;
405
* Device name should be the command-line argument.
413
* If we're running setuid root, bump up our priority then lose
422
timerclear(&timeout);
424
cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
429
blockinfo.status = WMCDDA_ACK;
430
send_status(&blockinfo);
431
blockinfo.status = WMCDDA_STOPPED;
433
fprintf(stderr,"cddaslave: done init.");
435
* Accept commands as they come in, and play some sound if we're
436
* supposed to be doing that.
443
* If we're playing, we don't want select to block. Otherwise,
444
* wait a little while for the next command.
449
timeout.tv_usec = 500000;
451
nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
453
if (nfds < 0) /* Broken pipe; our GUI exited. */
457
fprintf(stderr,"cddaslave: Borken pipe; GUI must have exited.");
461
if (FD_ISSET(0, &readfd))
463
command(cd_fd, &blockinfo);
465
* Process all commands in rapid succession, rather
466
* than possibly waiting for a CDDA read.
473
result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
477
/* Let the output queue drain. */
478
if (blockinfo.status == WMCDDA_DONE)
481
if (wmaudio_send_status())
483
/* queue drained, stop polling*/
490
send_status(&blockinfo);
495
result = wmcdda_normalize(cddabuf, result,
497
result = wmcdda_transform(cddabuf, result,
500
fwrite(cddabuf, result, 1, output);
501
result = wmaudio_convert(cddabuf, result,
503
if (wmaudio_play(cddabuf, result, &blockinfo))
507
send_status(&blockinfo);
512
send_status(&blockinfo);
516
#else /* BUILD_CDDA } { */
521
printf("cddaslave: will work only on Solaris 2.4 or newer.");