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