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
open_audio_for_extraction(char * fname)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
extract_signal_handler(int sig,siginfo_t * info,void * context)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
handle_jitter(uchar_t * buf,uchar_t * last_end)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
read_audio_track(cd_device * dev,struct track_info * ti,bstreamhandle h)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
extract_audio(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