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

« back to all changes in this revision

Viewing changes to kscd/libwm/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
 * $id$
 
3
 *
 
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 
 
9
 *
 
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.
 
14
 *
 
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.
 
19
 *
 
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
 
23
 *
 
24
 ******************************************************************
 
25
 *
 
26
 * Digital audio manipulator for WorkMan.
 
27
 *
 
28
 * The CDDA architecture looks like this:
 
29
 *
 
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)
 
38
 *
 
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.
 
42
 *
 
43
 */
 
44
 
 
45
#include "include/wm_config.h"
 
46
 
 
47
#ifdef BUILD_CDDA
 
48
 
 
49
static char cddaslave_id[] = "$Id: cddaslave.c,v 1.2 2001/06/05 05:37:25 dfoerste Exp $";
 
50
 
 
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"
 
56
 
 
57
 
 
58
int     playing = 0;            /* Should the CD be playing now? */
 
59
 
 
60
/*
 
61
 * Loudness setting, plus the floating volume multiplier and decaying-average
 
62
 * volume level.
 
63
 */
 
64
int             loudness = 0;
 
65
unsigned int    volume = 32768;
 
66
unsigned int    level;
 
67
 
 
68
/*
 
69
 * Playback speed (0 = slow)
 
70
 */
 
71
int             speed = 128;
 
72
 
 
73
/*
 
74
 * This is non-null if we're saving audio to a file.
 
75
 */
 
76
FILE            *output = NULL;
 
77
 
 
78
/*
 
79
 * Audio file header format.
 
80
 */
 
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;
 
89
};
 
90
 
 
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
 
99
 
 
100
void *malloc();
 
101
long cdda_transform();
 
102
 
 
103
/*
 
104
 * Send status information upstream.
 
105
 */
 
106
void
 
107
send_status(blk)
 
108
        struct cdda_block       *blk;
 
109
{
 
110
        write(1, blk, sizeof(*blk));
 
111
}
 
112
 
 
113
/*
 
114
 * Accept a command from our master.
 
115
 *
 
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).
 
133
 */
 
134
int
 
135
command(cd_fd, blk)
 
136
        int                     cd_fd;
 
137
        struct cdda_block       *blk;
 
138
{
 
139
        unsigned char           inbuf[10];
 
140
        char                    *filename;
 
141
        int                     namelen;
 
142
        struct auheader         hdr;
 
143
 
 
144
        if (read(0, inbuf, 1) <= 0)     /* Parent died. */
 
145
        {
 
146
                wmcdda_close();
 
147
                wmaudio_close();
 
148
                exit(0);
 
149
        }
 
150
 
 
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;
 
163
 
 
164
                wmaudio_stop();
 
165
 
 
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]);
 
169
 
 
170
                wmaudio_ready();
 
171
 
 
172
                level = 2500;
 
173
                volume = 1 << 15;
 
174
 
 
175
                blk->status = WMCDDA_ACK;
 
176
                send_status(blk);
 
177
                break;
 
178
 
 
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;
 
187
 
 
188
        case 'B':
 
189
                read(0, inbuf, 1);
 
190
                wmaudio_balance(inbuf[0]);
 
191
                blk->status = WMCDDA_ACK;
 
192
                send_status(blk);
 
193
                break;
 
194
 
 
195
        case 'V':
 
196
                read(0, inbuf, 1);
 
197
                wmaudio_volume(inbuf[0]);
 
198
                blk->status = WMCDDA_ACK;
 
199
                send_status(blk);
 
200
                break;
 
201
 
 
202
        case 'G':
 
203
                blk->status = WMCDDA_ACK;
 
204
                send_status(blk);
 
205
 
 
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;
 
213
 
 
214
        case 'Q':
 
215
                blk->status = WMCDDA_ACK;
 
216
                send_status(blk);
 
217
                wmcdda_close();
 
218
                wmaudio_close();
 
219
                exit(0);
 
220
 
 
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;
 
228
 
 
229
        case 'd':
 
230
                read(0, inbuf, 1);
 
231
                wmcdda_direction(inbuf[0]);
 
232
                blk->status = WMCDDA_ACK;
 
233
                send_status(blk);
 
234
                break;
 
235
 
 
236
        case 'L':
 
237
                read(0, inbuf, 1);
 
238
                loudness = inbuf[0];
 
239
                blk->status = WMCDDA_ACK;
 
240
                send_status(blk);
 
241
                break;
 
242
 
 
243
        case 'F':
 
244
                read(0, &namelen, sizeof(namelen));
 
245
                if (output != NULL)
 
246
                {
 
247
                        fclose(output);
 
248
                        output = NULL;
 
249
                }
 
250
                if (namelen)
 
251
                {
 
252
                        filename = malloc(namelen + 1);
 
253
                        if (filename == NULL)
 
254
                        {
 
255
                                perror("cddaslave");
 
256
                                wmcdda_close();
 
257
                                wmaudio_close();
 
258
                                exit(1);
 
259
                        }
 
260
 
 
261
                        read(0, filename, namelen);
 
262
                        filename[namelen] = '\0';
 
263
                        output = fopen(filename, "w");
 
264
                        if (output == NULL)
 
265
                                perror(filename);
 
266
                        else
 
267
                        {
 
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);
 
275
 
 
276
                                fwrite(&hdr, sizeof(hdr), 1, output);
 
277
                                fwrite("Recorded from CD by WorkMan", 28, 1,
 
278
                                        output);
 
279
                        }
 
280
 
 
281
                        free(filename);
 
282
                }
 
283
 
 
284
                blk->status = WMCDDA_ACK;
 
285
                send_status(blk);
 
286
        }
 
287
        return(cd_fd);
 
288
}
 
289
 
 
290
/*
 
291
 * Transform some CDDA data.
 
292
 */
 
293
long
 
294
wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
 
295
{
 
296
        long            i;
 
297
        long            *buf32 = (long *)rawbuf;
 
298
        register short  *buf16 = (short *)rawbuf;
 
299
        register int    aval;
 
300
 
 
301
        /*
 
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.
 
309
         *
 
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.
 
313
         *
 
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.
 
317
         *
 
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.
 
323
         */
 
324
        if (loudness)
 
325
        {
 
326
                /* We aren't really going backwards, but i > 0 is fast */
 
327
                for (i = buflen / 2; i > 0; i--)
 
328
                {
 
329
                        /*
 
330
                         * Adjust this sample to the current level.
 
331
                         */
 
332
                        aval = (*buf16 = (((long)*buf16) * volume) >> 15);
 
333
                        buf16++;
 
334
 
 
335
                        /*
 
336
                         * Don't adjust the decaying average for each sample;
 
337
                         * that just spends CPU time for very little benefit.
 
338
                         */
 
339
                        if (i & 127)
 
340
                                continue;
 
341
 
 
342
                        /*
 
343
                         * We want to use absolute values to compute the
 
344
                         * decaying average; otherwise it'd sit around 0.
 
345
                         */
 
346
                        if (aval < 0)
 
347
                                aval = -aval;
 
348
 
 
349
                        /*
 
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.
 
353
                         */
 
354
                        if (aval & ~8191)
 
355
                                aval <<= 3;
 
356
 
 
357
                        /*
 
358
                         * Adjust the decaying average.
 
359
                         */
 
360
                        level = ((level << 11) - level + aval) >> 11;
 
361
 
 
362
                        /*
 
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.
 
366
                         */
 
367
                        if (! (level & ~511))
 
368
                                level = 512;
 
369
 
 
370
                        /*
 
371
                         * And adjust the volume setting using the inverse
 
372
                         * of the decaying average.
 
373
                         */
 
374
                        volume = (2500 << 15) / level;
 
375
                }
 
376
        }
 
377
 
 
378
        if (speed == 128)
 
379
                return (buflen);
 
380
 
 
381
        /*
 
382
         * Half-speed play.  Stretch things out.
 
383
         */
 
384
        if (speed == 0)
 
385
        {
 
386
                buflen /= 2;    /* Since we're moving 32 bits at a time */
 
387
 
 
388
                for (i = buflen - 1; i > 0; i--)
 
389
                {
 
390
                        buf32[i] = buf32[i / 2];
 
391
                }
 
392
 
 
393
                buflen *= 4;    /* 2 for doubling the buffer, 2 from above */
 
394
        }
 
395
 
 
396
        /*
 
397
         * Slow play; can't optimize it as well as half-speed.
 
398
         */
 
399
        if (speed && speed < 128)
 
400
        {
 
401
                int     multiplier = ((speed + 128) * 128) / 256;
 
402
                int     newlen;
 
403
                int     tally = 0, pos;
 
404
 
 
405
                buflen /= 4;    /* Get the number of 32-bit values */
 
406
 
 
407
                /*
 
408
                 * Buffer length doubles when speed is 0, stays the same
 
409
                 * when speed is 128.
 
410
                 */
 
411
                newlen = (buflen * 128) / multiplier;
 
412
 
 
413
                pos = buflen - 1;
 
414
                for (i = newlen - 1; i > 0; i--)
 
415
                {
 
416
                        buf32[i] = buf32[pos];
 
417
                        tally += multiplier;
 
418
                        if (tally & 128)
 
419
                        {
 
420
                                pos--;
 
421
                                tally ^= 128;
 
422
                        }
 
423
                }
 
424
 
 
425
                buflen = newlen * 4;
 
426
        }
 
427
 
 
428
        return (buflen);
 
429
}
 
430
 
 
431
 
 
432
main(argc, argv)
 
433
        char    **argv;
 
434
{
 
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;
 
444
 
 
445
        /*
 
446
         * Device name should be the command-line argument.
 
447
         */
 
448
        if (argc < 2)
 
449
                devname = "";
 
450
        else
 
451
                devname = argv[1];
 
452
 
 
453
        /*
 
454
         * If we're running setuid root, bump up our priority then lose
 
455
         * superuser access.
 
456
         */
 
457
        nice(-14);
 
458
        setuid(getuid());
 
459
 
 
460
        FD_ZERO(&dummyfd);
 
461
        FD_ZERO(&readfd);
 
462
 
 
463
        timerclear(&timeout);
 
464
 
 
465
        cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
 
466
        if (cd_fd < 0)
 
467
                exit(1);
 
468
        wmaudio_init();
 
469
 
 
470
        blockinfo.status = WMCDDA_ACK;
 
471
        send_status(&blockinfo);
 
472
        blockinfo.status = WMCDDA_STOPPED;
 
473
 
 
474
        /*
 
475
         * Accept commands as they come in, and play some sound if we're
 
476
         * supposed to be doing that.
 
477
         */
 
478
        while (1)
 
479
        {
 
480
                FD_SET(0, &readfd);
 
481
 
 
482
                /*
 
483
                 * If we're playing, we don't want select to block.  Otherwise,
 
484
                 * wait a little while for the next command.
 
485
                 */
 
486
                if (playing)
 
487
                        timeout.tv_usec = 0;
 
488
                else
 
489
                        timeout.tv_usec = 500000;
 
490
 
 
491
                nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
 
492
 
 
493
                if (nfds < 0)   /* Broken pipe; our GUI exited. */
 
494
                {
 
495
                        wmcdda_close(cd_fd);
 
496
                        wmaudio_close();
 
497
                        exit(0);
 
498
                }
 
499
 
 
500
                if (FD_ISSET(0, &readfd))
 
501
                {
 
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);
 
506
                        /*
 
507
                         * Process all commands in rapid succession, rather
 
508
                         * than possibly waiting for a CDDA read.
 
509
                         */
 
510
                        continue;
 
511
                }
 
512
                
 
513
                if (playing)
 
514
                {
 
515
                        result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
 
516
                                &blockinfo);
 
517
                        if (result <= 0)
 
518
                        {
 
519
                                /* Let the output queue drain. */
 
520
                                if (blockinfo.status == WMCDDA_DONE)
 
521
                                {
 
522
                                        wmaudio_mark_last();
 
523
                                        if (wmaudio_send_status())
 
524
                                        {
 
525
                                                /* queue drained, stop polling*/
 
526
                                                playing = 0;
 
527
                                        }
 
528
                                }
 
529
                                else
 
530
                                {
 
531
                                        playing = 0;
 
532
                                        send_status(&blockinfo);
 
533
                                }
 
534
                        }
 
535
                        else
 
536
                        {
 
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))
 
546
                                {
 
547
                                        playing = 0;
 
548
                                        wmaudio_stop();
 
549
                                        send_status(&blockinfo);
 
550
                                }
 
551
                        }
 
552
                }
 
553
                else
 
554
                        send_status(&blockinfo);
 
555
        }
 
556
}
 
557
 
 
558
#else /* BUILD_CDDA */
 
559
 
 
560
main()
 
561
{
 
562
        exit(0);
 
563
}
 
564
 
 
565
#endif /* BUILD_CDDA */