xref: /illumos-gate/usr/src/cmd/cdrw/dae.c (revision 98584592)
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