#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <libintl.h>
#include <signal.h>
#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
#define READ_OVERLAP 7
#define BLOCKS_COMPARE 3
static int abort_read;
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);
}
static void
extract_signal_handler(int sig, siginfo_t *info, void *context)
{
abort_read = 1;
}
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;
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;
blks_to_overlap = 0;
off = 0;
sv.sa_sigaction = 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 ((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) {
(void) progress((end_blk - start_blk),
(int64_t)(c_blk - start_blk));
blocks_to_read = end_blk - c_blk + blks_to_overlap;
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;
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");
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);
}
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);
}