1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include <libintl.h> 33 #include <signal.h> 34 35 #include "bstream.h" 36 #include "util.h" 37 #include "misc_scsi.h" 38 #include "device.h" 39 #include "main.h" 40 #include "msgs.h" 41 42 #define BLOCK_SIZE 2352 43 #define READ_BURST_SIZE 200 44 #define SMALL_READ_BURST_SIZE 24 /* < 64K in all cases */ 45 #define READ_OVERLAP 7 46 #define BLOCKS_COMPARE 3 47 48 static int abort_read; 49 50 /* 51 * These are routines for extracting audio from a cd. During 52 * extraction we will also convert the audio type from the 53 * CD to the audio type specified on the command line. This 54 * handles both newer CD drives which support the MMC2 standard 55 * and older Sun Toshiba drives which need jitter correction. 56 */ 57 58 static bstreamhandle 59 open_audio_for_extraction(char *fname) 60 { 61 int at; 62 char *ext; 63 64 if (audio_type == AUDIO_TYPE_NONE) { 65 ext = (char *)(strrchr(fname, '.')); 66 if (ext) { 67 ext++; 68 } 69 if ((ext == NULL) || ((at = get_audio_type(ext)) == -1)) { 70 err_msg(gettext( 71 "Cannot understand file extension for %s\n"), 72 fname); 73 exit(1); 74 } 75 } else { 76 at = audio_type; 77 } 78 if (at == AUDIO_TYPE_SUN) 79 return (open_au_write_stream(fname)); 80 if (at == AUDIO_TYPE_WAV) 81 return (open_wav_write_stream(fname)); 82 if (at == AUDIO_TYPE_CDA) 83 return (open_file_write_stream(fname)); 84 if (at == AUDIO_TYPE_AUR) 85 return (open_aur_write_stream(fname)); 86 return (NULL); 87 } 88 89 /* ARGSUSED */ 90 static void 91 extract_signal_handler(int sig, siginfo_t *info, void *context) 92 { 93 abort_read = 1; 94 } 95 96 /* 97 * Older drives use different data buffer and m:s:f channels to transmit audio 98 * information. These channels may not be in sync with each other with the 99 * maximum disparity being the size of the data buffer. So handling is needed 100 * to keep these two channels in sync. 101 */ 102 103 static int 104 handle_jitter(uchar_t *buf, uchar_t *last_end) 105 { 106 int i; 107 for (i = BLOCK_SIZE*(READ_OVERLAP - BLOCKS_COMPARE); i >= 0; i -= 4) { 108 if (memcmp(last_end - BLOCK_SIZE * BLOCKS_COMPARE, buf + i, 109 BLOCK_SIZE * BLOCKS_COMPARE) == 0) { 110 return (i + (BLOCK_SIZE * BLOCKS_COMPARE)); 111 } 112 } 113 for (i = BLOCK_SIZE*(READ_OVERLAP - BLOCKS_COMPARE); 114 i < 2*READ_OVERLAP*BLOCK_SIZE; i += 4) { 115 if (memcmp(last_end - BLOCK_SIZE * BLOCKS_COMPARE, buf + i, 116 BLOCK_SIZE * BLOCKS_COMPARE) == 0) { 117 return (i + (BLOCK_SIZE * BLOCKS_COMPARE)); 118 } 119 } 120 return (-1); 121 } 122 123 int 124 read_audio_track(cd_device *dev, struct track_info *ti, bstreamhandle h) 125 { 126 uint32_t blocks_to_write, blocks_to_read, blks_to_overlap; 127 uint32_t start_blk, end_blk, c_blk; 128 uint32_t read_burst_size; 129 uchar_t *tmp, *buf, *prev, *previous_end; 130 int ret, off; 131 struct sigaction sv; 132 struct sigaction oldsv; 133 134 ret = 0; 135 abort_read = 0; 136 137 /* 138 * It is good to do small sized I/Os as we have seen many devices 139 * choke with large I/Os. But if the device does not support 140 * reading accurate CDDA then we have to do overlapped I/Os 141 * and reducing size might affect performance. So use small 142 * I/O size if device supports accurate CDDA. 143 */ 144 if (dev->d_cap & DEV_CAP_ACCURATE_CDDA) { 145 read_burst_size = SMALL_READ_BURST_SIZE; 146 } else { 147 read_burst_size = READ_BURST_SIZE; 148 } 149 buf = (uchar_t *)my_zalloc(BLOCK_SIZE * read_burst_size); 150 prev = (uchar_t *)my_zalloc(BLOCK_SIZE * read_burst_size); 151 start_blk = ti->ti_start_address; 152 end_blk = ti->ti_start_address + ti->ti_track_size - 1; 153 154 /* Even when we need jitter correction, this will be 0 1st time */ 155 blks_to_overlap = 0; 156 off = 0; 157 158 /* set up signal handler to write audio TOC if ^C is pressed */ 159 sv.sa_handler = extract_signal_handler; 160 (void) sigemptyset(&sv.sa_mask); 161 sv.sa_flags = 0; 162 (void) sigaction(SIGINT, &sv, &oldsv); 163 164 if ((dev->d_cap & DEV_CAP_EXTRACT_CDDA) == 0) { 165 err_msg(gettext("Audio extraction method unknown for %s\n"), 166 dev->d_name ? dev->d_name : gettext("CD drive")); 167 exit(1); 168 } 169 170 /* if the speed option given, try to change the speed */ 171 if ((requested_speed != 0) && !cflag) { 172 if (verbose) 173 (void) printf(gettext("Trying to set speed to %dX.\n"), 174 requested_speed); 175 if (dev->d_speed_ctrl(dev, SET_READ_SPEED, 176 requested_speed) == 0) { 177 178 err_msg(gettext("Unable to set speed.\n")); 179 exit(1); 180 } 181 if (verbose) { 182 int speed; 183 speed = dev->d_speed_ctrl(dev, GET_READ_SPEED, 0); 184 if (speed == requested_speed) { 185 (void) printf(gettext("Speed set to %dX.\n"), 186 speed); 187 } else { 188 (void) printf(gettext( 189 "Speed set to closest approximation ")); 190 191 (void) printf(gettext( 192 "of %dX allowed by device (%dX).\n"), 193 requested_speed, speed); 194 } 195 } 196 } 197 198 print_n_flush( 199 gettext("Extracting audio from track %d..."), ti->ti_track_no); 200 init_progress(); 201 202 if (debug) 203 (void) printf("\nStarting: %d Ending: %d\n", 204 start_blk, end_blk); 205 206 blocks_to_write = 0; 207 208 for (c_blk = start_blk; c_blk < end_blk; c_blk += blocks_to_write) { 209 /* update progress indicator */ 210 (void) progress((end_blk - start_blk), 211 (int64_t)(c_blk - start_blk)); 212 blocks_to_read = end_blk - c_blk + blks_to_overlap; 213 214 /* 215 * Make sure we don't read more blocks than the maximum 216 * burst size. 217 */ 218 219 if (blocks_to_read > read_burst_size) 220 blocks_to_read = read_burst_size; 221 222 if (dev->d_read_audio(dev, c_blk - blks_to_overlap, 223 blocks_to_read, buf) == 0) 224 goto read_audio_track_done; 225 226 /* 227 * This drive supports accurate audio extraction don't 228 * do jitter correction. 229 */ 230 if ((c_blk == start_blk) || 231 (dev->d_cap & DEV_CAP_ACCURATE_CDDA)) { 232 blocks_to_write = blocks_to_read; 233 previous_end = buf + (blocks_to_write * BLOCK_SIZE); 234 goto skip_jitter_correction; 235 } 236 237 if (c_blk == start_blk) 238 blks_to_overlap = 0; 239 else 240 blks_to_overlap = READ_OVERLAP; 241 off = handle_jitter(buf, previous_end); 242 if (off == -1) { 243 if (debug) 244 (void) printf( 245 "jitter control failed\n"); 246 247 /* recover if jitter correction failed */ 248 off = BLOCK_SIZE * BLOCKS_COMPARE; 249 } 250 251 blocks_to_write = blocks_to_read - blks_to_overlap; 252 253 while ((off + (blocks_to_write*BLOCK_SIZE)) > 254 (blocks_to_read * BLOCK_SIZE)) { 255 blocks_to_write--; 256 } 257 258 if ((blocks_to_write + c_blk) > end_blk) { 259 blocks_to_write = end_blk - c_blk; 260 } 261 262 if (blocks_to_write == 0) { 263 c_blk = end_blk - 1; 264 blocks_to_write = 1; 265 (void) memset(&buf[off], 0, off % BLOCK_SIZE); 266 } 267 268 previous_end = buf + off + blocks_to_write * BLOCK_SIZE; 269 skip_jitter_correction: 270 (void) memcpy(prev, buf, read_burst_size * BLOCK_SIZE); 271 if (h->bstr_write(h, &buf[off], blocks_to_write*BLOCK_SIZE) 272 < 0) 273 goto read_audio_track_done; 274 tmp = buf; 275 buf = prev; 276 prev = tmp; 277 278 if (abort_read == 1) 279 goto read_audio_track_done; 280 } 281 282 ret = 1; 283 (void) str_print(gettext("done.\n"), progress_pos); 284 285 read_audio_track_done: 286 (void) sigaction(SIGINT, &oldsv, (struct sigaction *)0); 287 288 free(buf); 289 free(prev); 290 return (ret); 291 } 292 293 void 294 extract_audio(void) 295 { 296 bstreamhandle h; 297 struct track_info *ti; 298 299 (void) check_device(target, CHECK_NO_MEDIA | CHECK_DEVICE_NOT_READY | 300 EXIT_IF_CHECK_FAILED); 301 302 ti = (struct track_info *)my_zalloc(sizeof (*ti)); 303 if (!build_track_info(target, extract_track_no, ti)) { 304 err_msg(gettext("Cannot get track information for track %d\n"), 305 extract_track_no); 306 exit(1); 307 } 308 309 /* Verify track */ 310 if ((ti->ti_track_size == 0) || ((ti->ti_flags & TI_NWA_VALID) && 311 (ti->ti_start_address == ti->ti_nwa))) { 312 err_msg(gettext("Track %d is empty\n"), extract_track_no); 313 exit(1); 314 } 315 if (ti->ti_track_mode & 4) { 316 err_msg(gettext("Track %d is not an audio track\n"), 317 extract_track_no); 318 exit(1); 319 } 320 if (ti->ti_data_mode == 2) { 321 err_msg(gettext("Track format is not supported\n")); 322 exit(1); 323 } 324 325 h = open_audio_for_extraction(extract_file); 326 if (h == NULL) { 327 err_msg(gettext("Cannot open %s:%s\n"), extract_file, 328 get_err_str()); 329 exit(1); 330 } 331 if (read_audio_track(target, ti, h) == 0) { 332 err_msg(gettext("Extract audio failed\n")); 333 h->bstr_close(h); 334 exit(1); 335 } 336 if (h->bstr_close(h) != 0) { 337 err_msg(gettext("Error closing audio stream : %s\n"), 338 get_err_str()); 339 exit(1); 340 } 341 exit(0); 342 } 343