File indexing completed on 2024-03-24 04:57:17
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 /* } */