File indexing completed on 2024-04-28 08:46:56

0001 /*
0002  * This file is part of WorkMan, the civilized CD player library
0003  * Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
0004  * Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
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
0017  * License along with this library; if not, write to the Free
0018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0019  *
0020  *
0021  * Sun (really Solaris) CDDA functions.
0022  */
0023 
0024 #include "include/wm_cdda.h"
0025 
0026 #if defined(sun) || defined(__sun__) && defined(SYSV)
0027 
0028 
0029 #include "include/wm_struct.h"
0030 /* types.h and cdio.h are included by wm_cdda.h */
0031 
0032 #include <stdio.h>
0033 #include <math.h>
0034 #include <sys/ioctl.h>
0035 #include <malloc.h>
0036 #include <errno.h>
0037 
0038 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
0039 
0040 #define CDDABLKSIZE 2368
0041 #define SAMPLES_PER_BLK 588
0042 
0043 /* Address of next block to read. */
0044 int current_position = 0;
0045 
0046 /* Address of first and last blocks to read. */
0047 int starting_position = 0;
0048 int ending_position = 0;
0049 
0050 /* Playback direction. */
0051 int direction = 1;
0052 
0053 /* Number of blocks to read at once; initialize to the maximum. */
0054 /* (was 30. Set to 15 for INTeL. Maybe config option? */
0055 int numblocks = 15;
0056 
0057 /*
0058  * This is the fastest way to convert from BCD to 8-bit.
0059  */
0060 unsigned char unbcd[256] = {
0061   0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,0,0,0,0,0,
0062  10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,0,0,0,0,0,
0063  20, 21, 22, 23, 24, 25, 26, 27, 28, 29,  0,0,0,0,0,0,
0064  30, 31, 32, 33, 34, 35, 36, 37, 38, 39,  0,0,0,0,0,0,
0065  40, 41, 42, 43, 44, 45, 46, 47, 48, 49,  0,0,0,0,0,0,
0066  50, 51, 52, 53, 54, 55, 56, 57, 58, 59,  0,0,0,0,0,0,
0067  60, 61, 62, 63, 64, 65, 66, 67, 68, 69,  0,0,0,0,0,0,
0068  70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  0,0,0,0,0,0,
0069  80, 81, 82, 83, 84, 85, 86, 87, 88, 89,  0,0,0,0,0,0,
0070  90, 91, 92, 93, 94, 95, 96, 97, 98, 99,  0,0,0,0,0,0,
0071  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0072  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0073  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0074  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0075  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0076  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0077 };
0078 
0079 static long wmcdda_normalize(struct cdda_block *block);
0080 
0081 /*
0082  * Initialize the CDDA data buffer and open the appropriate device.
0083  *
0084  * NOTE: We allocate twice as much space as we need to actually read a block;
0085  * this lets us do audio manipulations without bothering to malloc a second
0086  * buffer.
0087  *
0088  * Also, test to see if we can actually *do* CDDA on this drive; if not, we
0089  * need to exit right away so the UI doesn't show the user any CDDA controls.
0090  */
0091 int
0092 wmcdda_init(struct cdda_device* pdev, struct cdda_block *block)
0093 {
0094   struct cdrom_cdda cdda;
0095   int i;
0096 
0097   if (pdev->fd > -1)
0098     return -1;
0099 
0100   for (i = 0; i < pdev->numblocks; i++) {
0101     /* in Linux const */
0102     pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
0103     pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
0104     if (!pdev->blocks[i].buf)
0105       return -ENOMEM;
0106   }
0107 
0108   pdev->fd = open(pdev->devname, 0);
0109   if (pdev->fd == -1)
0110     pdev->fd = open("/dev/rdsk/c0t6d0s2", 0);
0111 
0112     if (pdev->fd > -1)
0113     {
0114         cdda.cdda_addr = 200;
0115         cdda.cdda_length = 1;
0116         cdda.cdda_data = pdev->blocks[0].buf;
0117         cdda.cdda_subcode = CDROM_DA_SUBQ;
0118 
0119         if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
0120         {
0121             block->status = WM_CDM_STOPPED;
0122             return -1;
0123         } else {
0124           block->status = WM_CDM_STOPPED;
0125           return 0;
0126         }
0127     } else {
0128             block->status = WM_CDM_EJECTED;
0129             return -1;
0130     }
0131 }
0132 
0133 /*
0134  * Close the CD-ROM device in preparation for exiting.
0135  */
0136 int
0137 wmcdda_close(struct cdda_device* pdev)
0138 {
0139     int i;
0140 
0141     if(-1 == pdev->fd)
0142         return -1;
0143 
0144     close(pdev->fd);
0145     pdev->fd = -1;
0146 
0147     for (i = 0; i < pdev->numblocks; i++) {
0148         free(pdev->blocks[i].buf);
0149         pdev->blocks[i].buf = 0;
0150         pdev->blocks[i].buflen = 0;
0151     }
0152 
0153     return 0;
0154 }
0155 
0156 /*
0157  * Set up for playing the CD.  Actually this doesn't play a thing, just sets a
0158  * couple variables so we'll know what to do when we're called.
0159  */
0160 int
0161 wmcdda_setup(int start, int end)
0162 {
0163     current_position = start - 150;
0164     ending_position = end - 150;
0165 
0166     /*
0167      * Special case: don't start at the "end" of a track if we're
0168      * playing backwards!
0169      */
0170     if (direction == -1)
0171         current_position = ending_position - numblocks;
0172   return 0;
0173 }
0174 
0175 /*
0176  * Read some blocks from the CD.  Stop if we hit the end of the current region.
0177  *
0178  * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
0179  */
0180 long
0181 wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
0182 {
0183     struct cdrom_cdda   cdda;
0184     int         blk;
0185     unsigned char   *q;
0186     extern int      speed;
0187     unsigned char*      rawbuf = block->buf;
0188 
0189     if(pdev->fd < 0 && (wmcdda_init(pdev, block) < 0)) {
0190         return -1;
0191     }
0192 
0193     /*
0194      * Hit the end of the CD, probably.
0195      */
0196     if ((direction > 0 && current_position >= ending_position) ||
0197         (direction < 0 && current_position < starting_position))
0198     {
0199         block->status = WM_CDM_TRACK_DONE;
0200         return (0);
0201     }
0202 
0203     cdda.cdda_addr = current_position;
0204     if (ending_position && current_position + pdev->frames_at_once > ending_position)
0205         cdda.cdda_length = ending_position - current_position;
0206     else
0207         cdda.cdda_length = pdev->frames_at_once;
0208     cdda.cdda_data = (unsigned char*)block->buf;
0209     cdda.cdda_subcode = CDROM_DA_SUBQ;
0210 
0211     if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
0212     {
0213         if (errno == ENXIO) /* CD ejected! */
0214         {
0215             block->status = WM_CDM_EJECTED;
0216             return (-1);
0217         }
0218 
0219         /* Sometimes it fails once, dunno why */
0220         if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
0221         {
0222             if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
0223             {
0224                 if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
0225                 {
0226                     perror("CDROMCDDA");
0227                     block->status = WM_CDM_CDDAERROR;
0228                     return (-1);
0229                 }
0230             }
0231         }
0232     }
0233 
0234     if (speed > 148)
0235     {
0236         /*
0237          * We want speed=148 to advance by cdda_length, but
0238          * speed=256 to advance cdda_length * 4.
0239          */
0240         current_position = current_position +
0241             (cdda.cdda_length * direction * (speed - 112)) / 36;
0242     }
0243     else
0244         current_position = current_position + cdda.cdda_length * direction;
0245 
0246     for (blk = 0; blk < numblocks; blk++)
0247     {
0248         /*
0249          * New valid Q-subchannel information?  Update the block
0250          * status.
0251          */
0252         q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
0253         if (*q == 1)
0254         {
0255             block->track =  unbcd[q[1]];
0256             block->index =  unbcd[q[2]];
0257             /*block->minute = unbcd[q[7]];
0258             block->second = unbcd[q[8]];*/
0259             block->frame =  unbcd[q[9]];
0260             block->status = WM_CDM_PLAYING;
0261             block->buflen = cdda.cdda_length;
0262         }
0263     }
0264 
0265     return wmcdda_normalize(block);
0266 }
0267 
0268 /*
0269  * Normalize a bunch of CDDA data.  Basically this means ripping out the
0270  * Q subchannel data and doing byte-swapping, since the CD audio is in
0271  * littleendian format.
0272  *
0273  * Scanning is handled here too.
0274  *
0275  * XXX - do byte swapping on Intel boxes?
0276  */
0277 long
0278 wmcdda_normalize(struct cdda_block *block)
0279 {
0280     int     i, nextq;
0281     long buflen = block->buflen;
0282     int     blocks = buflen / CDDABLKSIZE;
0283     unsigned char *rawbuf = block->buf;
0284     unsigned char   *dest = rawbuf;
0285     unsigned char   tmp;
0286     long        *buf32 = (long *)rawbuf, tmp32;
0287 
0288 /*
0289  * this was #ifndef LITTLEENDIAN
0290  * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
0291  */
0292 #if WM_BIG_ENDIAN
0293     if (blocks--)
0294         for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
0295         {
0296             /* Only need to use temp buffer on first block. */
0297             tmp = *rawbuf++;
0298             *dest++ = *rawbuf++;
0299             *dest++ = tmp;
0300         }
0301 #endif
0302 
0303     while (blocks--)
0304     {
0305         /* Skip over Q data. */
0306         rawbuf += 16;
0307 
0308         for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
0309         {
0310 #if WM_LITTLE_ENDIAN
0311             *dest++ = *rawbuf++;
0312             *dest++ = *rawbuf++;
0313 #else
0314             *dest++ = rawbuf[1];
0315             *dest++ = rawbuf[0];
0316             rawbuf += 2;
0317 #endif
0318         }
0319     }
0320 
0321     buflen -= ((buflen / CDDABLKSIZE) * 16);
0322 
0323     /*
0324      * Reverse the data here if we're playing backwards.
0325      * XXX - ideally this should be done above.
0326      */
0327     if (direction < 0)
0328     {
0329         buflen /= 4;    /* we can move 32 bits at a time. */
0330 
0331         for (i = 0; i < buflen / 2; i++)
0332         {
0333             tmp32 = buf32[i];
0334             buf32[i] = buf32[buflen - i - 1];
0335             buf32[buflen - i - 1] = tmp32;
0336         }
0337 
0338         buflen *= 4;
0339     }
0340 
0341     return (buflen);
0342 }
0343 
0344 /*
0345  * Set the playback direction.
0346  */
0347 void
0348 wmcdda_direction(int newdir)
0349 {
0350     if (newdir == 0)
0351     {
0352         numblocks = 20;
0353         direction = 1;
0354     }
0355     else
0356     {
0357         numblocks = 30;
0358         direction = -1;
0359     }
0360 }
0361 
0362 /*
0363  * Do system-specific stuff to get ready to play at a particular speed.
0364  */
0365 void
0366 wmcdda_speed(int speed)
0367 {
0368     if (speed > 128)
0369         numblocks = 12;
0370     else
0371         numblocks = direction > 0 ? 20 : 30;
0372 }
0373 
0374 #endif /* } */