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 if (speed == 0) { 188 (void) printf(gettext("Could not obtain " 189 "current Read Speed.\n")); 190 } else { 191 (void) printf(gettext("Speed set to " 192 "closest approximation of %dX allowed " 193 "by device (%dX).\n"), 194 requested_speed, speed); 195 } 196 } 197 } 198 199 print_n_flush( 200 gettext("Extracting audio from track %d..."), ti->ti_track_no); 201 init_progress(); 202 203 if (debug) 204 (void) printf("\nStarting: %d Ending: %d\n", 205 start_blk, end_blk); 206 207 blocks_to_write = 0; 208 209 for (c_blk = start_blk; c_blk < end_blk; c_blk += blocks_to_write) { 210 /* update progress indicator */ 211 (void) progress((end_blk - start_blk), 212 (int64_t)(c_blk - start_blk)); 213 blocks_to_read = end_blk - c_blk + blks_to_overlap; 214 215 /* 216 * Make sure we don't read more blocks than the maximum 217 * burst size. 218 */ 219 220 if (blocks_to_read > read_burst_size) 221 blocks_to_read = read_burst_size; 222 223 if (dev->d_read_audio(dev, c_blk - blks_to_overlap, 224 blocks_to_read, buf) == 0) 225 goto read_audio_track_done; 226 227 /* 228 * This drive supports accurate audio extraction don't 229 * do jitter correction. 230 */ 231 if ((c_blk == start_blk) || 232 (dev->d_cap & DEV_CAP_ACCURATE_CDDA)) { 233 blocks_to_write = blocks_to_read; 234 previous_end = buf + (blocks_to_write * BLOCK_SIZE); 235 goto skip_jitter_correction; 236 } 237 238 if (c_blk == start_blk) 239 blks_to_overlap = 0; 240 else 241 blks_to_overlap = READ_OVERLAP; 242 off = handle_jitter(buf, previous_end); 243 if (off == -1) { 244 if (debug) 245 (void) printf( 246 "jitter control failed\n"); 247 248 /* recover if jitter correction failed */ 249 off = BLOCK_SIZE * BLOCKS_COMPARE; 250 } 251 252 blocks_to_write = blocks_to_read - blks_to_overlap; 253 254 while ((off + (blocks_to_write*BLOCK_SIZE)) > 255 (blocks_to_read * BLOCK_SIZE)) { 256 blocks_to_write--; 257 } 258 259 if ((blocks_to_write + c_blk) > end_blk) { 260 blocks_to_write = end_blk - c_blk; 261 } 262 263 if (blocks_to_write == 0) { 264 c_blk = end_blk - 1; 265 blocks_to_write = 1; 266 (void) memset(&buf[off], 0, off % BLOCK_SIZE); 267 } 268 269 previous_end = buf + off + blocks_to_write * BLOCK_SIZE; 270 skip_jitter_correction: 271 (void) memcpy(prev, buf, read_burst_size * BLOCK_SIZE); 272 if (h->bstr_write(h, &buf[off], blocks_to_write*BLOCK_SIZE) 273 < 0) 274 goto read_audio_track_done; 275 tmp = buf; 276 buf = prev; 277 prev = tmp; 278 279 if (abort_read == 1) 280 goto read_audio_track_done; 281 } 282 283 ret = 1; 284 (void) str_print(gettext("done.\n"), progress_pos); 285 286 read_audio_track_done: 287 (void) sigaction(SIGINT, &oldsv, (struct sigaction *)0); 288 289 free(buf); 290 free(prev); 291 return (ret); 292 } 293 294 void 295 extract_audio(void) 296 { 297 bstreamhandle h; 298 struct track_info *ti; 299 300 (void) check_device(target, CHECK_NO_MEDIA | CHECK_DEVICE_NOT_READY | 301 EXIT_IF_CHECK_FAILED); 302 303 ti = (struct track_info *)my_zalloc(sizeof (*ti)); 304 if (!build_track_info(target, extract_track_no, ti)) { 305 err_msg(gettext("Cannot get track information for track %d\n"), 306 extract_track_no); 307 exit(1); 308 } 309 310 /* Verify track */ 311 if ((ti->ti_track_size == 0) || ((ti->ti_flags & TI_NWA_VALID) && 312 (ti->ti_start_address == ti->ti_nwa))) { 313 err_msg(gettext("Track %d is empty\n"), extract_track_no); 314 exit(1); 315 } 316 if (ti->ti_track_mode & 4) { 317 err_msg(gettext("Track %d is not an audio track\n"), 318 extract_track_no); 319 exit(1); 320 } 321 if (ti->ti_data_mode == 2) { 322 err_msg(gettext("Track format is not supported\n")); 323 exit(1); 324 } 325 326 h = open_audio_for_extraction(extract_file); 327 if (h == NULL) { 328 err_msg(gettext("Cannot open %s:%s\n"), extract_file, 329 get_err_str()); 330 exit(1); 331 } 332 if (read_audio_track(target, ti, h) == 0) { 333 err_msg(gettext("Extract audio failed\n")); 334 h->bstr_close(h); 335 exit(1); 336 } 337 if (h->bstr_close(h) != 0) { 338 err_msg(gettext("Error closing audio stream : %s\n"), 339 get_err_str()); 340 exit(1); 341 } 342 exit(0); 343 } 344