diff --git a/cdrskin/cdrskin.1 b/cdrskin/cdrskin.1
index e5c5fa4..52634c0 100644
--- a/cdrskin/cdrskin.1
+++ b/cdrskin/cdrskin.1
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
-.TH CDRSKIN 1 "Version 1.3.1, May 20, 2013"
+.TH CDRSKIN 1 "Version 1.3.1, May 23, 2013"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@@ -37,7 +37,7 @@ Blanking of CD-RW and DVD-RW.
.br
Formatting of DVD-RW, DVD+RW, DVD-RAM, BD.
.br
-Burning of data or audio tracks to CD,
+Burning of data tracks or audio tracks with CD-TEXT to CD,
.br
either in versatile Track at Once mode (TAO)
.br
@@ -55,6 +55,8 @@ on overwriteable DVD+RW, DVD-RW, DVD-RAM, BD-RE
.br
or on data file or block device.
.br
+Extraction of audio tracks and CD-TEXT to hard disk files.
+.br
Bus scan, burnfree, speed options, retrieving media info, padding, fifo.
.br
See section EXAMPLES at the end of this text.
@@ -1000,6 +1002,7 @@ the file with the given path. If CD-TEXT can be retrieved, then this file
will be suitable for option textfile=.
.br
Not all drives can read CD-TEXT and not all audio CDs bear CD-TEXT.
+It is not considered an error if no CD-TEXT is available.
.TP
.BI cdtext_to_v07t= path
Extract the CD-TEXT packs from the lead-in of an audio CD and write them
@@ -1010,6 +1013,7 @@ will be suitable for option input_sheet_v07t=.
If the given path is "-", then the result is printed to standard output.
.br
Not all drives can read CD-TEXT and not all audio CDs bear CD-TEXT.
+It is not considered an error if no CD-TEXT is available.
.TP
.BI \--demand_a_drive
Exit with a nonzero value if no drive can be found during a bus scan.
@@ -1074,6 +1078,32 @@ A number of 64 KB may improve throughput with systems
which show latency problems. The default depends on media type, option
stream_recording=, and on compile time options.
.TP
+.BI extract_audio_to= directory_path
+Extract tracks from an audio CD as separate WAVE audio files into the
+given directory.
+This directory has to already exist, but none of the track files may exist.
+This option will rather fail than overwrite an existing file.
+.br
+By default all tracks of the CD are extracted to files with names
+trackNN.wav, where NN is the track number from 01 to at most 99.
+.TP
+.BI extract_basename= name
+Set a filename which shall be used by extract_audio_to= instead of the default
+name "track".
+.TP
+.BI --extract_dap
+Enable Digital Audio Play flaw obscuring mechanisms
+like audio data mute and interpolate.
+.TP
+.BI extract_tracks= number[,number[,...]]
+Set a list of track numbers to define which tracks shall be extracted
+by extract_audio_to=.
+If no extract_tracks= is given, then all audio tracks get extracted.
+It is permissible to have more than one extract_tracks= option in order
+to split a long list into shorter pieces.
+.br
+The lowest permissible track number is 1, the highest is 99.
+.TP
.BI fallback_program= command
Set a command name to be executed if cdrskin encounters a known cdrecord
option which it does not yet support. If a non-empty command is given with
@@ -1613,7 +1643,7 @@ cdrskin -v dev=/dev/sr0 blank=deformat_sequential
.br
cdrskin -v dev=/dev/hdc speed=12 fs=8m \\
.br
-blank=as_needed -eject padsize=300k my_image.iso
+ blank=as_needed -eject padsize=300k my_image.iso
.SS
.B Write compressed afio archive on-the-fly (not possible with minimally blanked DVD-RW or DVD-R DL):
.br
@@ -1621,7 +1651,7 @@ find . | afio -oZ - | \\
.br
cdrskin -v dev=0,1,0 fs=32m speed=8 \\
.br
-blank=as_needed padsize=300k -
+ blank=as_needed padsize=300k -
.SS
.B Write multi-session to the same CD, DVD-R[W], DVD+R[/DL], or BD-R:
.br
@@ -1643,16 +1673,23 @@ mkisofs ... -C "$c_values" ...
.br
x=$(cdrskin dev=/dev/sr0 -multi \\
.br
---tell_media_space 2>/dev/null)
+ --tell_media_space 2>/dev/null)
.br
echo "Available: $x blocks of 2048 data bytes"
.SS
-.B Write audio tracks to CD:
+.B Write audio tracks and CD-TEXT to CD:
.br
cdrskin -v dev=ATA:1,0,0 speed=48 -sao \\
.br
-track1.wav track2.au -audio -swab track3.raw
+ input_sheet_v07t=cdtext.v07t \\
.br
+ track1.wav track2.au -audio -swab track3.raw
+.SS
+.B Extract audio tracks and CD-TEXT from CD into directory /home/me/my_cd:
+.br
+cdrskin -v dev=/dev/sr0 extract_audio_to=/home/me/my_cd \\
+.br
+ cdtext_to_v07t=/home/me/my_cd/cdtext.v07t
.SH FILES
.SS
Startup files:
diff --git a/cdrskin/cdrskin.c b/cdrskin/cdrskin.c
index 3503876..8d343ff 100644
--- a/cdrskin/cdrskin.c
+++ b/cdrskin/cdrskin.c
@@ -2850,8 +2850,21 @@ set_dev:;
printf(" dvd_obs=\"default\"|number\n");
printf(
" set number of bytes per DVD/BD write: 32k or 64k\n");
+ printf(" extract_audio_to=
\n");
printf(
- " fallback_program= use external program for exotic CD jobs\n");
+ " Copy all tracks of an audio CD as separate\n");
+ printf(
+ " .WAV files /track.wav to hard disk\n");
+ printf(" extract_basename=\n");
+ printf(
+ " Compose track files as /.wav\n");
+ printf(
+ " --extract_dap Enable flaw obscuring mechanisms for audio reading\n");
+ printf(" extract_tracks=\n");
+ printf(
+ " Restrict extract_audio_to= to list of tracks.\n");
+ printf(
+ " fallback_program= use external program for exotic CD jobs.\n");
printf(" --fifo_disable disable fifo despite any fs=...\n");
printf(" --fifo_per_track use a separate fifo for each track\n");
printf(
@@ -3117,7 +3130,7 @@ set_severities:;
if(o->verbosity>=Cdrskin_verbose_debuG)
Cdrpreskin_set_severities(o,"NEVER","DEBUG",0);
else if(o->verbosity >= Cdrskin_verbose_progresS)
- Cdrpreskin_set_severities(o, "NEVER", "NOTE", 0);
+ Cdrpreskin_set_severities(o, "NEVER", "UPDATE", 0);
} else if(strcmp(argv[i],"-vv")==0 || strcmp(argv[i],"-vvv")==0 ||
strcmp(argv[i],"-vvvv")==0) {
@@ -3367,6 +3380,13 @@ struct CdrskiN {
char cdtext_to_textfile_path[Cdrskin_strleN];
int do_cdtext_to_vt07;
char cdtext_to_vt07_path[Cdrskin_strleN];
+ int do_extract_audio;
+ char extract_audio_dir[Cdrskin_strleN];
+ char extract_basename[249];
+ char extract_audio_tracks[100]; /* if [i] > 0 : extract track i */
+ int extract_flags; /* bit3 = for flag bit3 of
+ burn_drive_extract_audio()
+ */
#ifdef Libburn_develop_quality_scaN
int do_qcheck; /* 0= no , 1=nec_optiarc_rep_err_rate */
@@ -3622,6 +3642,12 @@ int Cdrskin_new(struct CdrskiN **skin, struct CdrpreskiN *preskin, int flag)
o->cdtext_to_textfile_path[0]= 0;
o->do_cdtext_to_vt07= 0;
o->cdtext_to_vt07_path[0]= 0;
+ o->do_extract_audio= 0;
+ o->extract_audio_dir[0]= 0;
+ o->extract_basename[0]= 0;
+ for(i= 0; i < 100; i++)
+ o->extract_audio_tracks[i]= 0;
+ o->extract_flags= 0;
#ifdef Libburn_develop_quality_scaN
o->do_qcheck= 0;
@@ -4971,12 +4997,16 @@ int Cdrskin_cdtext_to_file(struct CdrskiN *skin, char *path, int flag)
FILE *fp= NULL;
struct stat stbuf;
+ ret= Cdrskin_grab_drive(skin, 0);
+ if(ret<=0)
+ goto ex;
+
fmt= flag & 15;
drive= skin->drives[skin->driveno].drive;
ret= burn_disc_get_leadin_text(drive, &text_packs, &num_packs, 0);
if(ret <= 0 || num_packs <= 0) {
fprintf(stderr, "cdrskin: No CD-Text or CD-Text unaware drive.\n");
- return(2);
+ ret= 2; goto ex;
}
if(fmt == 1) {
ret = burn_make_input_sheet_v07t(text_packs, num_packs, 0, 0, &result,
@@ -5035,6 +5065,7 @@ ex:;
return(1);
}
+
/** Perform -toc under control of Cdrskin_atip().
@param flag Bitfield for control purposes:
bit0= do not list sessions separately (do it cdrecord style)
@@ -5860,6 +5891,117 @@ ex:;
}
+/*
+ @param flag Bitfield for control purposes:
+ bit3= Enable DAP : "flaw obscuring mechanisms like
+ audio data mute and interpolate"
+*/
+int Cdrskin_extract_audio_to_dir(struct CdrskiN *skin,
+ char *dir, char *basename,
+ char print_tracks[100], int flag)
+{
+ int num_sessions= 0, num_tracks= 0, profile_number, session_no, track_no;
+ int track_count= 0, existing= 0, ret, pass, not_audio= 0, pick_tracks= 0, i;
+ int tracks_extracted= 0, min_tno= 100, max_tno= 0;
+ struct burn_drive *drive;
+ struct burn_disc *disc= NULL;
+ struct burn_session **sessions;
+ struct burn_track **tracks;
+ enum burn_disc_status s;
+ struct burn_toc_entry toc_entry;
+ struct stat stbuf;
+ char profile_name[80], path[4096 + 256];
+
+ ret= Cdrskin_grab_drive(skin, 0);
+ if(ret<=0)
+ goto ex;
+ drive= skin->drives[skin->driveno].drive;
+ s= burn_disc_get_status(drive);
+ disc= burn_drive_get_disc(drive);
+ if(s == BURN_DISC_EMPTY || s == BURN_DISC_BLANK || disc == NULL) {
+not_readable:;
+ fprintf(stderr, "cdrskin: SORRY : No audio CD in drive.\n");
+ ret= 0; goto ex;
+ }
+ ret= burn_disc_get_profile(drive, &profile_number, profile_name);
+ if(ret <= 0 || profile_number < 0x08 || profile_number > 0x0a) {
+not_audio_cd:;
+ fprintf(stderr, "cdrskin: SORRY : Medium in drive is not an audio CD.\n");
+ ret= 0; goto ex;
+ }
+ for(i= 0; i < 99; i++)
+ if(print_tracks[i])
+ pick_tracks= 1;
+ sessions= burn_disc_get_sessions(disc, &num_sessions);
+ if(num_sessions <= 0)
+ goto not_readable;
+ for(pass= 0; pass < 2; pass++) {
+ if(pass > 0) {
+ if(existing > 0)
+ {ret= 0; goto ex;}
+ if(not_audio == track_count)
+ goto not_audio_cd;
+ }
+ track_count= 0;
+ for(session_no= 0; session_no < num_sessions; session_no++) {
+ tracks= burn_session_get_tracks(sessions[session_no],&num_tracks);
+ if(tracks==NULL)
+ continue;
+ for(track_no= 0; track_no < num_tracks; track_no++) {
+ track_count++;
+ burn_track_get_entry(tracks[track_no], &toc_entry);
+ if(toc_entry.point < min_tno)
+ min_tno= toc_entry.point;
+ if(toc_entry.point > max_tno)
+ max_tno= toc_entry.point;
+ if(pick_tracks) {
+ if(toc_entry.point <= 0 || toc_entry.point > 99)
+ continue; /* should not happen */
+ if(print_tracks[toc_entry.point] == 0)
+ continue;
+ }
+ if((toc_entry.control & 7) >= 4) {
+ if(pass == 0)
+ fprintf(stderr,
+ "cdrskin: WARNING : Track %2.2d is not an audio track.\n",
+ toc_entry.point);
+ not_audio++;
+ continue;
+ }
+ sprintf(path, "%s/%s%2.2u.wav", dir, basename, toc_entry.point);
+ if(pass == 0) {
+ if(stat(path, &stbuf) != -1) {
+ fprintf(stderr,
+ "cdrskin: SORRY : May not overwrite existing file: '%s'\n",
+ path);
+ existing++;
+ }
+ continue;
+ }
+ if(skin->verbosity >= Cdrskin_verbose_progresS)
+ fprintf(stderr, "cdrskin: Writing audio track file: %s\n", path);
+ ret= burn_drive_extract_audio_track(drive, tracks[track_no], path,
+ (flag & 8) | (skin->verbosity >= Cdrskin_verbose_progresS));
+ if(ret <= 0)
+ goto ex;
+ tracks_extracted++;
+ }
+ }
+ }
+ if(tracks_extracted == 0 && pick_tracks) {
+ fprintf(stderr,
+ "cdrskin: SORRY : Not a single track matched the list of extract_tracks=\n");
+ if(min_tno < 100 && max_tno > 0)
+ fprintf(stderr, "cdrskin: HINT : Track range is %2.2d to %2.2d\n",
+ min_tno, max_tno);
+ ret= 0; goto ex;
+ }
+ ret= 1;
+ex:;
+ return(ret);
+}
+
+
#ifndef Cdrskin_extra_leaN
/* A70324: proposal by Eduard Bloch */
@@ -8303,6 +8445,68 @@ dvd_obs:;
ClN(printf("cdrskin: ignoring obsolete eject_device=%s\n",
skin->eject_device));
+ } else if(strncmp(argv[i],"-extract_audio_to=", 18)==0) {
+ value_pt= argpt + 18;
+ goto extract_audio_to;
+ } else if(strncmp(argpt, "extract_audio_to=", 17) == 0) {
+ value_pt= argpt + 17;
+extract_audio_to:;
+ if(strlen(value_pt) >= sizeof(skin->extract_audio_dir)) {
+ fprintf(stderr,
+ "cdrskin: FAILURE : extract_audio_to=... is much too long.\n");
+ return(0);
+ }
+ skin->do_extract_audio= 1;
+ strcpy(skin->extract_audio_dir, value_pt);
+
+ } else if(strncmp(argv[i],"-extract_tracks=", 16)==0) {
+ value_pt= argpt + 16;
+ goto extract_tracks;
+ } else if(strncmp(argpt, "extract_tracks=", 15) == 0) {
+ value_pt= argpt + 15;
+extract_tracks:;
+ value= 0.0;
+ for(cpt= value_pt; ; cpt++) {
+ if(*cpt >= '0' && *cpt <= '9') {
+ value= value * 10 + *cpt - '0';
+ } else {
+ if(value >= 1.0 && value <= 99.0) {
+ skin->extract_audio_tracks[(int) value]= 1;
+ if(skin->verbosity >= Cdrskin_verbose_cmD)
+ fprintf(stderr, "cdrskin: Will extract track number %.f\n",
+ value);
+ } else {
+ fprintf(stderr,
+ "cdrskin: WARNING : extract_tracks= with unsuitable number: %.f\n",
+ value);
+ }
+ if(*cpt == 0)
+ break;
+ value= 0;
+ }
+ }
+
+ } else if(strncmp(argv[i],"-extract_basename=", 18)==0) {
+ value_pt= argpt + 18;
+ goto extract_basename;
+ } else if(strncmp(argpt, "extract_basename=", 17) == 0) {
+ value_pt= argpt + 17;
+extract_basename:;
+ if(strchr(value_pt, '/') != NULL) {
+ fprintf(stderr,
+ "cdrskin: FAILURE : extract_basename=... may not contain '/'\n");
+ return(0);
+ }
+ if(strlen(value_pt) > 248) {
+ fprintf(stderr,
+ "cdrskin: FAILURE : Oversized extract_basename=... (Max. 248)\n");
+ return(0);
+ }
+ strcpy(skin->extract_basename, value_pt);
+
+ } else if(strcmp(argv[i],"--extract_dap") == 0) {
+ skin->extract_flags|= 8;
+
#ifndef Cdrskin_extra_leaN
} else if(strcmp(argv[i],"--fifo_disable")==0) {
@@ -9284,6 +9488,14 @@ int Cdrskin_run(struct CdrskiN *skin, int *exit_value, int flag)
if(ret<=0)
{*exit_value= 19; goto ex;}
}
+ if(skin->do_extract_audio) {
+ ret= Cdrskin_extract_audio_to_dir(skin, skin->extract_audio_dir,
+ skin->extract_basename,
+ skin->extract_audio_tracks,
+ skin->extract_flags & 8);
+ if(ret<=0)
+ {*exit_value= 19; goto ex;}
+ }
if(skin->do_list_speeds) {
if(skin->n_drives<=0)
{*exit_value= 17; goto no_drive;}