File indexing completed on 2024-04-14 04:49:16

0001 /*  This file is part of the KDE project
0002 
0003     Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
0004     Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
0005     Copyright (C) 2006 Alexander Kern <alex.kern@gmx.de>
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License version 2 as published by the Free Software Foundation.
0010 
0011     This library is distributed in the hope that it will be useful,
0012     but WITHOUT ANY WARRANTY; without even the implied warranty of
0013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014     Library General Public License for more details.
0015 
0016     You should have received a copy of the GNU Library General Public License
0017     along with this library; see the file COPYING.LIB.  If not, write to
0018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019     Boston, MA 02110-1301, USA.
0020 
0021     Sun (really Solaris) digital audio functions.
0022 */
0023 
0024 #if defined(sun) || defined(__sun__)
0025 
0026 #include "audio.h"
0027 
0028 #include <stdio.h>
0029 #include <malloc.h>
0030 #include <sys/ioctl.h>
0031 #include <sys/audioio.h>
0032 #include <sys/stropts.h>
0033 #include <sys/time.h>
0034 #include <errno.h>
0035 #include <fcntl.h>
0036 #include <signal.h>
0037 
0038 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0039 
0040 /*
0041  * Since there's a lag time between writing audio to the audio device and
0042  * hearing it, we need to make sure the status indicators correlate to what's
0043  * playing out the speaker.  Luckily, Solaris gives us some audio
0044  * synchronization facilities that make this pretty easy.
0045  *
0046  * We maintain a circular queue of status information.  When we write some
0047  * sound to the audio device, we put its status info into the queue.  We write
0048  * a marker into the audio stream; when the audio device driver encounters the
0049  * marker, it increments a field in a status structure.  When we see that
0050  * field go up, we grab the next status structure from the queue and send it
0051  * to the parent process.
0052  *
0053  * The minimum size of the queue depends on the latency of the audio stream.
0054  */
0055 #define QSIZE 500
0056 
0057 struct cdda_block   queue[QSIZE];
0058 int         qtail;
0059 int         qstart;
0060 
0061 /*
0062  * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed
0063  * to be playing; this is used to keep track.
0064  */
0065 extern int playing;
0066 
0067 static int  aufd, aucfd;
0068 static int  raw_audio = 1;  /* Can /dev/audio take 44.1KHz stereo? */
0069 
0070 /*
0071  * For fast linear-to-ulaw mapping, we use a lookup table that's generated
0072  * at startup.
0073  */
0074 unsigned char *ulawmap, linear_to_ulaw();
0075 
0076 char *getenv();
0077 
0078 /*
0079  * Dummy signal handler so writes to /dev/audio will interrupt.
0080  */
0081 static void
0082 dummy( void )
0083 {
0084     signal(SIGALRM, dummy);
0085 }
0086 
0087 /*
0088  * Initialize the audio device.
0089  */
0090 void
0091 sun_audio_init( void )
0092 {
0093     audio_info_t        info;
0094     char            *audiodev, *acdev;
0095     int         linval;
0096 
0097     audiodev = getenv("AUDIODEV");
0098     if (audiodev == NULL ||
0099         strncmp("/dev/", audiodev, 5) ||
0100         strstr(audiodev, "/../") )
0101         audiodev = "/dev/audio";
0102 
0103     acdev = malloc(strlen(audiodev) + 4);
0104     if (acdev == NULL)
0105     {
0106         perror("Cannot allocate audio control filename");
0107         exit(1);
0108     }
0109     strcpy(acdev, audiodev);
0110     strcat(acdev, "ctl");
0111 
0112     aucfd = open(acdev, O_WRONLY, 0);
0113     if (aucfd < 0)
0114     {
0115         perror(acdev);
0116         exit(1);
0117     }
0118     free(acdev);
0119 
0120     aufd = open(audiodev, O_WRONLY, 0);
0121     if (aufd < 0)
0122     {
0123         perror(audiodev);
0124         exit(1);
0125     }
0126 
0127     signal(SIGALRM, dummy);
0128 
0129     /*
0130      * Try to set the device to CD-style audio; we can process it
0131      * with the least CPU overhead.
0132      */
0133     AUDIO_INITINFO(&info);
0134     info.play.sample_rate = 44100;
0135     info.play.channels = 2;
0136     info.play.precision = 16;
0137     info.play.encoding = AUDIO_ENCODING_LINEAR;
0138     info.play.pause = 0;
0139     info.record.pause = 0;
0140     info.monitor_gain = 0;
0141 
0142     if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
0143         if (errno == EINVAL)
0144         {
0145             /*
0146              * Oh well, so much for that idea.
0147              */
0148             AUDIO_INITINFO(&info);
0149             info.play.sample_rate = 8000;
0150             info.play.channels = 1;
0151             info.play.precision = 8;
0152             info.play.encoding = AUDIO_ENCODING_ULAW;
0153             info.play.pause = 0;
0154             info.record.pause = 0;
0155             info.monitor_gain = 0;
0156             if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
0157             {
0158                 perror("Can't set up audio device");
0159                 exit(1);
0160             }
0161 
0162             /*
0163              * Initialize the linear-to-ulaw mapping table.
0164              */
0165             if (ulawmap == NULL)
0166                 ulawmap = malloc(65536);
0167             if (ulawmap == NULL)
0168             {
0169                 perror("malloc");
0170                 exit(1);
0171             }
0172             for (linval = 0; linval < 65536; linval++)
0173                 ulawmap[linval] = linear_to_ulaw(linval-32768);
0174             ulawmap += 32768;
0175             raw_audio = 0;
0176         }
0177         else
0178         {
0179             perror(audiodev);
0180             exit(1);
0181         }
0182 }
0183 
0184 /*
0185  * Get ready to play some sound.
0186  */
0187 void
0188 sun_audio_ready( void )
0189 {
0190     audio_info_t        info;
0191 
0192     /*
0193      * Start at the correct queue position.
0194      */
0195     if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
0196     qtail = info.play.eof % QSIZE;
0197     qstart = qtail;
0198 
0199     queue[qtail].status = WM_CDM_PLAYING;
0200 }
0201 
0202 /*
0203  * Stop the audio immediately.
0204  */
0205 int
0206 sun_audio_stop( void )
0207 {
0208     if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
0209         perror("flush");
0210   return 0;
0211 }
0212 
0213 /*
0214  * Close the audio device.
0215  */
0216 int
0217 sun_audio_close( void )
0218 {
0219     wmaudio_stop();
0220     close(aufd);
0221     close(aucfd);
0222   return 0;
0223 }
0224 
0225 /*
0226  * Set/get the balance and volume level.
0227  */
0228 int
0229 sun_audio_balvol(int setget, unsigned char *balance, unsigned char *volume)
0230 {
0231     audio_info_t info;
0232     AUDIO_INITINFO(&info);
0233     if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) {
0234         perror("AUDIO_GETINFO");
0235         return -1;
0236     }
0237 
0238     if(setget) {
0239         balance *= AUDIO_RIGHT_BALANCE;
0240         info.play.balance = *balance / 255;
0241         info.play.gain = *volume;
0242         if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
0243             perror("AUDIO_SETINFO");
0244             return -1;
0245         }
0246     } else {
0247         *volume = info.play.gain;
0248         *balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
0249     }
0250     return 0;
0251 }
0252 
0253 /*
0254  * Mark the most recent audio block on the queue as the last one.
0255  */
0256 void
0257 sun_audio_mark_last( void )
0258 {
0259     queue[qtail].status = WM_CDM_TRACK_DONE;
0260 }
0261 
0262 /*
0263  * Figure out the most recent status information and send it upstream.
0264  */
0265 int
0266 sun_audio_send_status( void )
0267 {
0268     audio_info_t        info;
0269     int         qhead;
0270 
0271     /*
0272      * Now send the most current status information to our parent.
0273      */
0274     if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
0275         perror("AUDIO_GETINFO");
0276     qhead = info.play.eof % QSIZE;
0277 
0278     if (qhead != qstart && playing)
0279     {
0280         int balance;
0281 
0282         if (queue[qhead].status != WM_CDM_TRACK_DONE)
0283             queue[qhead].status = WM_CDM_PLAYING;
0284         queue[qhead].volume = info.play.gain;
0285         queue[qhead].balance = (info.play.balance * 255) /
0286                     AUDIO_RIGHT_BALANCE;
0287 
0288         send_status(queue + qhead);
0289         qstart = -1;
0290     }
0291 
0292     return (queue[qhead].status == WM_CDM_TRACK_DONE);
0293 }
0294 
0295 /*
0296  * Play some audio and pass a status message upstream, if applicable.
0297  * Returns 0 on success.
0298  */
0299 int
0300 sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
0301 {
0302     int         i;
0303     short           *buf16;
0304     int         alarmcount = 0;
0305     struct itimerval    it;
0306     long playablelen;
0307 
0308     alarm(1);
0309   playablelen = dev_audio_convert(rawbuf, buflen, blk);
0310     while (write(aufd, rawbuf, playablelen) <= 0)
0311         if (errno == EINTR)
0312         {
0313             if (! raw_audio && alarmcount++ < 5)
0314             {
0315                 /*
0316                  * 8KHz /dev/audio blocks for several seconds
0317                  * waiting for its queue to drop below a low
0318                  * water mark.
0319                  */
0320                 wmaudio_send_status();
0321                 timerclear(&it.it_interval);
0322                 timerclear(&it.it_value);
0323                 it.it_value.tv_usec = 500000;
0324                 setitimer(ITIMER_REAL, &it, NULL);
0325                 continue;
0326             }
0327 
0328 /*          close(aufd);
0329             close(aucfd);
0330             wmaudio_init();
0331 */ sun_audio_stop();
0332             alarm(2);
0333             continue;
0334         }
0335         else
0336         {
0337             blk->status = WM_CDM_CDDAERROR;
0338             return (-1);
0339         }
0340     alarm(0);
0341 
0342     /*
0343      * Mark this spot in the audio stream.
0344      *
0345      * Marks don't always succeed (if the audio buffer is empty
0346      * this call will block forever) so do it asynchronously.
0347      */
0348     fcntl(aufd, F_SETFL, O_NONBLOCK);
0349     if (write(aufd, rawbuf, 0) < 0)
0350     {
0351         if (errno != EAGAIN)
0352             perror("audio mark");
0353     }
0354     else
0355         qtail = (qtail + 1) % QSIZE;
0356 
0357     fcntl(aufd, F_SETFL, 0);
0358 
0359     queue[qtail] = *blk;
0360 
0361     if (wmaudio_send_status() < 0)
0362         return (-1);
0363     else
0364         return (0);
0365 }
0366 
0367 /*
0368 ** This routine converts from linear to ulaw.
0369 **
0370 ** Craig Reese: IDA/Supercomputing Research Center
0371 ** Joe Campbell: Department of Defense
0372 ** 29 September 1989
0373 **
0374 ** References:
0375 ** 1) CCITT Recommendation G.711  (very difficult to follow)
0376 ** 2) "A New Digital Technique for Implementation of Any
0377 **     Continuous PCM Companding Law," Villeret, Michel,
0378 **     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
0379 **     1973, pg. 11.12-11.17
0380 ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
0381 **     for Analog-to_Digital Conversion Techniques,"
0382 **     17 February 1987
0383 **
0384 ** Input: Signed 16 bit linear sample
0385 ** Output: 8 bit ulaw sample
0386 */
0387 #define ZEROTRAP    /* turn on the trap as per the MIL-STD */
0388 #define BIAS 0x84               /* define the add-in bias for 16 bit samples */
0389 #define CLIP 32635
0390 
0391 unsigned char
0392 linear_to_ulaw( sample )
0393 int sample;
0394 {
0395     static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
0396                    4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
0397                    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
0398                    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
0399                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
0400                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
0401                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
0402                    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
0403                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0404                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0405                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0406                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0407                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0408                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0409                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
0410                    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
0411     int sign, exponent, mantissa;
0412     unsigned char ulawbyte;
0413 
0414     /* Get the sample into sign-magnitude. */
0415     sign = (sample >> 8) & 0x80;            /* set aside the sign */
0416     if ( sign != 0 ) sample = -sample;              /* get magnitude */
0417     if ( sample > CLIP ) sample = CLIP;             /* clip the magnitude */
0418 
0419     /* Convert from 16 bit linear to ulaw. */
0420     sample = sample + BIAS;
0421     exponent = exp_lut[( sample >> 7 ) & 0xFF];
0422     mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
0423     ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
0424 #ifdef ZEROTRAP
0425     if ( ulawbyte == 0 ) ulawbyte = 0x02;   /* optional CCITT trap */
0426 #endif
0427 
0428     return ulawbyte;
0429 }
0430 
0431 /*
0432  * Downsample a block of CDDA data, if necessary, for playing out an old-style
0433  * audio device.
0434  */
0435 long
0436 dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
0437 {
0438     short       *buf16 = (short *)rawbuf;
0439     int     i, j, samples;
0440     int     mono_value;
0441     unsigned char   *rbend = rawbuf + buflen;
0442 
0443     /* Don't do anything if the audio device can take the raw values. */
0444     if (raw_audio)
0445         return (buflen);
0446 
0447     for (i = 0; buf16 < (short *)(rbend); i++)
0448     {
0449         /* Downsampling to 8KHz is a little irregular. */
0450         samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
0451 
0452         /* And unfortunately, we don't always end on a nice boundary. */
0453         if (buf16 + samples > (short *)(rbend))
0454             samples = ((short *)rbend) - buf16;
0455 
0456         /*
0457          * No need to average all the values; taking the first one
0458          * is sufficient and less CPU-intensive.  But we do need to
0459          * do both channels.
0460          */
0461         mono_value = (buf16[0] + buf16[1]) / 2;
0462         buf16 += samples;
0463         rawbuf[i] = ulawmap[mono_value];
0464     }
0465 
0466     return (i);
0467 }
0468 
0469 static struct audio_oops sun_audio_oops = {
0470   .wmaudio_open    = sun_audio_open,
0471   .wmaudio_close   = sun_audio_close,
0472   .wmaudio_play    = sun_audio_play,
0473   .wmaudio_stop    = sun_audio_stop,
0474   .wmaudio_state   = NULL,
0475   .wmaudio_balvol = sun_audio_balvol
0476 };
0477 
0478 struct audio_oops*
0479 setup_sun_audio(const char *dev, const char *ctl)
0480 {
0481   int err;
0482 
0483   if((err = sun_audio_init())) {
0484     ERRORLOG("cannot initialize SUN /dev/audio subsystem \n");
0485     return NULL;
0486   }
0487 
0488   sun_audio_open();
0489 
0490   return &sun_audio_oops;
0491 }
0492 
0493 #endif