Home | History | Annotate | Download | only in cdrw
      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