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

« back to all changes in this revision

Viewing changes to kscd/libwm/plat_linux_audio.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: plat_linux_audio.c,v 1.4 2000/08/30 04:40:36 dfoerste Exp $
 
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
 * Linux digital audio functions.
 
26
 */
 
27
 
 
28
#include "include/wm_config.h"
 
29
 
 
30
static char plat_linux_audio_id[] = "$Id: plat_linux_audio.c,v 1.4 2000/08/30 04:40:36 dfoerste Exp $";
 
31
 
 
32
#if defined(__linux__) && defined(BUILD_CDDA) /* { */
 
33
 
 
34
#include "include/wm_cdda.h"
 
35
 
 
36
/* types.h included by wm_cdda.h */
 
37
 
 
38
#include <stdio.h>
 
39
#include <malloc.h>
 
40
#include <sys/ioctl.h>
 
41
#include <sys/audioio.h>
 
42
#include <sys/stropts.h>
 
43
#include <sys/time.h>
 
44
#include <errno.h>
 
45
#include <fcntl.h>
 
46
#include <signal.h>
 
47
 
 
48
#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
 
49
 
 
50
/*
 
51
 * Since there's a lag time between writing audio to the audio device and
 
52
 * hearing it, we need to make sure the status indicators correlate to what's
 
53
 * playing out the speaker.  Luckily, Solaris gives us some audio
 
54
 * synchronization facilities that make this pretty easy.
 
55
 *
 
56
 * We maintain a circular queue of status information.  When we write some
 
57
 * sound to the audio device, we put its status info into the queue.  We write
 
58
 * a marker into the audio stream; when the audio device driver encounters the
 
59
 * marker, it increments a field in a status structure.  When we see that
 
60
 * field go up, we grab the next status structure from the queue and send it
 
61
 * to the parent process.
 
62
 *
 
63
 * The minimum size of the queue depends on the latency of the audio stream.
 
64
 */
 
65
#define QSIZE 500
 
66
 
 
67
struct cdda_block       queue[QSIZE];
 
68
int                     qtail;
 
69
int                     qstart;
 
70
 
 
71
/*
 
72
 * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
 
73
 * to be playing; this is used to keep track.
 
74
 */
 
75
extern int playing;
 
76
 
 
77
static int      aufd, aucfd;
 
78
static int      raw_audio = 1;  /* Can /dev/audio take 44.1KHz stereo? */
 
79
 
 
80
/* 
 
81
 * For fast linear-to-ulaw mapping, we use a lookup table that's generated
 
82
 * at startup.
 
83
 */
 
84
unsigned char *ulawmap, linear_to_ulaw();
 
85
 
 
86
char *getenv();
 
87
 
 
88
/*
 
89
 * Dummy signal handler so writes to /dev/audio will interrupt.
 
90
 */
 
91
static void
 
92
dummy( void )
 
93
{
 
94
        signal(SIGALRM, dummy);
 
95
}
 
96
 
 
97
/*
 
98
 * Initialize the audio device.
 
99
 */
 
100
void
 
101
wmaudio_init( void )
 
102
{
 
103
        audio_info_t            info;
 
104
        char                    *audiodev, *acdev;
 
105
        int                     linval;
 
106
 
 
107
        audiodev = getenv("AUDIODEV");
 
108
        if (audiodev == NULL)
 
109
                audiodev = "/dev/audio";
 
110
 
 
111
        acdev = malloc(strlen(audiodev) + 4);
 
112
        if (acdev == NULL)
 
113
        {
 
114
                perror("Can't allocate audio control filename");
 
115
                exit(1);
 
116
        }
 
117
        strcpy(acdev, audiodev);
 
118
        strcat(acdev, "ctl");
 
119
 
 
120
        aucfd = open(acdev, O_WRONLY, 0);
 
121
        if (aucfd < 0)
 
122
        {
 
123
                perror(acdev);
 
124
                exit(1);
 
125
        }
 
126
        free(acdev);
 
127
 
 
128
        aufd = open(audiodev, O_WRONLY, 0);
 
129
        if (aufd < 0)
 
130
        {
 
131
                perror(audiodev);
 
132
                exit(1);
 
133
        }
 
134
 
 
135
        signal(SIGALRM, dummy);
 
136
 
 
137
        /*
 
138
         * Try to set the device to CD-style audio; we can process it
 
139
         * with the least CPU overhead.
 
140
         */
 
141
        AUDIO_INITINFO(&info);
 
142
        info.play.sample_rate = 44100;
 
143
        info.play.channels = 2;
 
144
        info.play.precision = 16;
 
145
        info.play.encoding = AUDIO_ENCODING_LINEAR;
 
146
        info.play.pause = 0;
 
147
        info.record.pause = 0;
 
148
        info.monitor_gain = 0;
 
149
 
 
150
        if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
 
151
                if (errno == EINVAL)
 
152
                {
 
153
                        /*
 
154
                         * Oh well, so much for that idea.
 
155
                         */
 
156
                        AUDIO_INITINFO(&info);
 
157
                        info.play.sample_rate = 8000;
 
158
                        info.play.channels = 1;
 
159
                        info.play.precision = 8;
 
160
                        info.play.encoding = AUDIO_ENCODING_ULAW;
 
161
                        info.play.pause = 0;
 
162
                        info.record.pause = 0;
 
163
                        info.monitor_gain = 0;
 
164
                        if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
 
165
                        {
 
166
                                perror("Can't set up audio device");
 
167
                                exit(1);
 
168
                        }
 
169
 
 
170
                        /*
 
171
                         * Initialize the linear-to-ulaw mapping table.
 
172
                         */
 
173
                        if (ulawmap == NULL)
 
174
                                ulawmap = malloc(65536);
 
175
                        if (ulawmap == NULL)
 
176
                        {
 
177
                                perror("malloc");
 
178
                                exit(1);
 
179
                        }
 
180
                        for (linval = 0; linval < 65536; linval++)
 
181
                                ulawmap[linval] = linear_to_ulaw(linval-32768);
 
182
                        ulawmap += 32768;
 
183
                        raw_audio = 0;
 
184
                }
 
185
                else
 
186
                {
 
187
                        perror(audiodev);
 
188
                        exit(1);
 
189
                }
 
190
}
 
191
 
 
192
/*
 
193
 * Get ready to play some sound.
 
194
 */
 
195
void
 
196
wmaudio_ready( void )
 
197
{
 
198
        audio_info_t            info;
 
199
 
 
200
        /*
 
201
         * Start at the correct queue position.
 
202
         */
 
203
        if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
 
204
        qtail = info.play.eof % QSIZE;
 
205
        qstart = qtail;
 
206
 
 
207
        queue[qtail].status = WMCDDA_OK;
 
208
}
 
209
 
 
210
/*
 
211
 * Stop the audio immediately.
 
212
 */
 
213
void
 
214
wmaudio_stop( void )
 
215
{
 
216
        if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
 
217
                perror("flush");
 
218
}
 
219
 
 
220
/*
 
221
 * Close the audio device.
 
222
 */
 
223
void
 
224
wmaudio_close( void )
 
225
{
 
226
        wmaudio_stop();
 
227
        close(aufd);
 
228
        close(aucfd);
 
229
}
 
230
 
 
231
/*
 
232
 * Set the volume level.
 
233
 */
 
234
void
 
235
wmaudio_volume(int level)
 
236
{
 
237
        audio_info_t            info;
 
238
 
 
239
        AUDIO_INITINFO(&info);
 
240
        if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
 
241
        info.play.gain = level;
 
242
        if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
 
243
}
 
244
 
 
245
/*
 
246
 * Set the balance level.
 
247
 */
 
248
void
 
249
wmaudio_balance(int level)
 
250
{
 
251
        audio_info_t            info;
 
252
 
 
253
        AUDIO_INITINFO(&info);
 
254
        if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
 
255
        level *= AUDIO_RIGHT_BALANCE;
 
256
        info.play.balance = level / 255;
 
257
        if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
 
258
}
 
259
 
 
260
/*
 
261
 * Mark the most recent audio block on the queue as the last one.
 
262
 */
 
263
void
 
264
wmaudio_mark_last( void )
 
265
{
 
266
        queue[qtail].status = WMCDDA_DONE;
 
267
}
 
268
 
 
269
/*
 
270
 * Figure out the most recent status information and send it upstream.
 
271
 */
 
272
int
 
273
wmaudio_send_status( void )
 
274
{
 
275
        audio_info_t            info;
 
276
        int                     qhead;
 
277
 
 
278
        /*
 
279
         * Now send the most current status information to our parent.
 
280
         */
 
281
        if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
 
282
                perror("AUDIO_GETINFO");
 
283
        qhead = info.play.eof % QSIZE;
 
284
 
 
285
        if (qhead != qstart && playing)
 
286
        {
 
287
                int     balance;
 
288
 
 
289
                if (queue[qhead].status != WMCDDA_DONE)
 
290
                        queue[qhead].status = WMCDDA_PLAYED;
 
291
                queue[qhead].volume = info.play.gain;
 
292
                queue[qhead].balance = (info.play.balance * 255) /
 
293
                                        AUDIO_RIGHT_BALANCE;
 
294
 
 
295
                send_status(queue + qhead);
 
296
                qstart = -1;
 
297
        }
 
298
 
 
299
        return (queue[qhead].status == WMCDDA_DONE);
 
300
}
 
301
 
 
302
/*
 
303
 * Play some audio and pass a status message upstream, if applicable.
 
304
 * Returns 0 on success.
 
305
 */
 
306
int
 
307
wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
 
308
{
 
309
        int                     i;
 
310
        short                   *buf16;
 
311
        int                     alarmcount = 0;
 
312
        struct itimerval        it;
 
313
 
 
314
        alarm(1);
 
315
 
 
316
        while (write(aufd, rawbuf, buflen) <= 0)
 
317
                if (errno == EINTR)
 
318
                {
 
319
                        if (! raw_audio && alarmcount++ < 5)
 
320
                        {
 
321
                                /*
 
322
                                 * 8KHz /dev/audio blocks for several seconds
 
323
                                 * waiting for its queue to drop below a low
 
324
                                 * water mark.
 
325
                                 */
 
326
                                wmaudio_send_status();
 
327
                                timerclear(&it.it_interval);
 
328
                                timerclear(&it.it_value);
 
329
                                it.it_value.tv_usec = 500000;
 
330
                                setitimer(ITIMER_REAL, &it, NULL);
 
331
                                continue;
 
332
                        }
 
333
 
 
334
/*                      close(aufd);
 
335
                        close(aucfd);
 
336
                        wmaudio_init();
 
337
*/ wmaudio_stop( void );
 
338
                        alarm(2);
 
339
                        continue;
 
340
                }
 
341
                else
 
342
                {
 
343
                        blk->status = WMCDDA_ERROR;
 
344
                        return (-1);
 
345
                }
 
346
        alarm(0);
 
347
 
 
348
        /*
 
349
         * Mark this spot in the audio stream.
 
350
         *
 
351
         * Marks don't always succeed (if the audio buffer is empty
 
352
         * this call will block forever) so do it asynchronously.
 
353
         */
 
354
        fcntl(aufd, F_SETFL, O_NONBLOCK);
 
355
        if (write(aufd, rawbuf, 0) < 0)
 
356
        {
 
357
                if (errno != EAGAIN)
 
358
                        perror("audio mark");
 
359
        }
 
360
        else
 
361
                qtail = (qtail + 1) % QSIZE;
 
362
 
 
363
        fcntl(aufd, F_SETFL, 0);
 
364
 
 
365
        queue[qtail] = *blk;
 
366
 
 
367
        if (wmaudio_send_status() < 0)
 
368
                return (-1);
 
369
        else
 
370
                return (0);
 
371
}
 
372
 
 
373
/*
 
374
 * Get the current audio state.
 
375
 */
 
376
void
 
377
wmaudio_state(struct cdda_block *blk)
 
378
{
 
379
        audio_info_t            info;
 
380
        int                     balance;
 
381
 
 
382
        if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
 
383
                perror("AUDIO_GETINFO");
 
384
        blk->volume = info.play.gain;
 
385
        blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
 
386
}
 
387
 
 
388
/*
 
389
** This routine converts from linear to ulaw.
 
390
**
 
391
** Craig Reese: IDA/Supercomputing Research Center
 
392
** Joe Campbell: Department of Defense
 
393
** 29 September 1989
 
394
**
 
395
** References:
 
396
** 1) CCITT Recommendation G.711  (very difficult to follow)
 
397
** 2) "A New Digital Technique for Implementation of Any
 
398
**     Continuous PCM Companding Law," Villeret, Michel,
 
399
**     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
 
400
**     1973, pg. 11.12-11.17
 
401
** 3) MIL-STD-188-113,"Interoperability and Performance Standards
 
402
**     for Analog-to_Digital Conversion Techniques,"
 
403
**     17 February 1987
 
404
**
 
405
** Input: Signed 16 bit linear sample
 
406
** Output: 8 bit ulaw sample
 
407
*/
 
408
#define ZEROTRAP    /* turn on the trap as per the MIL-STD */
 
409
#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
 
410
#define CLIP 32635
 
411
 
 
412
unsigned char
 
413
linear_to_ulaw( sample )
 
414
int sample;
 
415
{
 
416
        static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
 
417
                                   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
 
418
                                   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
 
419
                                   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
 
420
                                   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 
421
                                   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 
422
                                   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 
423
                                   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 
424
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
425
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
426
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
427
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
428
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
429
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
430
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 
431
                                   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
 
432
        int sign, exponent, mantissa;
 
433
        unsigned char ulawbyte;
 
434
 
 
435
        /* Get the sample into sign-magnitude. */
 
436
        sign = (sample >> 8) & 0x80;            /* set aside the sign */
 
437
        if ( sign != 0 ) sample = -sample;              /* get magnitude */
 
438
        if ( sample > CLIP ) sample = CLIP;             /* clip the magnitude */
 
439
 
 
440
        /* Convert from 16 bit linear to ulaw. */
 
441
        sample = sample + BIAS;
 
442
        exponent = exp_lut[( sample >> 7 ) & 0xFF];
 
443
        mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
 
444
        ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
 
445
#ifdef ZEROTRAP
 
446
        if ( ulawbyte == 0 ) ulawbyte = 0x02;   /* optional CCITT trap */
 
447
#endif
 
448
 
 
449
        return ulawbyte;
 
450
}
 
451
 
 
452
/*
 
453
 * Downsample a block of CDDA data, if necessary, for playing out an old-style
 
454
 * audio device.
 
455
 */
 
456
long
 
457
wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
 
458
{
 
459
        short           *buf16 = (short *)rawbuf;
 
460
        int             i, j, samples;
 
461
        int             mono_value;
 
462
        unsigned char   *rbend = rawbuf + buflen;
 
463
 
 
464
        /* Don't do anything if the audio device can take the raw values. */
 
465
        if (raw_audio)
 
466
                return (buflen);
 
467
 
 
468
        for (i = 0; buf16 < (short *)(rbend); i++)
 
469
        {
 
470
                /* Downsampling to 8KHz is a little irregular. */
 
471
                samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
 
472
 
 
473
                /* And unfortunately, we don't always end on a nice boundary. */
 
474
                if (buf16 + samples > (short *)(rbend))
 
475
                        samples = ((short *)rbend) - buf16;
 
476
 
 
477
                /*
 
478
                 * No need to average all the values; taking the first one
 
479
                 * is sufficient and less CPU-intensive.  But we do need to
 
480
                 * do both channels.
 
481
                 */
 
482
                mono_value = (buf16[0] + buf16[1]) / 2;
 
483
                buf16 += samples;
 
484
                rawbuf[i] = ulawmap[mono_value];
 
485
        }
 
486
 
 
487
        return (i);
 
488
}
 
489
 
 
490
#endif /* ... && BUILD_CDDA */