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