dae.c revision 0c83a891bb3029b854c3ebe4d0467b839a68fa49
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <sys/types.h>
2N/A#include <string.h>
2N/A#include <stdlib.h>
2N/A#include <libintl.h>
2N/A#include <signal.h>
2N/A
2N/A#include "bstream.h"
2N/A#include "util.h"
2N/A#include "misc_scsi.h"
2N/A#include "device.h"
2N/A#include "main.h"
2N/A#include "msgs.h"
2N/A
2N/A#define BLOCK_SIZE 2352
2N/A#define READ_BURST_SIZE 200
2N/A#define SMALL_READ_BURST_SIZE 24 /* < 64K in all cases */
2N/A#define READ_OVERLAP 7
2N/A#define BLOCKS_COMPARE 3
2N/A
2N/Astatic int abort_read;
2N/A
2N/A/*
2N/A * These are routines for extracting audio from a cd. During
2N/A * extraction we will also convert the audio type from the
2N/A * CD to the audio type specified on the command line. This
2N/A * handles both newer CD drives which support the MMC2 standard
2N/A * and older Sun Toshiba drives which need jitter correction.
2N/A */
2N/A
2N/Astatic bstreamhandle
2N/Aopen_audio_for_extraction(char *fname)
2N/A{
2N/A int at;
2N/A char *ext;
2N/A
2N/A if (audio_type == AUDIO_TYPE_NONE) {
2N/A ext = (char *)(strrchr(fname, '.'));
2N/A if (ext) {
2N/A ext++;
2N/A }
2N/A if ((ext == NULL) || ((at = get_audio_type(ext)) == -1)) {
2N/A err_msg(gettext(
2N/A "Cannot understand file extension for %s\n"),
2N/A fname);
2N/A exit(1);
2N/A }
2N/A } else {
2N/A at = audio_type;
2N/A }
2N/A if (at == AUDIO_TYPE_SUN)
2N/A return (open_au_write_stream(fname));
2N/A if (at == AUDIO_TYPE_WAV)
2N/A return (open_wav_write_stream(fname));
2N/A if (at == AUDIO_TYPE_CDA)
2N/A return (open_file_write_stream(fname));
2N/A if (at == AUDIO_TYPE_AUR)
2N/A return (open_aur_write_stream(fname));
2N/A return (NULL);
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Astatic void
2N/Aextract_signal_handler(int sig, siginfo_t *info, void *context)
2N/A{
2N/A abort_read = 1;
2N/A}
2N/A
/*
* 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 {
(void) printf(gettext(
"Speed set to closest approximation "));
(void) printf(gettext(
"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);
}