~ubuntu-branches/ubuntu/hoary/kdemultimedia/hoary

« back to all changes in this revision

Viewing changes to kscd/cddaslave.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Schulze
  • Date: 2003-01-22 15:00:51 UTC
  • Revision ID: james.westby@ubuntu.com-20030122150051-uihwkdoxf15mi1tn
Tags: upstream-2.2.2
ImportĀ upstreamĀ versionĀ 2.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * @(#)cddaslave.c      1.11 13 Sep 1995
 
3
 *
 
4
 * Digital audio manipulator for WorkMan.
 
5
 *
 
6
 * The CDDA architecture looks like this:
 
7
 *
 
8
 *                     WorkMan  (or another UI!)
 
9
 *                       ^^^
 
10
 *                       |||    (separate processes connected by pipe)
 
11
 *                       vvv
 
12
 *     +------------- cddaslave -------------+
 
13
 *     |                  |                  |
 
14
 * command module    CDDA reader       audio output
 
15
 * (portable)        (per platform)    (per platform)
 
16
 *
 
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.
 
20
 */
 
21
 
 
22
 
 
23
#include "libwm/include/wm_config.h"
 
24
#include "libwm/include/wm_cdda.h"
 
25
 
 
26
#ifdef BUILD_CDDA /* { */
 
27
 
 
28
#include <stdio.h>
 
29
#include <sys/types.h>
 
30
#include <sys/time.h>
 
31
 
 
32
int     playing = 0;            /* Should the CD be playing now? */
 
33
 
 
34
/*
 
35
 * Loudness setting, plus the floating volume multiplier and decaying-average
 
36
 * volume level.
 
37
 */
 
38
int             loudness = 0;
 
39
unsigned int    volume = 32768;
 
40
unsigned int    level;
 
41
 
 
42
/*
 
43
 * Playback speed (0 = slow)
 
44
 */
 
45
int             speed = 128;
 
46
 
 
47
/*
 
48
 * This is non-null if we're saving audio to a file.
 
49
 */
 
50
FILE            *output = NULL;
 
51
 
 
52
/*
 
53
 * Audio file header format.
 
54
 */
 
55
typedef unsigned long   u_32;
 
56
struct auheader {
 
57
        u_32    magic;
 
58
        u_32    hdr_size;
 
59
        u_32    data_size;
 
60
        u_32    encoding;
 
61
        u_32    sample_rate;
 
62
        u_32    channels;
 
63
};
 
64
 
 
65
#ifdef BIG_ENDIAN
 
66
# ifndef htonl
 
67
#  define htonl(x) (x)
 
68
# endif
 
69
#else
 
70
extern unsigned long htonl(x);
 
71
#endif
 
72
 
 
73
void *malloc();
 
74
long cdda_transform();
 
75
 
 
76
/*
 
77
 * Send status information upstream.
 
78
 */
 
79
void
 
80
send_status(struct cdda_block *blk)
 
81
{
 
82
        write(1, blk, sizeof(*blk));
 
83
}
 
84
 
 
85
/*
 
86
 * Accept a command from our master.
 
87
 *
 
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.
 
92
 *   S                  Stop.
 
93
 *   Q                  Quit.
 
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)
 
101
 *   F0000              Stop saving.
 
102
 *   Ln                 Set loudness level (0-255).
 
103
 */
 
104
void
 
105
command(int cd_fd, struct cdda_block *blk)
 
106
{
 
107
        unsigned char           inbuf[10];
 
108
        char                    *filename;
 
109
        int                     namelen;
 
110
        struct auheader         hdr;
 
111
 
 
112
        if (read(0, inbuf, 1) <= 0)     /* Parent died. */
 
113
        {
 
114
                wmcdda_close();
 
115
                wmaudio_close();
 
116
                exit(0);
 
117
        }
 
118
 
 
119
        switch (inbuf[0]) {
 
120
        case 'P':
 
121
                read(0, inbuf, 9);
 
122
                playing = 1;
 
123
 
 
124
                wmaudio_stop();
 
125
 
 
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]);
 
129
 
 
130
                wmaudio_ready();
 
131
 
 
132
                level = 2500;
 
133
                volume = 1 << 15;
 
134
 
 
135
                blk->status = WMCDDA_ACK;
 
136
                send_status(blk);
 
137
                break;
 
138
 
 
139
        case 'S':
 
140
                playing = 0;
 
141
                wmaudio_stop();
 
142
                blk->status = WMCDDA_ACK;
 
143
                send_status(blk);
 
144
                blk->status = WMCDDA_STOPPED;
 
145
                send_status(blk);
 
146
                break;
 
147
 
 
148
        case 'B':
 
149
                read(0, inbuf, 1);
 
150
                wmaudio_balance(inbuf[0]);
 
151
                blk->status = WMCDDA_ACK;
 
152
                send_status(blk);
 
153
                break;
 
154
 
 
155
        case 'V':
 
156
                read(0, inbuf, 1);
 
157
                wmaudio_volume(inbuf[0]);
 
158
                blk->status = WMCDDA_ACK;
 
159
                send_status(blk);
 
160
                break;
 
161
 
 
162
        case 'G':
 
163
                blk->status = WMCDDA_ACK;
 
164
                send_status(blk);
 
165
 
 
166
                if (playing)
 
167
                        blk->status = WMCDDA_PLAYED;
 
168
                else
 
169
                        blk->status = WMCDDA_STOPPED;
 
170
                wmaudio_state(blk);
 
171
                send_status(blk);
 
172
                break;
 
173
 
 
174
        case 'Q':
 
175
                blk->status = WMCDDA_ACK;
 
176
                send_status(blk);
 
177
                wmcdda_close();
 
178
                wmaudio_close();
 
179
                exit(0);
 
180
 
 
181
        case 's':
 
182
                read(0, inbuf, 1);
 
183
                speed = inbuf[0];
 
184
                wmcdda_speed(speed);
 
185
                blk->status = WMCDDA_ACK;
 
186
                send_status(blk);
 
187
                break;
 
188
 
 
189
        case 'd':
 
190
                read(0, inbuf, 1);
 
191
                wmcdda_direction(inbuf[0]);
 
192
                blk->status = WMCDDA_ACK;
 
193
                send_status(blk);
 
194
                break;
 
195
 
 
196
        case 'L':
 
197
                read(0, inbuf, 1);
 
198
                loudness = inbuf[0];
 
199
                blk->status = WMCDDA_ACK;
 
200
                send_status(blk);
 
201
                break;
 
202
 
 
203
        case 'F':
 
204
                read(0, &namelen, sizeof(namelen));
 
205
                if (output != NULL)
 
206
                {
 
207
                        fclose(output);
 
208
                        output = NULL;
 
209
                }
 
210
                if (namelen)
 
211
                {
 
212
                        filename = malloc(namelen + 1);
 
213
                        if (filename == NULL)
 
214
                        {
 
215
                                perror("cddaslave");
 
216
                                wmcdda_close();
 
217
                                wmaudio_close();
 
218
                                exit(1);
 
219
                        }
 
220
 
 
221
                        read(0, filename, namelen);
 
222
                        filename[namelen] = '\0';
 
223
                        output = fopen(filename, "w");
 
224
                        if (output == NULL)
 
225
                                perror(filename);
 
226
                        else
 
227
                        {
 
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);
 
235
 
 
236
                                fwrite(&hdr, sizeof(hdr), 1, output);
 
237
                                fwrite("Recorded from CD by WorkMan", 28, 1,
 
238
                                        output);
 
239
                        }
 
240
 
 
241
                        free(filename);
 
242
                }
 
243
 
 
244
                blk->status = WMCDDA_ACK;
 
245
                send_status(blk);
 
246
        }
 
247
}
 
248
 
 
249
/*
 
250
 * Transform some CDDA data.
 
251
 */
 
252
long
 
253
wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
 
254
{
 
255
        long            i;
 
256
        long            *buf32 = (long *)rawbuf;
 
257
        register short  *buf16 = (short *)rawbuf;
 
258
        register int    aval;
 
259
 
 
260
        /*
 
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.
 
268
         *
 
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.
 
272
         *
 
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.
 
276
         *
 
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.
 
282
         */
 
283
        if (loudness)
 
284
        {
 
285
                /* We aren't really going backwards, but i > 0 is fast */
 
286
                for (i = buflen / 2; i > 0; i--)
 
287
                {
 
288
                        /*
 
289
                         * Adjust this sample to the current level.
 
290
                         */
 
291
                        aval = (*buf16 = (((long)*buf16) * volume) >> 15);
 
292
                        buf16++;
 
293
 
 
294
                        /*
 
295
                         * Don't adjust the decaying average for each sample;
 
296
                         * that just spends CPU time for very little benefit.
 
297
                         */
 
298
                        if (i & 127)
 
299
                                continue;
 
300
 
 
301
                        /*
 
302
                         * We want to use absolute values to compute the
 
303
                         * decaying average; otherwise it'd sit around 0.
 
304
                         */
 
305
                        if (aval < 0)
 
306
                                aval = -aval;
 
307
 
 
308
                        /*
 
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.
 
312
                         */
 
313
                        if (aval & ~8191)
 
314
                                aval <<= 3;
 
315
 
 
316
                        /*
 
317
                         * Adjust the decaying average.
 
318
                         */
 
319
                        level = ((level << 11) - level + aval) >> 11;
 
320
 
 
321
                        /*
 
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.
 
325
                         */
 
326
                        if (! (level & ~511))
 
327
                                level = 512;
 
328
 
 
329
                        /*
 
330
                         * And adjust the volume setting using the inverse
 
331
                         * of the decaying average.
 
332
                         */
 
333
                        volume = (2500 << 15) / level;
 
334
                }
 
335
        }
 
336
 
 
337
        if (speed == 128)
 
338
                return (buflen);
 
339
 
 
340
        /*
 
341
         * Half-speed play.  Stretch things out.
 
342
         */
 
343
        if (speed == 0)
 
344
        {
 
345
                buflen /= 2;    /* Since we're moving 32 bits at a time */
 
346
 
 
347
                for (i = buflen - 1; i > 0; i--)
 
348
                {
 
349
                        buf32[i] = buf32[i / 2];
 
350
                }
 
351
 
 
352
                buflen *= 4;    /* 2 for doubling the buffer, 2 from above */
 
353
        }
 
354
 
 
355
        /*
 
356
         * Slow play; can't optimize it as well as half-speed.
 
357
         */
 
358
        if (speed && speed < 128)
 
359
        {
 
360
                int     multiplier = ((speed + 128) * 128) / 256;
 
361
                int     newlen;
 
362
                int     tally = 0, pos;
 
363
 
 
364
                buflen /= 4;    /* Get the number of 32-bit values */
 
365
 
 
366
                /*
 
367
                 * Buffer length doubles when speed is 0, stays the same
 
368
                 * when speed is 128.
 
369
                 */
 
370
                newlen = (buflen * 128) / multiplier;
 
371
 
 
372
                pos = buflen - 1;
 
373
                for (i = newlen - 1; i > 0; i--)
 
374
                {
 
375
                        buf32[i] = buf32[pos];
 
376
                        tally += multiplier;
 
377
                        if (tally & 128)
 
378
                        {
 
379
                                pos--;
 
380
                                tally ^= 128;
 
381
                        }
 
382
                }
 
383
 
 
384
                buflen = newlen * 4;
 
385
        }
 
386
 
 
387
        return (buflen);
 
388
}
 
389
 
 
390
 
 
391
main(argc, argv)
 
392
        char    **argv;
 
393
{
 
394
        int                     cd_fd = 3;
 
395
        fd_set                  readfd, dummyfd;
 
396
        struct timeval          timeout;
 
397
        char                    *cddabuf;
 
398
        long                    cddabuflen;
 
399
        struct cdda_block       blockinfo;
 
400
        long                    result;
 
401
        int                     nfds;
 
402
        char                    *devname;
 
403
 
 
404
        /*
 
405
         * Device name should be the command-line argument.
 
406
         */
 
407
        if (argc < 2)
 
408
                devname = "";
 
409
        else
 
410
                devname = argv[1];
 
411
 
 
412
        /*
 
413
         * If we're running setuid root, bump up our priority then lose
 
414
         * superuser access.
 
415
         */
 
416
        nice(-14);
 
417
        setuid(getuid());
 
418
 
 
419
        FD_ZERO(&dummyfd);
 
420
        FD_ZERO(&readfd);
 
421
 
 
422
        timerclear(&timeout);
 
423
 
 
424
        cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
 
425
        if (cd_fd < 0)
 
426
                exit(1);
 
427
        wmaudio_init();
 
428
 
 
429
        blockinfo.status = WMCDDA_ACK;
 
430
        send_status(&blockinfo);
 
431
        blockinfo.status = WMCDDA_STOPPED;
 
432
 
 
433
fprintf(stderr,"cddaslave: done init.");
 
434
        /*
 
435
         * Accept commands as they come in, and play some sound if we're
 
436
         * supposed to be doing that.
 
437
         */
 
438
        while (1)
 
439
        {
 
440
                FD_SET(0, &readfd);
 
441
 
 
442
                /*
 
443
                 * If we're playing, we don't want select to block.  Otherwise,
 
444
                 * wait a little while for the next command.
 
445
                 */
 
446
                if (playing)
 
447
                        timeout.tv_usec = 0;
 
448
                else
 
449
                        timeout.tv_usec = 500000;
 
450
 
 
451
                nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
 
452
 
 
453
                if (nfds < 0)   /* Broken pipe; our GUI exited. */
 
454
                {
 
455
                        wmcdda_close(cd_fd);
 
456
                        wmaudio_close();
 
457
       fprintf(stderr,"cddaslave: Borken pipe; GUI must have exited.");
 
458
                        exit(0);
 
459
                }
 
460
 
 
461
                if (FD_ISSET(0, &readfd))
 
462
                {
 
463
                        command(cd_fd, &blockinfo);
 
464
                        /*
 
465
                         * Process all commands in rapid succession, rather
 
466
                         * than possibly waiting for a CDDA read.
 
467
                         */
 
468
                        continue;
 
469
                }
 
470
                
 
471
                if (playing)
 
472
                {
 
473
                        result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
 
474
                                &blockinfo);
 
475
                        if (result <= 0)
 
476
                        {
 
477
                                /* Let the output queue drain. */
 
478
                                if (blockinfo.status == WMCDDA_DONE)
 
479
                                {
 
480
                                        wmaudio_mark_last();
 
481
                                        if (wmaudio_send_status())
 
482
                                        {
 
483
                                                /* queue drained, stop polling*/
 
484
                                                playing = 0;
 
485
                                        }
 
486
                                }
 
487
                                else
 
488
                                {
 
489
                                        playing = 0;
 
490
                                        send_status(&blockinfo);
 
491
                                }
 
492
                        }
 
493
                        else
 
494
                        {
 
495
                                result = wmcdda_normalize(cddabuf, result,
 
496
                                                        &blockinfo);
 
497
                                result = wmcdda_transform(cddabuf, result,
 
498
                                                        &blockinfo);
 
499
                                if (output)
 
500
                                        fwrite(cddabuf, result, 1, output);
 
501
                                result = wmaudio_convert(cddabuf, result,
 
502
                                                        &blockinfo);
 
503
                                if (wmaudio_play(cddabuf, result, &blockinfo))
 
504
                                {
 
505
                                        playing = 0;
 
506
                                        wmaudio_stop();
 
507
                                        send_status(&blockinfo);
 
508
                                }
 
509
                        }
 
510
                }
 
511
                else
 
512
                        send_status(&blockinfo);
 
513
        }
 
514
}
 
515
 
 
516
#else /* BUILD_CDDA } { */
 
517
 
 
518
#include <stdio.h>
 
519
int main()
 
520
{
 
521
   printf("cddaslave: will work only on Solaris 2.4 or newer.");
 
522
   exit(0);
 
523
}
 
524
 
 
525
#endif /* } */