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