/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include "bstream.h" #include "util.h" #include "misc_scsi.h" #include "device.h" #include "main.h" #include "msgs.h" #define BLOCK_SIZE 2352 #define READ_BURST_SIZE 200 #define SMALL_READ_BURST_SIZE 24 /* < 64K in all cases */ #define READ_OVERLAP 7 #define BLOCKS_COMPARE 3 static int abort_read; /* * These are routines for extracting audio from a cd. During * extraction we will also convert the audio type from the * CD to the audio type specified on the command line. This * handles both newer CD drives which support the MMC2 standard * and older Sun Toshiba drives which need jitter correction. */ static bstreamhandle open_audio_for_extraction(char *fname) { int at; char *ext; if (audio_type == AUDIO_TYPE_NONE) { ext = (char *)(strrchr(fname, '.')); if (ext) { ext++; } if ((ext == NULL) || ((at = get_audio_type(ext)) == -1)) { err_msg(gettext( "Cannot understand file extension for %s\n"), fname); exit(1); } } else { at = audio_type; } if (at == AUDIO_TYPE_SUN) return (open_au_write_stream(fname)); if (at == AUDIO_TYPE_WAV) return (open_wav_write_stream(fname)); if (at == AUDIO_TYPE_CDA) return (open_file_write_stream(fname)); if (at == AUDIO_TYPE_AUR) return (open_aur_write_stream(fname)); return (NULL); } /* ARGSUSED */ static void extract_signal_handler(int sig, siginfo_t *info, void *context) { abort_read = 1; } /* * Older drives use different data buffer and m:s:f channels to transmit audio * information. These channels may not be in sync with each other with the * maximum disparity being the size of the data buffer. So handling is needed * to keep these two channels in sync. */ static int handle_jitter(uchar_t *buf, uchar_t *last_end) { int i; for (i = BLOCK_SIZE*(READ_OVERLAP - BLOCKS_COMPARE); i >= 0; i -= 4) { if (memcmp(last_end - BLOCK_SIZE * BLOCKS_COMPARE, buf + i, BLOCK_SIZE * BLOCKS_COMPARE) == 0) { return (i + (BLOCK_SIZE * BLOCKS_COMPARE)); } } for (i = BLOCK_SIZE*(READ_OVERLAP - BLOCKS_COMPARE); i < 2*READ_OVERLAP*BLOCK_SIZE; i += 4) { if (memcmp(last_end - BLOCK_SIZE * BLOCKS_COMPARE, buf + i, BLOCK_SIZE * BLOCKS_COMPARE) == 0) { return (i + (BLOCK_SIZE * BLOCKS_COMPARE)); } } return (-1); } int read_audio_track(cd_device *dev, struct track_info *ti, bstreamhandle h) { uint32_t blocks_to_write, blocks_to_read, blks_to_overlap; uint32_t start_blk, end_blk, c_blk; uint32_t read_burst_size; uchar_t *tmp, *buf, *prev, *previous_end; int ret, off; struct sigaction sv; struct sigaction oldsv; ret = 0; abort_read = 0; /* * It is good to do small sized I/Os as we have seen many devices * choke with large I/Os. But if the device does not support * reading accurate CDDA then we have to do overlapped I/Os * and reducing size might affect performance. So use small * I/O size if device supports accurate CDDA. */ if (dev->d_cap & DEV_CAP_ACCURATE_CDDA) { read_burst_size = SMALL_READ_BURST_SIZE; } else { read_burst_size = READ_BURST_SIZE; } buf = (uchar_t *)my_zalloc(BLOCK_SIZE * read_burst_size); prev = (uchar_t *)my_zalloc(BLOCK_SIZE * read_burst_size); start_blk = ti->ti_start_address; end_blk = ti->ti_start_address + ti->ti_track_size - 1; /* Even when we need jitter correction, this will be 0 1st time */ blks_to_overlap = 0; off = 0; /* set up signal handler to write audio TOC if ^C is pressed */ sv.sa_handler = extract_signal_handler; (void) sigemptyset(&sv.sa_mask); sv.sa_flags = 0; (void) sigaction(SIGINT, &sv, &oldsv); if ((dev->d_cap & DEV_CAP_EXTRACT_CDDA) == 0) { err_msg(gettext("Audio extraction method unknown for %s\n"), dev->d_name ? dev->d_name : gettext("CD drive")); exit(1); } /* if the speed option given, try to change the speed */ if ((requested_speed != 0) && !cflag) { if (verbose) (void) printf(gettext("Trying to set speed to %dX.\n"), requested_speed); if (dev->d_speed_ctrl(dev, SET_READ_SPEED, requested_speed) == 0) { err_msg(gettext("Unable to set speed.\n")); exit(1); } if (verbose) { int speed; speed = dev->d_speed_ctrl(dev, GET_READ_SPEED, 0); if (speed == requested_speed) { (void) printf(gettext("Speed set to %dX.\n"), speed); } else if (speed == 0) { (void) printf(gettext("Could not obtain " "current Read Speed.\n")); } else { (void) printf(gettext("Speed set to " "closest approximation of %dX allowed " "by device (%dX).\n"), requested_speed, speed); } } } print_n_flush( gettext("Extracting audio from track %d..."), ti->ti_track_no); init_progress(); if (debug) (void) printf("\nStarting: %d Ending: %d\n", start_blk, end_blk); blocks_to_write = 0; for (c_blk = start_blk; c_blk < end_blk; c_blk += blocks_to_write) { /* update progress indicator */ (void) progress((end_blk - start_blk), (int64_t)(c_blk - start_blk)); blocks_to_read = end_blk - c_blk + blks_to_overlap; /* * Make sure we don't read more blocks than the maximum * burst size. */ if (blocks_to_read > read_burst_size) blocks_to_read = read_burst_size; if (dev->d_read_audio(dev, c_blk - blks_to_overlap, blocks_to_read, buf) == 0) goto read_audio_track_done; /* * This drive supports accurate audio extraction don't * do jitter correction. */ if ((c_blk == start_blk) || (dev->d_cap & DEV_CAP_ACCURATE_CDDA)) { blocks_to_write = blocks_to_read; previous_end = buf + (blocks_to_write * BLOCK_SIZE); goto skip_jitter_correction; } if (c_blk == start_blk) blks_to_overlap = 0; else blks_to_overlap = READ_OVERLAP; off = handle_jitter(buf, previous_end); if (off == -1) { if (debug) (void) printf( "jitter control failed\n"); /* recover if jitter correction failed */ off = BLOCK_SIZE * BLOCKS_COMPARE; } blocks_to_write = blocks_to_read - blks_to_overlap; while ((off + (blocks_to_write*BLOCK_SIZE)) > (blocks_to_read * BLOCK_SIZE)) { blocks_to_write--; } if ((blocks_to_write + c_blk) > end_blk) { blocks_to_write = end_blk - c_blk; } if (blocks_to_write == 0) { c_blk = end_blk - 1; blocks_to_write = 1; (void) memset(&buf[off], 0, off % BLOCK_SIZE); } previous_end = buf + off + blocks_to_write * BLOCK_SIZE; skip_jitter_correction: (void) memcpy(prev, buf, read_burst_size * BLOCK_SIZE); if (h->bstr_write(h, &buf[off], blocks_to_write*BLOCK_SIZE) < 0) goto read_audio_track_done; tmp = buf; buf = prev; prev = tmp; if (abort_read == 1) goto read_audio_track_done; } ret = 1; (void) str_print(gettext("done.\n"), progress_pos); read_audio_track_done: (void) sigaction(SIGINT, &oldsv, (struct sigaction *)0); free(buf); free(prev); return (ret); } void extract_audio(void) { bstreamhandle h; struct track_info *ti; (void) check_device(target, CHECK_NO_MEDIA | CHECK_DEVICE_NOT_READY | EXIT_IF_CHECK_FAILED); ti = (struct track_info *)my_zalloc(sizeof (*ti)); if (!build_track_info(target, extract_track_no, ti)) { err_msg(gettext("Cannot get track information for track %d\n"), extract_track_no); exit(1); } /* Verify track */ if ((ti->ti_track_size == 0) || ((ti->ti_flags & TI_NWA_VALID) && (ti->ti_start_address == ti->ti_nwa))) { err_msg(gettext("Track %d is empty\n"), extract_track_no); exit(1); } if (ti->ti_track_mode & 4) { err_msg(gettext("Track %d is not an audio track\n"), extract_track_no); exit(1); } if (ti->ti_data_mode == 2) { err_msg(gettext("Track format is not supported\n")); exit(1); } h = open_audio_for_extraction(extract_file); if (h == NULL) { err_msg(gettext("Cannot open %s:%s\n"), extract_file, get_err_str()); exit(1); } if (read_audio_track(target, ti, h) == 0) { err_msg(gettext("Extract audio failed\n")); h->bstr_close(h); exit(1); } if (h->bstr_close(h) != 0) { err_msg(gettext("Error closing audio stream : %s\n"), get_err_str()); exit(1); } exit(0); }