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