2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)cdda2wav.c 1.64 06/02/19 Copyright 1998-2004 Heiko Eissfeldt, Copyright 2004-2006 J. Schilling */
14 #undef DEBUG_BUFFER_ADDRESSES
18 #undef DEBUG_DYN_OVERLAP
20 #define DEBUG_ILLLEADOUT 0 /* 0 disables, 1 enables */
22 * Copyright: GNU Public License 2 applies
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2, or (at your option)
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program; if not, write to the Free Software
36 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
40 * parts (C) Peter Widow
41 * parts (C) Thomas Niederreiter
42 * parts (C) RSA Data Security, Inc.
45 * 18.12.93 - first version, OK
46 * 01.01.94 - generalized & clean up HE
47 * 10.06.94 - first linux version HE
48 * 12.06.94 - wav header alignment problem fixed HE
49 * 12.08.94 - open the cdrom device O_RDONLY makes more sense :-)
50 * no more floating point math
51 * change to sector size 2352 which is more common
52 * sub-q-channel information per kernel ioctl requested
53 * doesn't work as well as before
54 * some new options (-max -i)
55 * 01.02.95 - async i/o via semaphores and shared memory
56 * 03.02.95 - overlapped reading on sectors
57 * 03.02.95 - generalized sample rates. all integral divisors are legal
58 * 04.02.95 - sun format added
59 * more divisors: all integral halves >= 1 allowed
60 * floating point math needed again
61 * 06.02.95 - bugfix for last track and not d0
62 * tested with photo-cd with audio tracks
64 * 29.01.96 - new options for bulk transfer
65 * 01.06.96 - tested with enhanced cd
66 * 01.06.96 - tested with cd-plus
67 * 02.06.96 - support pipes
68 * 02.06.96 - support raw format
69 * 04.02.96 - security hole fixed
70 * 22.04.97 - large parts rewritten
71 * 28.04.97 - make file names DOS compatible
72 * 01.09.97 - add speed control
73 * 20.10.97 - add find mono option
74 * Jan/Feb 98 - conversion to use Joerg Schillings SCSI library
90 #if defined (HAVE_LIMITS_H)
93 #if defined (HAVE_SYS_IOCTL_H)
94 #include <sys/ioctl.h>
99 #if defined (HAVE_SETPRIORITY)
100 #include <sys/resource.h>
104 #include <usal/scsitransp.h>
107 #include <be/kernel/OS.h>
111 #include "sndconfig.h"
113 #include "semshm.h" /* semaphore functions */
115 #include "wav.h" /* wav file header structures */
116 #include "sun.h" /* sun audio file header structures */
117 #include "raw.h" /* raw file handling */
118 #include "aiff.h" /* aiff file handling */
119 #include "aifc.h" /* aifc file handling */
121 #include "mp3.h" /* mp3 file handling */
123 #include "interface.h" /* low level cdrom interfacing */
125 #include "resample.h"
128 #include "ringbuff.h"
130 #include "exitcodes.h"
132 #include "cdda_paranoia.h"
134 #include "defaults.h"
136 static void RestrictPlaybackRate(long newrate);
137 static void output_indices(FILE *fp, index_list *p, unsigned trackstart);
138 static int write_info_file(char *fname_baseval, unsigned int track,
139 unsigned long SamplesDone, int numbered);
140 static void CloseAudio(int channels_val, unsigned long nSamples,
141 struct soundfile *audio_out);
142 static void CloseAll(void);
143 static void OpenAudio(char *fname, double rate, long nBitsPerSample,
144 long channels_val, unsigned long expected_bytes,
145 struct soundfile*audio_out);
146 static void set_offset(myringbuff *p, int offset);
147 static int get_offset(myringbuff *p);
148 static void usage(void);
149 static void init_globals(void);
150 static int is_fifo(char *filename);
154 * unique parameterless options first,
155 * unique parametrized option names next,
156 * ambigious parameterless option names next,
157 * ambigious string parametrized option names last
159 static const char *opts = "paranoia,paraopts&,version,help,h,\
160 no-write,N,dump-rates,R,bulk,B,alltracks,verbose-scsi+,V+,\
161 find-extremes,F,find-mono,G,no-infofile,H,\
162 deemphasize,T,info-only,J,silent-scsi,Q,\
163 cddbp-server*,cddbp-port*,\
164 scanbus,devices,device*,dev*,D*,auxdevice*,A*,interface*,I*,output-format*,O*,\
165 output-endianess*,E*,cdrom-endianess*,C*,speed#,S#,\
166 playback-realtime#L,p#L,md5#,M#,set-overlap#,P#,sound-device*,K*,\
167 cddb#,L#,channels*,c*,bits-per-sample#,b#,rate#,r#,gui,g,\
168 divider*,a*,track*,t*,index#,i#,duration*,d*,offset#,o#,\
169 sectors-per-request#,n#,verbose-level&,v&,buffers-in-ring#,l#,\
170 stereo,s,mono,m,wait,w,echo,e,quiet,q,max,x\
175 #include <io.h> /* for setmode() prototype */
178 /* global variables */
181 /* static variables */
182 static unsigned long nSamplesDone = 0;
184 static int child_pid = -2;
186 static unsigned long *nSamplesToDo;
187 static unsigned int current_track;
190 unsigned int get_current_track(void);
192 unsigned int get_current_track()
194 return current_track;
197 static void RestrictPlaybackRate(long newrate)
199 global.playback_rate = newrate;
201 if ( global.playback_rate < 25 ) global.playback_rate = 25; /* filter out insane values */
202 if ( global.playback_rate > 250 ) global.playback_rate = 250;
204 if ( global.playback_rate < 100 )
205 global.nsectors = (global.nsectors*global.playback_rate)/100;
209 long SamplesNeeded(long amount, long undersampling_val)
211 long retval = ((undersampling_val * 2 + Halved)*amount)/2;
212 if (Halved && (*nSamplesToDo & 1))
221 static void reset_name_iterator(void);
222 static void reset_name_iterator()
224 argv2 -= argc3 - argc2;
228 static char *get_next_name(void);
229 static char *get_next_name()
239 static char *cut_extension(char *fname);
241 static char *cut_extension(char *fname)
245 pp = strrchr(fname, '.');
248 pp = fname + strlen(fname);
256 static void output_indices(FILE *fp, index_list *p, unsigned trackstart)
260 fprintf(fp, "Index=\t\t");
267 for (ci = 1; p != NULL; ci++, p = p->next) {
268 int frameoff = p->frameoffset;
271 fputs("\nIndex0=\t\t", fp);
273 else if ( ci > 8 && (ci % 8) == 1)
274 fputs("\nIndex =\t\t", fp);
277 fprintf(fp, "%d ", frameoff - trackstart);
285 * write information before the start of the sampling process
288 * uglyfied for Joerg Schillings ultra dumb line parser
290 static int write_info_file(char *fname_baseval, unsigned int track,
291 unsigned long int SamplesDone, int numbered)
299 /* write info file */
300 if (!strcmp(fname_baseval,"-")) return 0;
302 strncpy(fname, fname_baseval, sizeof(fname) -1);
303 fname[sizeof(fname) -1] = 0;
305 sprintf(cut_extension(fname), "_%02u.inf", track);
307 strcpy(cut_extension(fname), ".inf");
309 info_fp = fopen (fname, "w");
314 #ifdef MD5_SIGNATURES
315 if (global.md5blocksize)
316 MD5Final (global.MD5_result, &global.context);
320 utc_time = time(NULL);
321 tmptr = localtime(&utc_time);
323 strftime(datetime, sizeof(datetime), "%x %X", tmptr);
325 strncpy(datetime, "unknown", sizeof(datetime));
327 fprintf(info_fp, "#created by icedax %s %s\n#\n", VERSION
331 "CDINDEX_DISCID=\t'%s'\n" , global.cdindex_id);
333 "CDDB_DISCID=\t0x%08lx\n\
337 Albumperformer=\t'%s'\n\
340 , (unsigned long) global.cddb_id
343 , global.creator != NULL ? global.creator : (const unsigned char *)""
344 , global.trackcreator[track] != NULL ? global.trackcreator[track] :
345 (global.creator != NULL ? global.creator : (const unsigned char *)"")
346 , global.disctitle != NULL ? global.disctitle : (const unsigned char *)""
349 "Tracktitle=\t'%s'\n"
350 , global.tracktitle[track] ? global.tracktitle[track] : (const unsigned char *)""
352 fprintf(info_fp, "Tracknumber=\t%u\n"
357 , Get_AudioStartSector(track)
360 "# track length in sectors (1/75 seconds each), rest samples\nTracklength=\t%ld, %d\n"
361 , SamplesDone/588L,(int)(SamplesDone%588));
363 "Pre-emphasis=\t%s\n"
364 , Get_Preemphasis(track) && (global.deemphasize == 0) ? "yes" : "no");
367 , Get_Channels(track) ? 4 : global.channels == 2 ? 2 : 1);
368 { int cr = Get_Copyright(track);
369 fputs("Copy_permitted=\t", info_fp);
372 fputs("once (copyright protected)\n", info_fp);
375 fputs("no (SCMS first copy)\n", info_fp);
378 fputs("yes (not copyright protected)\n", info_fp);
381 fputs("unknown\n", info_fp);
386 , global.need_big_endian ? "big" : "little"
388 fprintf(info_fp, "# index list\n");
389 output_indices(info_fp, global.trackindexlist[track],
390 Get_AudioStartSector(track));
392 /* MD5 checksums in info files are currently broken.
393 * for on-the-fly-recording the generation of info files has been shifted
394 * before the recording starts, so there is no checksum at that point.
396 #ifdef MD5_SIGNATURES
398 "#(blocksize) checksum\nMD-5=\t\t(%d) %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n"
399 , global.md5blocksize
400 , global.MD5_result[0]
401 , global.MD5_result[1]
402 , global.MD5_result[2]
403 , global.MD5_result[3]
404 , global.MD5_result[4]
405 , global.MD5_result[5]
406 , global.MD5_result[6]
407 , global.MD5_result[7]
408 , global.MD5_result[8]
409 , global.MD5_result[9]
410 , global.MD5_result[10]
411 , global.MD5_result[11]
412 , global.MD5_result[12]
413 , global.MD5_result[13]
414 , global.MD5_result[14]
415 , global.MD5_result[15]);
423 static void CloseAudio(int channels_val, unsigned long nSamples,
424 struct soundfile *audio_out)
427 audio_out->ExitSound( global.audio, (nSamples-global.SkippedSamples)*global.OutSampleSize*channels_val );
429 close (global.audio);
433 static unsigned int track = 1;
436 * define size-related entries in audio file header, update and close file */
437 static void CloseAll()
439 WAIT_T chld_return_status;
442 /* terminate child process first */
443 amiparent = child_pid > 0;
445 if (global.iloop > 0) {
450 #if defined HAVE_FORK_AND_SHAREDMEM
451 # ifdef DEBUG_CLEANUP
452 fprintf(stderr, "%s terminating, \n", amiparent ?
453 "Parent (READER)" : "Child (WRITER)");
456 # ifdef DEBUG_CLEANUP
457 fprintf(stderr, "icedax single process terminating, \n");
461 if (amiparent || child_pid < 0) {
462 /* switch to original mode and close device */
463 EnableCdda (get_scsi_p(), 0, 0);
467 /* do general clean up */
469 if (global.audio>=0) {
471 /* finish sample file for this track */
472 CloseAudio(global.channels,
473 global.nSamplesDoneInTrack, global.audio_out);
475 /* finish sample file for this track */
476 CloseAudio(global.channels,
477 (unsigned int) *nSamplesToDo, global.audio_out);
481 /* tell minimum and maximum amplitudes, if required */
482 if (global.findminmax) {
484 "Right channel: minimum amplitude :%d/-32768, maximum amplitude :%d/32767\n",
485 global.minamp[0], global.maxamp[0]);
487 "Left channel: minimum amplitude :%d/-32768, maximum amplitude :%d/32767\n",
488 global.minamp[1], global.maxamp[1]);
491 /* tell mono or stereo recording, if required */
492 if (global.findmono) {
493 fprintf(stderr, "Audio samples are originally %s.\n", global.ismono ? "mono" : "stereo");
496 return; /* end of child or single process */
500 if (global.have_forked == 1) {
502 fprintf(stderr, "Parent wait for child death, \n");
505 /* wait for child to terminate */
506 if (0 > wait(&chld_return_status)) {
509 if (WIFEXITED(chld_return_status)) {
510 if (WEXITSTATUS(chld_return_status)) {
511 fprintf(stderr, "\nW Child exited with %d\n", WEXITSTATUS(chld_return_status));
514 if (WIFSIGNALED(chld_return_status)) {
515 fprintf(stderr, "\nW Child exited due to signal %d\n", WTERMSIG(chld_return_status));
517 if (WIFSTOPPED(chld_return_status)) {
518 fprintf(stderr, "\nW Child is stopped due to signal %d\n", WSTOPSIG(chld_return_status));
523 fprintf(stderr, "\nW Parent child death, state:%d\n", chld_return_status);
528 rename("gmon.out", "gmon.child");
533 /* report a usage error and exit */
535 static void usage2 (const char *szMessage, ...)
537 static void usage2(const char *szMessage, va_dcl va_alist)
543 va_start(marker, szMessage);
548 vfprintf(stderr, szMessage, marker);
551 fprintf(stderr, "\nPlease use -help or consult the man page for help.\n");
557 /* report a fatal error, clean up and exit */
559 void FatalError (const char *szMessage, ...)
561 void FatalError(const char *szMessage, va_dcl va_alist)
567 va_start(marker, szMessage);
572 vfprintf(stderr, szMessage, marker);
576 if (child_pid >= 0) {
577 if (child_pid == 0) {
580 * Kill the parent too if we are not orphaned.
586 kill(child_pid, SIGINT);
593 /* open the audio output file and prepare the header.
594 * the header will be defined on terminating (when the size
595 * is known). So hitting the interrupt key leaves an intact
598 static void OpenAudio(char *fname, double rate, long nBitsPerSample,
599 long channels_val, unsigned long expected_bytes,
600 struct soundfile *audio_out)
602 if (global.audio == -1) {
604 global.audio = open (fname, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY
605 #ifdef SYNCHRONOUS_WRITE
609 if (global.audio == -1) {
610 if (errno == EAGAIN && is_fifo(fname)) {
611 FatalError ("Could not open fifo %s. Probably no fifo reader present.\n", fname);
613 perror("open audio sample file");
614 FatalError ("Could not open file %s\n", fname);
617 global.SkippedSamples = 0;
619 audio_out->InitSound( global.audio, channels_val, (unsigned long)rate, nBitsPerSample, expected_bytes );
621 #ifdef MD5_SIGNATURES
622 if (global.md5blocksize)
623 MD5Init (&global.context);
624 global.md5count = global.md5blocksize;
628 #include "scsi_cmds.h"
630 static int RealEnd(SCSI *usalp, UINT4 *buff);
632 static int RealEnd(SCSI *usalp, UINT4 *buff)
634 if (usal_cmd_err(usalp) != 0) {
637 k = usal_sense_key(usalp);
638 c = usal_sense_code(usalp);
639 q = usal_sense_qual(usalp);
640 if ((k == 0x05 /* ILLEGAL_REQUEST */ &&
641 c == 0x21 /* lba out of range */ &&
643 (k == 0x05 /* ILLEGAL_REQUEST */ &&
644 c == 0x63 /*end of user area encountered on this track*/ &&
646 (k == 0x08 /* BLANK_CHECK */ &&
647 c == 0x64 /* illegal mode for this track */ &&
653 if (usal_getresid(usalp) > 16) return 1;
657 /* Look into the subchannel data */
658 buff += CD_FRAMESAMPLES;
659 p = (unsigned char *)buff;
660 if (p[0] == 0x21 && p[1] == 0xaa) {
667 static void set_offset(myringbuff *p, int offset)
670 fprintf(stderr, "Write offset %d at %p\n", offset, &p->offset);
676 static int get_offset(myringbuff *p)
679 fprintf(stderr, "Read offset %d from %p\n", p->offset, &p->offset);
688 "usage: icedax [OPTIONS ...] [trackfilenames ...]\n\
690 [-c chans] [-s] [-m] [-b bits] [-r rate] [-a divider] [-S speed] [-x]\n\
691 [-t track[+endtrack]] [-i index] [-o offset] [-d duration] [-F] [-G]\n\
692 [-q] [-w] [-v vopts] [-R] [-P overlap] [-B] [-T] [-C input-endianess]\n\
693 [-e] [-n sectors] [-N] [-J] [-L cddbp-mode] [-H] [-g] [-l buffers] [-D cd-device]\n\
694 [-I interface] [-K sound-device] [-O audiotype] [-E output-endianess]\n\
695 [-A auxdevice] [-paranoia] [-cddbp-server=name] [-cddbp-port=port] [-version]\n", stderr);
697 (-D) dev=device set the cdrom or scsi device (as Bus,Id,Lun).\n\
698 (-A) auxdevice=device set the aux device (typically /dev/cdrom).\n\
699 (-K) sound-device=device set the sound device to use for -e (typically /dev/dsp).\n\
700 (-I) interface=interface specify the interface for cdrom access.\n\
701 (generic_scsi or cooked_ioctl).\n\
702 (-c) channels=channels set 1 for mono, 2 or s for stereo (s: channels swapped).\n\
703 (-s) -stereo select stereo recording.\n\
704 (-m) -mono select mono recording.\n\
705 (-x) -max select maximum quality (stereo/16-bit/44.1 KHz).\n\
706 (-b) bits=bits set bits per sample per channel (8, 12 or 16 bits).\n\
707 (-r) rate=rate set rate in samples per second. -R gives all rates\n\
708 (-a) divider=divider set rate to 44100Hz / divider. -R gives all rates\n\
709 (-R) -dump-rates dump a table with all available sample rates\n\
710 (-S) speed=speedfactor set the cdrom drive to a given speed during reading\n\
711 (-P) set-overlap=sectors set amount of overlap sampling (default is 0)\n\
712 (-n) sectors-per-request=secs read 'sectors' sectors per request.\n\
713 (-l) buffers-in-ring=buffers use a ring buffer with 'buffers' elements.\n\
714 (-t) track=track[+end track] select start track (option. end track).\n\
715 (-i) index=index select start index.\n\
716 (-o) offset=offset start at 'offset' sectors behind start track/index.\n\
717 one sector equivalents 1/75 second.\n\
718 (-O) output-format=audiotype set to wav, au (sun), cdr (raw), aiff or aifc format.\n\
719 (-C) cdrom-endianess=endian set little, big or guess input sample endianess.\n\
720 (-E) output-endianess=endian set little or big output sample endianess.\n\
721 (-d) duration=seconds set recording time in seconds or 0 for whole track.\n\
722 (-w) -wait wait for audio signal, then start recording.\n\
723 (-F) -find-extremes find extrem amplitudes in samples.\n\
724 (-G) -find-mono find if input samples are mono.\n\
725 (-T) -deemphasize undo pre-emphasis in input samples.\n\
726 (-e) -echo echo audio data to sound device (see -K) SOUND_DEV.\n\
727 (-v) verbose-level=optlist controls verbosity (for a list use -vhelp).\n\
728 (-N) -no-write do not create audio sample files.\n\
729 (-J) -info-only give disc information only.\n\
730 (-L) cddb=cddbpmode do cddbp title lookups.\n\
731 resolve multiple entries according to cddbpmode: 0=interactive, 1=first entry\n\
732 (-H) -no-infofile no info file generation.\n\
733 (-g) -gui generate special output suitable for gui frontends.\n\
734 (-Q) -silent-scsi do not print status of erreneous scsi-commands.\n\
735 -scanbus scan the SCSI bus and exit\n\
736 --devices scan for system devices and print with native names\n\
737 (-M) md5=count calculate MD-5 checksum for blocks of 'count' bytes.\n\
738 (-q) -quiet quiet operation, no screen output.\n\
739 (-p) playback-realtime=perc play (echo) audio pitched at perc percent (50%-200%).\n\
740 (-V) -verbose-scsi each option increases verbosity for SCSI commands.\n\
741 (-h) -help show this help screen.\n\
742 (-B) -alltracks, -bulk record each track into a seperate file.\n\
743 -paranoia use the lib paranoia for reading.\n\
744 -paraopts=opts set options for lib paranoia (see -paraopts=help).\n\
745 -cddbp-server=servername set the cddbp server to use for title lookups.\n\
746 -cddbp-port=portnumber set the cddbp port to use for title lookups.\n\
747 -version print version information.\n\
749 Please note: some short options will be phased out soon (disappear)!\n\
751 parameters: (optional) one or more file names or - for standard output.\n\
753 fputs("Version ", stderr);
754 fputs(VERSION, stderr);
756 defaults %s, %d bit, %d.%02d Hz, track 1, no offset, one track,\n",
757 CHANNELS-1?"stereo":"mono", BITS_P_S,
758 44100 / UNDERSAMPLING,
759 (4410000 / UNDERSAMPLING) % 100);
761 type %s '%s', don't wait for signal, not quiet,\n",
762 AUDIOTYPE, FILENAME);
764 use %s, device %s, aux %s\n",
765 DEF_INTERFACE, CD_DEVICE, AUX_DEVICE);
766 exit( SYNTAX_ERROR );
769 static void init_globals()
771 #ifdef HISTORICAL_JUNK
772 global.dev_name = CD_DEVICE; /* device name */
774 global.aux_name = AUX_DEVICE;/* auxiliary cdrom device */
775 strncpy(global.fname_base, FILENAME, sizeof(global.fname_base));/* auxiliary cdrom device */
776 global.have_forked = 0; /* state variable for clean up */
777 global.parent_died = 0; /* state variable for clean up */
778 global.audio = -1; /* audio file desc */
779 global.cooked_fd = -1; /* cdrom file desc */
780 global.no_file = 0; /* flag no_file */
781 global.no_infofile = 0; /* flag no_infofile */
782 global.no_cddbfile = 0; /* flag no_cddbfile */
783 global.quiet = 0; /* flag quiet */
784 global.verbose = SHOW_TOC + SHOW_SUMMARY + SHOW_STARTPOSITIONS + SHOW_TITLES; /* verbose level */
785 global.scsi_silent = 0;
786 global.scsi_verbose = 0; /* SCSI verbose level */
789 global.multiname = 0; /* multiple file names given */
790 global.sh_bits = 0; /* sh_bits: sample bit shift */
791 global.Remainder= 0; /* remainder */
792 global.iloop = 0; /* todo counter */
793 global.SkippedSamples = 0; /* skipped samples */
794 global.OutSampleSize = 0; /* output sample size */
795 global.channels = CHANNELS; /* output sound channels */
796 global.nSamplesDoneInTrack = 0; /* written samples in current track */
797 global.buffers = 4; /* buffers to use */
798 global.nsectors = NSECTORS; /* sectors to read in one request */
799 global.overlap = 1; /* amount of overlapping sectors */
800 global.useroverlap = -1; /* amount of overlapping sectors user override */
801 global.need_hostorder = 0; /* processing needs samples in host endianess */
802 global.in_lendian = -1; /* input endianess from SetupSCSI() */
803 global.outputendianess = NONE; /* user specified output endianess */
804 global.findminmax = 0; /* flag find extrem amplitudes */
806 global.maxamp[0] = INT_MIN; /* maximum amplitude */
807 global.maxamp[1] = INT_MIN; /* maximum amplitude */
808 global.minamp[0] = INT_MAX; /* minimum amplitude */
809 global.minamp[1] = INT_MAX; /* minimum amplitude */
811 global.maxamp[0] = -32768; /* maximum amplitude */
812 global.maxamp[1] = -32768; /* maximum amplitude */
813 global.minamp[0] = 32767; /* minimum amplitude */
814 global.minamp[1] = 32767; /* minimum amplitude */
816 global.speed = DEFAULT_SPEED; /* use default */
817 global.userspeed = -1; /* speed user override */
818 global.findmono = 0; /* flag find if samples are mono */
819 global.ismono = 1; /* flag if samples are mono */
820 global.swapchannels = 0; /* flag if channels shall be swapped */
821 global.deemphasize = 0; /* flag undo pre-emphasis in samples */
822 global.playback_rate = 100; /* new fancy selectable sound output rate */
823 global.gui = 0; /* flag plain formatting for guis */
824 global.cddb_id = 0; /* disc identifying id for CDDB database */
825 global.cddb_revision = 0; /* entry revision for CDDB database */
826 global.cddb_year = 0; /* disc identifying year for CDDB database */
827 global.cddb_genre[0] = '\0'; /* disc identifying genre for CDDB database */
828 global.cddbp = 0; /* flag if titles shall be looked up from CDDBP */
829 global.cddbp_server = 0; /* user supplied CDDBP server */
830 global.cddbp_port = 0; /* user supplied CDDBP port */
831 global.illleadout_cd = 0; /* flag if illegal leadout is present */
832 global.reads_illleadout = 0; /* flag if cdrom drive reads cds with illegal leadouts */
833 global.disctitle = NULL;
834 global.creator = NULL;
835 global.copyright_message = NULL;
836 memset(global.tracktitle, 0, sizeof(global.tracktitle));
837 memset(global.trackindexlist, 0, sizeof(global.trackindexlist));
839 global.just_the_toc = 0;
841 global.paranoia_parms.disable_paranoia =
842 global.paranoia_parms.disable_extra_paranoia =
843 global.paranoia_parms.disable_scratch_detect =
844 global.paranoia_parms.disable_scratch_repair = 0;
845 global.paranoia_parms.retries = 20;
846 global.paranoia_parms.overlap = -1;
847 global.paranoia_parms.mindynoverlap = -1;
848 global.paranoia_parms.maxdynoverlap = -1;
852 #if !defined (HAVE_STRCASECMP) || (HAVE_STRCASECMP != 1)
854 static int strcasecmp(const char *s1, const char *s2);
855 static int strcasecmp(const char *s1, const char *s2)
858 while (*s1 && *s2 && (tolower(*s1) - tolower(*s2) == 0)) {
862 if (*s1 == '\0' && *s2 == '\0') return 0;
863 if (*s1 == '\0') return -1;
864 if (*s2 == '\0') return +1;
865 return tolower(*s1) - tolower(*s2);
871 static int is_fifo(char *filename)
874 struct stat statstruct;
876 if (stat(filename, &statstruct)) {
877 /* maybe the output file does not exist. */
880 else comerr("Error during stat for output file\n");
882 if (S_ISFIFO(statstruct.st_mode)) {
893 #if !defined (HAVE_STRTOUL) || (HAVE_STRTOUL != 1)
894 static unsigned int strtoul(const char *s1, char **s2, int base);
895 static unsigned int strtoul(const char *s1, char **s2, int base)
900 /* strip zeros in front */
905 *s2 = astol(s1, &retval);
907 (void) astol(s1, &retval);
910 return (unsigned long) retval;
914 static unsigned long SectorBurst;
915 #if (SENTINEL > CD_FRAMESIZE_RAW)
916 error block size for overlap check has to be < sector size
921 switch_to_realtime_priority(void);
923 #ifdef HAVE_SYS_PRIOCNTL_H
925 #include <sys/priocntl.h>
926 #include <sys/rtpriocntl.h>
927 static void switch_to_realtime_priority()
938 strcpy(info.pc_clname, "RT");
939 if (-1 == priocntl(P_PID, pid, PC_GETCID, (void *)&info)) {
940 errmsg("Cannot get priority class id priocntl(PC_GETCID)\n");
944 memmove(&rtinfo, info.pc_clinfo, sizeof(rtinfo_t));
946 /* set priority not to the max */
947 rtparam.rt_pri = rtinfo.rt_maxpri - 2;
948 rtparam.rt_tqsecs = 0;
949 rtparam.rt_tqnsecs = RT_TQDEF;
950 param.pc_cid = info.pc_cid;
951 memmove(param.pc_clparms, &rtparam, sizeof(rtparms_t));
954 if (-1 == priocntl(P_PID, pid, PC_SETPARMS, (void *)¶m))
955 errmsg("Cannot set priority class parameters priocntl(PC_SETPARMS)\n");
961 #if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0
962 #define USE_POSIX_PRIORITY_SCHEDULING
964 #ifdef USE_POSIX_PRIORITY_SCHEDULING
967 static void switch_to_realtime_priority()
969 #ifdef _SC_PRIORITY_SCHEDULING
970 if (sysconf(_SC_PRIORITY_SCHEDULING) == -1) {
971 errmsg("WARNING: RR-scheduler not available, disabling.\n");
975 int sched_fifo_min, sched_fifo_max;
976 struct sched_param sched_parms;
978 sched_fifo_min = sched_get_priority_min(SCHED_FIFO);
979 sched_fifo_max = sched_get_priority_max(SCHED_FIFO);
980 sched_parms.sched_priority = sched_fifo_max - 1;
983 if (-1 == sched_setscheduler(getpid(), SCHED_FIFO, &sched_parms)
984 && global.quiet != 1)
985 errmsg("cannot set posix realtime scheduling policy\n");
991 #if defined(__CYGWIN32__)
994 * NOTE: Base.h from Cygwin-B20 has a second typedef for BOOL.
995 * We define BOOL to make all local code use BOOL
996 * from Windows.h and use the hidden __SBOOL for
997 * our global interfaces.
999 * NOTE: windows.h from Cygwin-1.x includes a structure field named sample,
1000 * so me may not define our own 'sample' or need to #undef it now.
1001 * With a few nasty exceptions, Microsoft assumes that any global
1002 * defines or identifiers will begin with an Uppercase letter, so
1003 * there may be more of these problems in the future.
1005 * NOTE: windows.h defines interface as an alias for struct, this
1006 * is used by COM/OLE2, I guess it is class on C++
1007 * We man need to #undef 'interface'
1009 #define BOOL WBOOL /* This is the Win BOOL */
1010 #define format __format /* Avoid format parameter hides global ... */
1011 #include <windows.h>
1015 static void switch_to_realtime_priority()
1017 /* set priority class */
1018 if (FALSE == SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) {
1019 fprintf(stderr, "No realtime priority possible.\n");
1023 /* set thread priority */
1024 if (FALSE == SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
1025 fprintf(stderr, "Could not set realtime priority.\n");
1029 static void switch_to_realtime_priority()
1037 int on_exitscsi(void *status);
1039 int on_exitscsi(void *status)
1041 exit((intptr_t)status);
1045 /* wrapper for signal handler exit needed for Mac-OS-X */
1046 static void exit_wrapper(int status);
1048 static void exit_wrapper(int status)
1050 #if defined DEBUG_CLEANUP
1051 fprintf( stderr, "Exit(%d) for %s\n", status, child_pid == 0 ? "Child" : "Parent");
1055 if (child_pid != 0) {
1056 SCSI *usalp = get_scsi_p();
1057 if (usalp->running) {
1058 usalp->cb_fun = on_exitscsi;
1059 usalp->cb_arg = (void *) (uintptr_t) status;
1061 on_exitscsi((void *) (intptr_t) status);
1068 /* signal handler for process communication */
1069 static void set_nonforked(int status);
1072 static void set_nonforked(int status)
1074 global.parent_died = 1;
1075 #if defined DEBUG_CLEANUP
1076 fprintf( stderr, "SIGPIPE received from %s\n.", child_pid == 0 ? "Child" : "Parent");
1078 if (child_pid == 0) {
1081 * Kill the parent too if we are not orphaned.
1087 kill(child_pid, SIGINT);
1089 exit(SIGPIPE_ERROR);
1095 static struct paranoia_statistics
1099 int last_heartbeatstate;
1108 int rip_smile_level;
1111 unsigned fixup_edges;
1112 unsigned fixup_atoms;
1118 unsigned fixup_droppeds;
1119 unsigned fixup_dupeds;
1123 static void paranoia_reset(void);
1124 static void paranoia_reset()
1126 para_stat->c_sector = 0;
1127 para_stat->v_sector = 0;
1128 para_stat->last_heartbeatstate = 0;
1129 para_stat->lasttime = 0;
1130 para_stat->heartbeat = ' ';
1131 para_stat->minoverlap = 0x7FFFFFFF;
1132 para_stat->curoverlap = 0;
1133 para_stat->maxoverlap = 0;
1134 para_stat->slevel = 0;
1135 para_stat->slastlevel = 0;
1136 para_stat->stimeout = 0;
1137 para_stat->rip_smile_level = 0;
1138 para_stat->verifies = 0;
1139 para_stat->reads = 0;
1140 para_stat->readerrs = 0;
1141 para_stat->fixup_edges = 0;
1142 para_stat->fixup_atoms = 0;
1143 para_stat->fixup_droppeds = 0;
1144 para_stat->fixup_dupeds = 0;
1145 para_stat->drifts = 0;
1146 para_stat->scratchs = 0;
1147 para_stat->overlaps = 0;
1148 para_stat->skips = 0;
1151 static void paranoia_callback(long inpos, int function);
1153 static void paranoia_callback(long inpos, int function)
1155 struct timeval thistime;
1160 para_stat->v_sector = inpos / CD_FRAMEWORDS;
1163 para_stat->last_heartbeatstate = 8;
1164 para_stat->heartbeat = '*';
1165 para_stat->slevel = 0;
1166 para_stat->v_sector = inpos / CD_FRAMEWORDS;
1168 case PARANOIA_CB_VERIFY:
1169 if (para_stat->stimeout >= 30) {
1170 if (para_stat->curoverlap > CD_FRAMEWORDS) {
1171 para_stat->slevel = 2;
1173 para_stat->slevel = 1;
1176 para_stat->verifies++;
1178 case PARANOIA_CB_READ:
1179 if (inpos / CD_FRAMEWORDS > para_stat->c_sector) {
1180 para_stat->c_sector = inpos / CD_FRAMEWORDS;
1184 case PARANOIA_CB_FIXUP_EDGE:
1185 if (para_stat->stimeout >= 5) {
1186 if (para_stat->curoverlap > CD_FRAMEWORDS) {
1187 para_stat->slevel = 2;
1189 para_stat->slevel = 1;
1192 para_stat->fixup_edges++;
1194 case PARANOIA_CB_FIXUP_ATOM:
1195 if (para_stat->slevel < 3 || para_stat->stimeout > 5) {
1196 para_stat->slevel = 3;
1198 para_stat->fixup_atoms++;
1200 case PARANOIA_CB_READERR:
1201 para_stat->slevel = 6;
1202 para_stat->readerrs++;
1204 case PARANOIA_CB_SKIP:
1205 para_stat->slevel = 8;
1208 case PARANOIA_CB_OVERLAP:
1209 para_stat->curoverlap = inpos;
1210 if (inpos > para_stat->maxoverlap)
1211 para_stat->maxoverlap = inpos;
1212 if (inpos < para_stat->minoverlap)
1213 para_stat->minoverlap = inpos;
1214 para_stat->overlaps++;
1216 case PARANOIA_CB_SCRATCH:
1217 para_stat->slevel = 7;
1218 para_stat->scratchs++;
1220 case PARANOIA_CB_DRIFT:
1221 if (para_stat->slevel < 4 || para_stat->stimeout > 5) {
1222 para_stat->slevel = 4;
1224 para_stat->drifts++;
1226 case PARANOIA_CB_FIXUP_DROPPED:
1227 para_stat->slevel = 5;
1228 para_stat->fixup_droppeds++;
1230 case PARANOIA_CB_FIXUP_DUPED:
1231 para_stat->slevel = 5;
1232 para_stat->fixup_dupeds++;
1236 gettimeofday(&thistime, NULL);
1237 /* now in tenth of seconds. */
1238 test = thistime.tv_sec * 10 + thistime.tv_usec / 100000;
1240 if (para_stat->lasttime != test
1242 || para_stat->slastlevel != para_stat->slevel) {
1245 || para_stat->slastlevel != para_stat->slevel) {
1247 static const char hstates[] = " .o0O0o.";
1249 para_stat->lasttime = test;
1250 para_stat->stimeout++;
1252 para_stat->last_heartbeatstate++;
1253 if (para_stat->last_heartbeatstate > 7) {
1254 para_stat->last_heartbeatstate = 0;
1256 para_stat->heartbeat = hstates[para_stat->last_heartbeatstate];
1258 if (function == -1) {
1259 para_stat->heartbeat = '*';
1263 if (para_stat->slastlevel != para_stat->slevel) {
1264 para_stat->stimeout = 0;
1266 para_stat->slastlevel = para_stat->slevel;
1269 if (para_stat->slevel < 8) {
1270 para_stat->rip_smile_level = para_stat->slevel;
1272 para_stat->rip_smile_level = 0;
1277 static long lSector;
1278 static long lSector_p2;
1279 static double rate = 44100.0 / UNDERSAMPLING;
1280 static int bits = BITS_P_S;
1281 static char fname[200];
1282 static const char *audio_type;
1283 static long BeginAtSample;
1284 static unsigned long SamplesToWrite;
1285 static unsigned minover;
1286 static unsigned maxover;
1288 static unsigned long calc_SectorBurst(void);
1289 static unsigned long calc_SectorBurst()
1291 unsigned long SectorBurstVal;
1293 SectorBurstVal = min(global.nsectors,
1294 (global.iloop + CD_FRAMESAMPLES-1) / CD_FRAMESAMPLES);
1295 if ( lSector+(int)SectorBurst-1 >= lSector_p2 )
1296 SectorBurstVal = lSector_p2 - lSector;
1297 return SectorBurstVal;
1300 /* if PERCENTAGE_PER_TRACK is defined, the percentage message will reach
1301 * 100% every time a track end is reached or the time limit is reached.
1303 * Otherwise if PERCENTAGE_PER_TRACK is not defined, the percentage message
1304 * will reach 100% once at the very end of the last track.
1306 #define PERCENTAGE_PER_TRACK
1308 static int do_read(myringbuff *p, unsigned *total_unsuccessful_retries);
1309 static int do_read(myringbuff *p, unsigned *total_unsuccessful_retries)
1311 unsigned char *newbuf;
1313 unsigned int added_size;
1315 /* how many sectors should be read */
1316 SectorBurst = calc_SectorBurst();
1319 if (global.paranoia_selected) {
1322 for (i = 0; i < SectorBurst; i++) {
1325 dp = paranoia_read_limited(global.cdp, paranoia_callback,
1326 global.paranoia_parms.retries);
1331 err = cdda_errors(global.cdp);
1332 msg = cdda_messages(global.cdp);
1344 memcpy(p->data + i*CD_FRAMESAMPLES, dp,
1347 fputs("E unrecoverable error!", stderr);
1351 newbuf = (unsigned char *)p->data;
1353 set_offset(p,offset);
1354 added_size = SectorBurst * CD_FRAMESAMPLES;
1356 handle_inputendianess(p->data, added_size);
1360 unsigned int retry_count;
1361 #define MAX_READRETRY 12
1365 SCSI *usalp = get_scsi_p();
1368 fprintf(stderr, "reading from %lu to %lu, overlap %u\n", lSector, lSector + SectorBurst -1, global.overlap);
1371 #ifdef DEBUG_BUFFER_ADDRESSES
1372 fprintf(stderr, "%p %l\n", p->data, global.pagesize);
1373 if (((unsigned)p->data) & (global.pagesize -1) != 0) {
1374 fprintf(stderr, "Address %p is NOT page aligned!!\n", p->data);
1378 if (global.reads_illleadout != 0 && lSector > Get_StartSector(LastTrack())) {
1380 UINT4 bufferSub[CD_FRAMESAMPLES + 24];
1382 /* we switch to single sector reads,
1383 * in order to handle the remaining sectors. */
1386 ReadCdRomSub( usalp, bufferSub, lSector+singles, 1 );
1387 *eorecording = RealEnd( usalp, bufferSub );
1391 memcpy(p->data+singles*CD_FRAMESAMPLES, bufferSub, CD_FRAMESIZE_RAW);
1393 } while (singles < SectorBurst);
1396 if ( *eorecording ) {
1397 patch_real_end(lSector+singles);
1398 SectorBurst = singles;
1399 #if DEBUG_ILLLEADOUT
1400 fprintf(stderr, "iloop=%11lu, nSamplesToDo=%11lu, end=%lu -->\n",
1401 global.iloop, *nSamplesToDo, lSector+singles);
1404 *nSamplesToDo -= global.iloop - SectorBurst*CD_FRAMESAMPLES;
1405 global.iloop = SectorBurst*CD_FRAMESAMPLES;
1406 #if DEBUG_ILLLEADOUT
1407 fprintf(stderr, "iloop=%11lu, nSamplesToDo=%11lu\n\n",
1408 global.iloop, *nSamplesToDo);
1413 retval = ReadCdRom( usalp, p->data, lSector, SectorBurst );
1415 handle_inputendianess(p->data, SectorBurst * CD_FRAMESAMPLES);
1417 (newbuf = synchronize( p->data, SectorBurst*CD_FRAMESAMPLES,
1418 *nSamplesToDo-global.iloop ))) {
1419 /* could not synchronize!
1420 * Try to invalidate the cdrom cache.
1421 * Increase overlap setting, if possible.
1423 /*trash_cache(p->data, lSector, SectorBurst);*/
1424 if (global.overlap < global.nsectors - 1) {
1427 SectorBurst = calc_SectorBurst();
1428 #ifdef DEBUG_DYN_OVERLAP
1429 fprintf(stderr, "using increased overlap of %u\n", global.overlap);
1432 lSector += global.overlap - 1;
1434 SectorBurst = calc_SectorBurst();
1438 } while (++retry_count < MAX_READRETRY);
1440 if (retry_count == MAX_READRETRY && newbuf == NULL && global.verbose != 0) {
1441 (*total_unsuccessful_retries)++;
1445 offset = newbuf - ((unsigned char *)p->data);
1447 offset = global.overlap * CD_FRAMESIZE_RAW;
1449 set_offset(p,offset);
1451 /* how much has been added? */
1452 added_size = SectorBurst * CD_FRAMESAMPLES - offset/4;
1454 if (newbuf && *nSamplesToDo != global.iloop) {
1455 minover = min(global.overlap, minover);
1456 maxover = max(global.overlap, maxover);
1459 /* should we reduce the overlap setting ? */
1460 if (offset > CD_FRAMESIZE_RAW && global.overlap > 1) {
1461 #ifdef DEBUG_DYN_OVERLAP
1462 fprintf(stderr, "decreasing overlap from %u to %u (jitter %d)\n", global.overlap, global.overlap-1, offset - (global.overlap)*CD_FRAMESIZE_RAW);
1465 SectorBurst = calc_SectorBurst();
1469 if (global.iloop >= added_size) {
1470 global.iloop -= added_size;
1475 lSector += SectorBurst - global.overlap;
1477 #if defined PERCENTAGE_PER_TRACK && defined HAVE_FORK_AND_SHAREDMEM
1480 while ((as = Get_StartSector(current_track+1)) != -1
1491 print_percentage(unsigned *poper, int c_offset);
1493 static void print_percentage(unsigned *poper, int c_offset)
1496 #ifdef PERCENTAGE_PER_TRACK
1497 /* Thomas Niederreiter wants percentage per track */
1498 unsigned start_in_track = max(BeginAtSample,
1499 Get_AudioStartSector(current_track)*CD_FRAMESAMPLES);
1501 per = min(BeginAtSample + (long)*nSamplesToDo,
1502 Get_StartSector(current_track+1)*CD_FRAMESAMPLES)
1503 - (long)start_in_track;
1505 per = (BeginAtSample+nSamplesDone
1510 per = global.iloop ? (nSamplesDone)/(*nSamplesToDo/100) : 100;
1513 if (global.overlap > 0) {
1514 fprintf(stderr, "\r%2d/%2d/%2d/%7d %3d%%",
1515 minover, maxover, global.overlap,
1516 c_offset - global.overlap*CD_FRAMESIZE_RAW,
1518 } else if (*poper != per) {
1519 fprintf(stderr, "\r%3d%%", per);
1525 static unsigned long do_write(myringbuff *p);
1526 static unsigned long do_write(myringbuff *p)
1529 unsigned int InSamples;
1530 static unsigned oper = 200;
1532 current_offset = get_offset(p);
1534 /* how many bytes are available? */
1535 InSamples = global.nsectors*CD_FRAMESAMPLES - current_offset/4;
1536 /* how many samples are wanted? */
1537 InSamples = min((*nSamplesToDo-nSamplesDone),InSamples);
1539 /* when track end is reached, close current file and start a new one */
1540 while ((nSamplesDone < *nSamplesToDo) && (InSamples != 0)) {
1541 long unsigned int how_much = InSamples;
1543 long int left_in_track;
1544 left_in_track = Get_StartSector(current_track+1)*CD_FRAMESAMPLES
1545 - (int)(BeginAtSample+nSamplesDone);
1547 if (*eorecording != 0 && current_track == cdtracks+1 &&
1548 (*total_segments_read) == (*total_segments_written)+1) {
1549 /* limit, if the actual end of the last track is
1550 * not known from the toc. */
1551 left_in_track = InSamples;
1554 if (left_in_track < 0) {
1555 fprintf(stderr, "internal error: negative left_in_track:%ld, current_track=%d\n",left_in_track, current_track);
1559 how_much = min(how_much, (unsigned long) left_in_track);
1562 #ifdef MD5_SIGNATURES
1563 if (global.md5count) {
1564 MD5Update (&global.context, ((unsigned char *)p->data) +current_offset, min(global.md5count,how_much));
1565 global.md5count -= min(global.md5count,how_much);
1568 if ( SaveBuffer ( p->data + current_offset/4,
1571 if (global.have_forked == 1) {
1574 * Kill the parent too if we are not orphaned.
1583 global.nSamplesDoneInTrack += how_much;
1584 SamplesToWrite -= how_much;
1586 /* move residual samples upto buffer start */
1587 if (how_much < InSamples) {
1589 (char *)(p->data) + current_offset,
1590 (char *)(p->data) + current_offset + how_much*4,
1591 (InSamples - how_much) * 4);
1594 if ((unsigned long) left_in_track <= InSamples || SamplesToWrite == 0) {
1595 /* the current portion to be handled is
1596 the end of a track */
1599 /* finish sample file for this track */
1600 CloseAudio(global.channels,
1601 global.nSamplesDoneInTrack, global.audio_out);
1602 } else if (SamplesToWrite == 0) {
1603 /* finish sample file for this track */
1604 CloseAudio(global.channels,
1605 (unsigned int) *nSamplesToDo, global.audio_out);
1608 if (global.verbose) {
1612 print_percentage(&oper, current_offset);
1614 #ifndef THOMAS_SCHAU_MAL
1615 if ((unsigned long)left_in_track > InSamples) {
1616 fputs(" incomplete", stderr);
1619 if (global.tracktitle[current_track] != NULL) {
1621 " track %2u '%s' recorded",
1623 global.tracktitle[current_track]);
1626 " track %2u recorded",
1630 oper = para_stat->readerrs + para_stat->skips +
1631 para_stat->fixup_edges + para_stat->fixup_atoms +
1632 para_stat->fixup_droppeds + para_stat->fixup_dupeds +
1634 f = (100.0 * oper) / (((double)global.nSamplesDoneInTrack)/588.0);
1636 if (para_stat->readerrs) {
1637 fprintf(stderr, " with audible hard errors");
1638 } else if ((para_stat->skips) > 0) {
1639 fprintf(stderr, " with %sretry/skip errors",
1640 f < 2.0 ? "":"audible ");
1641 } else if (oper > 0) {
1644 fprintf(stderr, " with ");
1646 fprintf(stderr, "minor");
1648 fprintf(stderr, "medium");
1650 fprintf(stderr, "noticable audible");
1651 else if (oper < 100)
1652 fprintf(stderr, "major audible");
1654 fprintf(stderr, "extreme audible");
1655 fprintf(stderr, " problems");
1657 fprintf(stderr, " successfully");
1660 fprintf(stderr, " (%.1f%% problem sectors)", f);
1662 fprintf(stderr, " successfully");
1665 if (waitforsignal == 1) {
1666 fprintf(stderr, ". %d silent samples omitted", global.SkippedSamples);
1668 fputs("\n", stderr);
1670 if (global.reads_illleadout && *eorecording == 1) {
1671 fprintf(stderr, "Real lead out at: %ld sectors\n",
1672 (*nSamplesToDo+BeginAtSample)/CD_FRAMESAMPLES);
1675 if (global.paranoia_selected) {
1676 oper = 200; /* force new output */
1677 print_percentage(&oper, current_offset);
1678 if (para_stat->minoverlap == 0x7FFFFFFF)
1679 para_stat->minoverlap = 0;
1680 fprintf(stderr, " %u rderr, %u skip, %u atom, %u edge, %u drop, %u dup, %u drift\n"
1681 ,para_stat->readerrs
1683 ,para_stat->fixup_atoms
1684 ,para_stat->fixup_edges
1685 ,para_stat->fixup_droppeds
1686 ,para_stat->fixup_dupeds
1687 ,para_stat->drifts);
1688 oper = 200; /* force new output */
1689 print_percentage(&oper, current_offset);
1690 fprintf(stderr, " %u overlap(%.4g .. %.4g)\n",
1691 para_stat->overlaps,
1692 (float)para_stat->minoverlap / (2352.0/2.0),
1693 (float)para_stat->maxoverlap / (2352.0/2.0));
1699 global.nSamplesDoneInTrack = 0;
1700 if ( bulk && SamplesToWrite > 0 ) {
1701 if ( !global.no_file ) {
1704 /* build next filename */
1705 tmp_fname = get_next_name();
1706 if (tmp_fname != NULL) {
1707 strncpy(global.fname_base,
1709 sizeof global.fname_base);
1711 sizeof(global.fname_base)-1] =
1715 tmp_fname = cut_extension(global.fname_base);
1716 tmp_fname[0] = '\0';
1718 if (global.multiname == 0) {
1719 sprintf(fname, "%s_%02u.%s",
1724 sprintf(fname, "%s.%s",
1729 OpenAudio( fname, rate, bits, global.channels,
1730 (Get_AudioStartSector(current_track+1) -
1731 Get_AudioStartSector(current_track))
1734 } /* global.nofile */
1735 } /* if ( bulk && SamplesToWrite > 0 ) */
1738 } /* left_in_track <= InSamples */
1739 InSamples -= how_much;
1742 if (!global.quiet && *nSamplesToDo != nSamplesDone) {
1743 print_percentage(&oper, current_offset);
1745 return nSamplesDone;
1748 #define PRINT_OVERLAP_INIT \
1749 if (global.verbose) { \
1750 if (global.overlap > 0) \
1751 fprintf(stderr, "overlap:min/max/cur, jitter, percent_done:\n / / / 0%%"); \
1753 fputs("percent_done:\n 0%", stderr); \
1756 #if defined HAVE_FORK_AND_SHAREDMEM
1757 static void forked_read(void);
1759 /* This function does all audio cdrom reads
1760 * until there is nothing more to do
1762 static void forked_read()
1764 unsigned total_unsuccessful_retries = 0;
1766 #if !defined(HAVE_SEMGET) || !defined(USE_SEMAPHORES)
1770 minover = global.nsectors;
1773 while (global.iloop) {
1775 do_read(get_next_buffer(), &total_unsuccessful_retries);
1779 } /* while (global.iloop) */
1781 if (total_unsuccessful_retries) {
1782 fprintf(stderr,"%u unsuccessful matches while reading\n",total_unsuccessful_retries);
1786 static void forked_write(void);
1788 static void forked_write()
1791 /* don't need these anymore. Good security policy says we get rid
1797 #if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)
1802 for (;nSamplesDone < *nSamplesToDo;) {
1803 if (*eorecording == 1 && (*total_segments_read) == (*total_segments_written)) break;
1805 /* get oldest buffers */
1807 nSamplesDone = do_write(get_oldest_buffer());
1816 /* This function implements the read and write calls in one loop (in case
1817 * there is no fork/thread_create system call).
1818 * This means reads and writes have to wait for each other to complete.
1820 static void nonforked_loop(void);
1822 static void nonforked_loop()
1824 unsigned total_unsuccessful_retries = 0;
1826 minover = global.nsectors;
1829 while (global.iloop) {
1831 do_read(get_next_buffer(), &total_unsuccessful_retries);
1833 do_write(get_oldest_buffer());
1837 if (total_unsuccessful_retries) {
1838 fprintf(stderr,"%u unsuccessful matches while reading\n",total_unsuccessful_retries);
1843 void verbose_usage(void);
1845 void verbose_usage()
1848 help lists all verbose options.\n\
1849 disable disables verbose mode.\n\
1850 all enables all verbose options.\n\
1851 toc display the table of contents.\n\
1852 summary display a summary of track parameters.\n\
1853 indices retrieve/display index positions.\n\
1854 catalog retrieve/display media catalog number.\n\
1855 trackid retrieve/display international standard recording code.\n\
1856 sectors display the start sectors of each track.\n\
1857 titles display any known track titles.\n\
1862 void paranoia_usage(void);
1864 void paranoia_usage()
1867 help lists all paranoia options.\n\
1868 disable disables paranoia mode. Paranoia is still being used.\n\
1869 no-verify switches verify off, and overlap on.\n\
1870 retries=amount set the number of maximum retries per sector.\n\
1871 overlap=amount set the number of sectors used for statical paranoia overlap.\n\
1872 minoverlap=amt set the min. number of sectors used for dynamic paranoia overlap.\n\
1873 maxoverlap=amt set the max. number of sectors used for dynamic paranoia overlap.\n\
1879 handle_verbose_opts(char *optstr, long *flagp);
1881 int handle_verbose_opts(char *optstr, long *flagp)
1889 if ((ep = strchr(optstr, ',')) != NULL) {
1890 optlen = ep - optstr;
1893 optlen = strlen(optstr);
1894 np = optstr + optlen;
1896 if (strncmp(optstr, "toc", optlen) == 0) {
1899 else if (strncmp(optstr, "summary", optlen) == 0) {
1900 *flagp |= SHOW_SUMMARY;
1902 else if (strncmp(optstr, "indices", optlen) == 0) {
1903 *flagp |= SHOW_INDICES;
1905 else if (strncmp(optstr, "catalog", optlen) == 0) {
1908 else if (strncmp(optstr, "trackid", optlen) == 0) {
1909 *flagp |= SHOW_ISRC;
1911 else if (strncmp(optstr, "sectors", optlen) == 0) {
1912 *flagp |= SHOW_STARTPOSITIONS;
1914 else if (strncmp(optstr, "titles", optlen) == 0) {
1915 *flagp |= SHOW_TITLES;
1917 else if (strncmp(optstr, "all", optlen) == 0) {
1920 else if (strncmp(optstr, "disable", optlen) == 0) {
1923 else if (strncmp(optstr, "help", optlen) == 0) {
1929 unsigned arg = strtoul(optstr, &endptr, 10);
1930 if (optstr != endptr
1931 && arg <= SHOW_MAX) {
1933 fprintf(stderr, "Warning: numerical parameters for -v are no more supported in the next releases!\n");
1936 fprintf(stderr, "unknown option %s\n", optstr);
1948 handle_paranoia_opts(char *optstr, long *flagp);
1950 int handle_paranoia_opts(char *optstr, long *flagp)
1958 if ((ep = strchr(optstr, ',')) != NULL) {
1959 optlen = ep - optstr;
1962 optlen = strlen(optstr);
1963 np = optstr + optlen;
1965 if (strncmp(optstr, "retries=", min(8,optlen)) == 0) {
1966 char *eqp = strchr(optstr, '=');
1969 astoi(eqp+1, &rets);
1971 global.paranoia_parms.retries = rets;
1974 else if (strncmp(optstr, "overlap=", min(8, optlen)) == 0) {
1975 char *eqp = strchr(optstr, '=');
1978 astoi(eqp+1, &rets);
1980 global.paranoia_parms.overlap = rets;
1983 else if (strncmp(optstr, "minoverlap=", min(11, optlen)) == 0) {
1984 char *eqp = strchr(optstr, '=');
1987 astoi(eqp+1, &rets);
1989 global.paranoia_parms.mindynoverlap = rets;
1992 else if (strncmp(optstr, "maxoverlap=", min(11, optlen)) == 0) {
1993 char *eqp = strchr(optstr, '=');
1996 astoi(eqp+1, &rets);
1998 global.paranoia_parms.maxdynoverlap = rets;
2001 else if (strncmp(optstr, "no-verify", optlen) == 0) {
2002 global.paranoia_parms.disable_extra_paranoia = 1;
2004 else if (strncmp(optstr, "disable", optlen) == 0) {
2005 global.paranoia_parms.disable_paranoia = 1;
2007 else if (strncmp(optstr, "help", optlen) == 0) {
2012 fprintf(stderr, "unknown option %s\n", optstr);
2020 fputs("lib paranoia support is not configured!\n", stderr);
2026 /* and finally: the MAIN program */
2027 int main(int argc, char *argv[])
2030 long sector_offset = 0;
2031 unsigned long endtrack = 1;
2032 double rectime = DURATION;
2035 int littleendian = -1;
2037 static char *user_sound_device = "";
2039 int tracks_included;
2042 int_name = DEF_INTERFACE;
2043 audio_type = AUDIOTYPE;
2044 save_args(argc, argv);
2046 /* init global variables */
2050 /* When being invoked as list_audio_tracks, just dump a list of
2052 am_i_cdda2wav = !(strlen(argv[0]) >= sizeof("list_audio_tracks")-1
2053 && !strcmp(argv[0]+strlen(argv[0])+1-sizeof("list_audio_tracks"),"list_audio_tracks"));
2054 if (!am_i_cdda2wav) global.verbose = SHOW_JUSTAUDIOTRACKS;
2056 /* Control those set-id privileges... */
2059 env_p = getenv("CDDA_DEVICE");
2060 if (env_p != NULL) {
2061 global.dev_name = env_p;
2064 env_p = getenv("CDDBP_SERVER");
2065 if (env_p != NULL) {
2066 global.cddbp_server = env_p;
2069 env_p = getenv("CDDBP_PORT");
2070 if (env_p != NULL) {
2071 global.cddbp_port = env_p;
2078 BOOL version = FALSE;
2080 char *channels = NULL;
2082 char *divider = NULL;
2083 char *trackspec = NULL;
2084 char *duration = NULL;
2086 char *oendianess = NULL;
2087 char *cendianess = NULL;
2089 BOOL stereo = FALSE;
2092 BOOL dump_rates = FALSE;
2093 int userverbose = -1;
2100 if (getargs(&cac, &cav, opts
2101 , &global.paranoia_selected
2102 , handle_paranoia_opts, ¶opts
2106 , &global.no_file, &global.no_file
2107 , &dump_rates, &dump_rates
2108 , &bulk, &bulk, &bulk
2109 , &global.scsi_verbose, &global.scsi_verbose
2111 , &global.findminmax, &global.findminmax
2112 , &global.findmono, &global.findmono
2113 , &global.no_infofile, &global.no_infofile
2115 , &global.deemphasize, &global.deemphasize
2116 , &global.just_the_toc, &global.just_the_toc
2117 , &global.scsi_silent, &global.scsi_silent
2119 , &global.cddbp_server, &global.cddbp_port
2122 , &global.dev_name, &global.dev_name, &global.dev_name
2123 , &global.aux_name, &global.aux_name
2124 , &int_name, &int_name
2125 , &audio_type, &audio_type
2127 , &oendianess, &oendianess
2128 , &cendianess, &cendianess
2129 , &global.userspeed, &global.userspeed
2131 , &global.playback_rate, &global.playback_rate
2132 , &global.md5blocksize, &global.md5blocksize
2133 , &global.useroverlap, &global.useroverlap
2134 , &user_sound_device, &user_sound_device
2137 , &channels, &channels
2140 , &global.gui, &global.gui
2142 , ÷r, ÷r
2143 , &trackspec, &trackspec
2144 , &cd_index, &cd_index
2145 , &duration, &duration
2146 , §or_offset, §or_offset
2148 , &global.nsectors, &global.nsectors
2149 , handle_verbose_opts, &userverbose
2150 , handle_verbose_opts, &userverbose
2151 , &global.buffers, &global.buffers
2155 , &waitforsignal, &waitforsignal
2156 , &global.echo, &global.echo
2157 , &global.quiet, &global.quiet
2161 errmsgno(EX_BAD, "Bad Option: %s.\n", cav[0]);
2162 fputs ("use 'icedax -help' to get more information.\n", stderr);
2163 exit (SYNTAX_ERROR);
2165 if (getfiles(&cac, &cav, opts) == 0)
2166 /* No more file type arguments */;
2167 moreargs = cav - argv;
2169 fprintf(stderr, "icedax " CDRKIT_VERSION "\n");
2175 if (!global.scanbus)
2176 cdr_defaults(&global.dev_name, NULL, NULL, NULL);
2177 if (dump_rates) { /* list available rates */
2181 Available rates are:\n\
2182 Rate Divider Rate Divider Rate Divider Rate Divider\n\
2184 for (ii = 1; ii <= 44100 / 880 / 2; ii++) {
2186 fprintf(stderr, "%7.1f %2ld %7.1f %2ld.5 ",
2187 44100.0/i2, i2, 44100.0/(i2+0.5), i2);
2189 fprintf(stderr, "%7.1f %2ld %7.1f %2ld.5\n",
2190 44100.0/i2, i2, 44100.0/(i2+0.5), i2);
2196 if (*channels == 's') {
2197 global.channels = 2;
2198 global.swapchannels = 1;
2200 global.channels = strtol(channels, NULL, 10);
2208 divider_d = strtod(divider , NULL);
2209 if (divider_d > 0.0) {
2210 rate = 44100.0 / divider_d;
2212 fputs("E option -divider requires a nonzero, positive argument.\nSee -dump-rates.", stderr);
2219 track = strtoul(trackspec, &endptr, 10 );
2220 endtrack = strtoul(endptr, &endptr2, 10 );
2221 if (endptr2 == endptr) {
2223 } else if (track == endtrack) {
2228 char *end_ptr = NULL;
2229 rectime = strtod(duration, &end_ptr );
2230 if (*end_ptr == 'f') {
2231 rectime = rectime / 75.0;
2232 /* TODO: add an absolute end of recording. */
2234 } else if (*end_ptr == 'F') {
2235 rectime = rectime / 75.0;
2237 } else if (*end_ptr != '\0') {
2242 if (strcasecmp(oendianess, "little") == 0) {
2243 global.outputendianess = LITTLE;
2244 } else if (strcasecmp(oendianess, "big") == 0) {
2245 global.outputendianess = BIG;
2247 usage2("wrong parameter '%s' for option -E", oendianess);
2251 if (strcasecmp(cendianess, "little") == 0) {
2253 } else if (strcasecmp(cendianess, "big") == 0) {
2255 } else if (strcasecmp(cendianess, "guess") == 0) {
2258 usage2("wrong parameter '%s' for option -C", cendianess);
2262 global.cddbp = 1 + cddbp;
2265 global.channels = 2;
2268 global.channels = 1;
2269 global.need_hostorder = 1;
2272 #ifdef ECHO_TO_SOUNDCARD
2273 if (global.playback_rate != 100) {
2274 RestrictPlaybackRate( global.playback_rate );
2276 global.need_hostorder = 1;
2278 fprintf(stderr, "There is no sound support compiled into %s.\n",argv[0]);
2286 global.channels = 2; bits = 16; rate = 44100;
2288 if (global.findminmax) {
2289 global.need_hostorder = 1;
2291 if (global.deemphasize) {
2292 global.need_hostorder = 1;
2294 if (global.just_the_toc) {
2295 global.verbose = SHOW_MAX;
2299 #ifdef Thomas_will_es
2301 global.no_infofile = 1;
2302 global.verbose = SHOW_MAX;
2304 global.no_cddbfile = 1;
2306 if (global.no_file) {
2307 global.no_infofile = 1;
2308 global.no_cddbfile = 1;
2310 if (global.no_infofile) {
2311 global.no_cddbfile = 1;
2313 if (global.md5blocksize) {
2314 #ifdef MD5_SIGNATURES
2315 fputs("MD5 signatures are currently broken! Sorry\n", stderr);
2317 fputs("MD5 signatures are currently broken! Sorry\n", stderr);
2320 if (user_sound_device) {
2321 #ifndef ECHO_TO_SOUNDCARD
2322 fputs("There is no sound support configured!\n", stderr);
2325 if (global.paranoia_selected) {
2326 global.useroverlap = 0;
2328 if (userverbose >= 0) {
2329 global.verbose = userverbose;
2333 /* check all parameters */
2334 if (global.buffers < 1) {
2335 usage2("Incorrect buffer setting: %d", global.buffers);
2338 if (global.nsectors < 1) {
2339 usage2("Incorrect nsectors setting: %d", global.nsectors);
2342 if (global.verbose < 0 || global.verbose > SHOW_MAX) {
2343 usage2("Incorrect verbose level setting: %d",global.verbose);
2345 if (global.verbose == 0) global.quiet = 1;
2347 if ( rectime < 0.0 ) {
2348 usage2("Incorrect recording time setting: %d.%02d",
2349 (int)rectime, (int)(rectime*100+0.5) % 100);
2352 if ( global.channels != 1 && global.channels != 2 ) {
2353 usage2("Incorrect channel setting: %d",global.channels);
2356 if ( bits != 8 && bits != 12 && bits != 16 ) {
2357 usage2("Incorrect bits_per_sample setting: %d",bits);
2360 if ( rate < 827.0 || rate > 44100.0 ) {
2361 usage2("Incorrect sample rate setting: %d.%02d",
2362 (int)rate, ((int)rate*100) % 100);
2365 int_part = (double)(long) (2*44100.0 / rate);
2367 if (2*44100.0 / rate - int_part >= 0.5 ) {
2369 fprintf( stderr, "Nearest available sample rate is %d.%02d Hertz\n",
2370 2*44100 / (int)int_part,
2371 (2*4410000 / (int)int_part) % 100);
2373 Halved = ((int) int_part) & 1;
2374 rate = 2*44100.0 / int_part;
2375 undersampling = (int) int_part / 2.0;
2376 samples_to_do = undersampling;
2378 if (!strcmp((char *)int_name,"generic_scsi"))
2379 interface = GENERIC_SCSI;
2380 else if (!strcmp((char *)int_name,"cooked_ioctl"))
2381 interface = COOKED_IOCTL;
2383 usage2("Incorrect interface setting: %s",int_name);
2386 /* check * init audio file */
2387 if (!strncmp(audio_type,"wav",3)) {
2388 global.audio_out = &wavsound;
2389 } else if (!strncmp(audio_type, "sun", 3) || !strncmp(audio_type, "au", 2)) {
2390 /* Enhanced compatibility */
2392 global.audio_out = &sunsound;
2393 } else if (!strncmp(audio_type, "cdr", 3) ||
2394 !strncmp(audio_type, "raw", 3)) {
2395 global.audio_out = &rawsound;
2396 } else if (!strncmp(audio_type, "aiff", 4)) {
2397 global.audio_out = &aiffsound;
2398 } else if (!strncmp(audio_type, "aifc", 4)) {
2399 global.audio_out = &aifcsound;
2401 } else if (!strncmp(audio_type, "mp3", 3)) {
2402 global.audio_out = &mp3sound;
2403 if (!global.quiet) {
2404 unsigned char Lame_version[20];
2406 fetch_lame_version(Lame_version);
2407 fprintf(stderr, "Using LAME version %s.\n", Lame_version);
2411 fprintf(stderr, "Warning: sample size forced to 16 bit for MP3 format.\n");
2413 #endif /* USE_LAME */
2415 usage2("Incorrect audio type setting: %3s", audio_type);
2418 if (bulk == -1) bulk = 0;
2420 global.need_big_endian = global.audio_out->need_big_endian;
2421 if (global.outputendianess != NONE)
2422 global.need_big_endian = global.outputendianess == BIG;
2424 if (global.no_file) global.fname_base[0] = '\0';
2427 strcat(global.fname_base, ".");
2428 strcat(global.fname_base, audio_type);
2431 /* If we need to calculate with samples or write them to a soundcard,
2432 * we need a conversion to host byte order.
2434 if (global.channels != 2
2437 global.need_hostorder = 1;
2440 * Remove for release 2.0
2441 * this is a bug compatibility feature.
2443 if (global.gui && global.verbose == SHOW_TOC)
2444 global.verbose |= SHOW_STARTPOSITIONS | SHOW_SUMMARY | SHOW_TITLES;
2447 * all options processed.
2448 * Now a file name per track may follow
2450 argc2 = argc3 = argc - moreargs;
2451 argv2 = argv + moreargs;
2452 if ( moreargs < argc ) {
2453 if (!strcmp(argv[moreargs],"-")) {
2454 #ifdef NEED_O_BINARY
2455 setmode(fileno(stdout), O_BINARY);
2457 global.audio = dup (fileno(stdout));
2458 strncpy( global.fname_base, "standard_output", sizeof(global.fname_base) );
2459 global.fname_base[sizeof(global.fname_base)-1]=0;
2460 } else if (!is_fifo(argv[moreargs])) {
2461 /* we do have at least one argument */
2462 global.multiname = 1;
2466 #define SETSIGHAND(PROC, SIG, SIGNAME) if (signal(SIG, PROC) == SIG_ERR) \
2467 { fprintf(stderr, "cannot set signal %s handler\n", SIGNAME); exit(SETSIG_ERROR); }
2468 SETSIGHAND(exit_wrapper, SIGINT, "SIGINT")
2469 SETSIGHAND(exit_wrapper, SIGQUIT, "SIGQUIT")
2470 SETSIGHAND(exit_wrapper, SIGTERM, "SIGTERM")
2471 SETSIGHAND(exit_wrapper, SIGHUP, "SIGHUP")
2473 SETSIGHAND(set_nonforked, SIGPIPE, "SIGPIPE")
2475 /* setup interface and open cdrom device */
2476 /* request sychronization facilities and shared memory */
2479 /* use global.useroverlap to set our overlap */
2480 if (global.useroverlap != -1)
2481 global.overlap = global.useroverlap;
2483 /* check for more valid option combinations */
2485 if (global.nsectors < 1+global.overlap) {
2486 fprintf( stderr, "Warning: Setting #nsectors to minimum of %d, due to jitter correction!\n", global.overlap+1);
2487 global.nsectors = global.overlap+1;
2490 if (global.overlap > 0 && global.buffers < 2) {
2491 fprintf( stderr, "Warning: Setting #buffers to minimum of 2, due to jitter correction!\n");
2495 /* Value of 'nsectors' must be defined here */
2499 while (global.shmsize < sizeof (struct paranoia_statistics))
2500 global.shmsize += global.pagesize;
2502 global.shmsize += 10*global.pagesize; /* XXX Der Speicherfehler ist nicht in libparanoia sondern in cdda2wav :-( */
2503 global.shmsize += HEADER_SIZE + ENTRY_SIZE_PAGE_AL * global.buffers;
2505 #if defined (HAVE_FORK_AND_SHAREDMEM)
2507 * The (void *) cast is to avoid a GCC warning like:
2508 * warning: dereferencing type-punned pointer will break strict-aliasing rules
2509 * which does not apply to this code. (void *) introduces a compatible
2510 * intermediate type in the cast list.
2512 he_fill_buffer = request_shm_sem(global.shmsize, (unsigned char **)(void *)&he_fill_buffer);
2513 if (he_fill_buffer == NULL) {
2514 fprintf( stderr, "no shared memory available!\n");
2517 #else /* do not have fork() and shared memory */
2518 he_fill_buffer = malloc(global.shmsize);
2519 if (he_fill_buffer == NULL) {
2520 fprintf( stderr, "no buffer memory available!\n");
2528 para_stat = (struct paranoia_statistics *)he_fill_buffer;
2529 while (i < sizeof (struct paranoia_statistics)) {
2530 i += global.pagesize;
2531 he_fill_buffer += global.pagesize;
2532 global.shmsize -= global.pagesize;
2537 if (global.verbose != 0)
2539 "%u bytes buffer memory requested, %d buffers, %d sectors\n",
2540 global.shmsize, global.buffers, global.nsectors);
2542 /* initialize pointers into shared memory segment */
2543 last_buffer = he_fill_buffer + 1;
2544 total_segments_read = (unsigned long *) (last_buffer + 1);
2545 total_segments_written = total_segments_read + 1;
2546 child_waits = (int *) (total_segments_written + 1);
2547 parent_waits = child_waits + 1;
2548 in_lendian = parent_waits + 1;
2549 eorecording = in_lendian + 1;
2550 *total_segments_read = *total_segments_written = 0;
2551 nSamplesToDo = (unsigned long *)(eorecording + 1);
2553 *in_lendian = global.in_lendian;
2555 set_total_buffers(global.buffers, sem_id);
2558 #if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)
2559 atexit ( free_sem );
2563 * set input endian default
2565 if (littleendian != -1)
2566 *in_lendian = littleendian;
2568 /* get table of contents */
2569 cdtracks = ReadToc();
2570 if (cdtracks == 0) {
2571 fprintf(stderr, "No track in table of contents! Aborting...\n");
2582 if (ReadTocText != NULL && FirstAudioTrack () != -1) {
2583 ReadTocText(get_scsi_p());
2586 if ( global.verbose == SHOW_JUSTAUDIOTRACKS ) {
2589 for (z = 0; z < cdtracks; z++)
2590 if (Get_Datatrack(z) == 0)
2591 printf("%02d\t%06ld\n", Get_Tracknumber(z), Get_AudioStartSector(z));
2595 if ( global.verbose != 0 ) {
2596 fputs( "#icedax version ", stderr );
2597 fputs( VERSION, stderr );
2598 #if defined USE_POSIX_PRIORITY_SCHEDULING || defined HAVE_SYS_PRIOCNTL_H
2599 fputs( ", real time sched.", stderr );
2601 #if defined ECHO_TO_SOUNDCARD
2602 fputs( ", soundcard", stderr );
2604 #if defined USE_PARANOIA
2605 fputs( ", libparanoia", stderr );
2607 fputs( " support\n", stderr );
2610 FixupTOC(cdtracks + 1);
2613 if (!global.paranoia_selected) {
2614 fprintf(stderr, "NICE\n");
2615 /* try to get some extra kicks */
2618 #if defined HAVE_SETPRIORITY
2619 setpriority(PRIO_PROCESS, 0, -20);
2621 # if defined(HAVE_NICE) && (HAVE_NICE == 1)
2630 /* switch cdrom to audio mode */
2631 EnableCdda (get_scsi_p(), 1, CD_FRAMESIZE_RAW);
2633 atexit ( CloseAll );
2636 if ( FirstAudioTrack () == -1 ) {
2637 if (no_disguised_audiotracks()) {
2638 FatalError ( "This disk has no audio tracks\n" );
2644 /* check if start track is in range */
2645 if ( track < 1 || track > cdtracks ) {
2646 usage2("Incorrect start track setting: %d",track);
2649 /* check if end track is in range */
2650 if ( endtrack < track || endtrack > cdtracks ) {
2651 usage2("Incorrect end track setting: %ld",endtrack);
2655 lSector = Get_AudioStartSector ( track );
2656 lSector_p1 = Get_EndSector ( track ) + 1;
2658 if ( lSector < 0 ) {
2660 FatalError ( "Track %d not found\n", track );
2662 fprintf(stderr, "Skipping data track %d...\n", track);
2663 if (endtrack == track) endtrack++;
2667 } while (bulk != 0 && track <= cdtracks && lSector < 0);
2669 if ((global.illleadout_cd == 0 || global.reads_illleadout != 0) && cd_index != -1) {
2670 if (global.verbose && !global.quiet) {
2671 global.verbose |= SHOW_INDICES;
2673 sector_offset += ScanIndices( track, cd_index, bulk );
2676 if (global.deemphasize || (global.verbose & SHOW_INDICES)) {
2677 ScanIndices( track, cd_index, bulk );
2681 lSector += sector_offset;
2682 /* check against end sector of track */
2683 if ( lSector >= lSector_p1 ) {
2684 fprintf(stderr, "W Sector offset %lu exceeds track size (ignored)\n", sector_offset );
2685 lSector -= sector_offset;
2688 if ( lSector < 0L ) {
2689 fputs( "Negative start sector! Set to zero.\n", stderr );
2693 lSector_p2 = Get_LastSectorOnCd( track );
2694 if (bulk == 1 && track == endtrack && rectime == 0.0)
2696 if ( rectime == 0.0 ) {
2697 /* set time to track time */
2698 *nSamplesToDo = (lSector_p1 - lSector) * CD_FRAMESAMPLES;
2699 rectime = (lSector_p1 - lSector) / 75.0;
2700 if (CheckTrackrange( track, endtrack) == 1) {
2701 lSector_p2 = Get_EndSector ( endtrack ) + 1;
2703 if (lSector_p2 >= 0) {
2704 rectime = (lSector_p2 - lSector) / 75.0;
2705 *nSamplesToDo = (long)(rectime*44100.0 + 0.5);
2707 fputs( "End track is no valid audio track (ignored)\n", stderr );
2710 fputs( "Track range does not consist of audio tracks only (ignored)\n", stderr );
2713 /* Prepare the maximum recording duration.
2714 * It is defined as the biggest amount of
2715 * adjacent audio sectors beginning with the
2716 * specified track/index/offset. */
2718 if ( rectime > (lSector_p2 - lSector) / 75.0 ) {
2719 rectime = (lSector_p2 - lSector) / 75.0;
2720 lSector_p1 = lSector_p2;
2723 /* calculate # of samples to read */
2724 *nSamplesToDo = (long)(rectime*44100.0 + 0.5);
2727 global.OutSampleSize = (1+bits/12);
2728 if (*nSamplesToDo/undersampling == 0L) {
2729 usage2("Time interval is too short. Choose a duration greater than %d.%02d secs!",
2730 undersampling/44100, (int)(undersampling/44100) % 100);
2732 if ( moreargs < argc ) {
2733 if (!strcmp(argv[moreargs],"-") || is_fifo(argv[moreargs])) {
2738 fprintf(stderr, "W Bulk mode is disabled while outputting to a %spipe\n",
2739 is_fifo(argv[moreargs]) ? "named " : "");
2742 global.no_cddbfile = 1;
2745 if (global.no_infofile == 0) {
2746 global.no_infofile = 1;
2747 if (global.channels == 1 || bits != 16 || rate != 44100) {
2748 fprintf(stderr, "W Sample conversions disable generation of info files!\n");
2749 } else if (waitforsignal == 1) {
2750 fprintf(stderr, "W Option -w 'wait for signal' disables generation of info files!\n");
2751 } else if (sector_offset != 0) {
2752 fprintf(stderr, "W Using an start offset (option -o) disables generation of info files!\n");
2754 !((lSector == Get_AudioStartSector(track)) &&
2755 ((long)(lSector + rectime*75.0 + 0.5) == Get_EndSector(track) + 1))) {
2756 fprintf(stderr, "W Duration is not set for complete tracks (option -d), this disables generation\n of info files!\n");
2758 global.no_infofile = 0;
2762 SamplesToWrite = *nSamplesToDo*2/(int)int_part;
2765 int first = FirstAudioTrack();
2766 tracks_included = Get_Track(
2767 (unsigned) (lSector + *nSamplesToDo/CD_FRAMESAMPLES -1))
2768 - max((int)track,first) +1;
2771 if (global.multiname != 0 && moreargs + tracks_included > argc) {
2772 global.multiname = 0;
2775 if ( !waitforsignal ) {
2778 if (!global.no_infofile) {
2781 for (i = track; i < (int)track + tracks_included; i++) {
2782 unsigned minsec, maxsec;
2785 /* build next filename */
2787 tmp_fname = get_next_name();
2788 if (tmp_fname != NULL)
2789 strncpy( global.fname_base, tmp_fname, sizeof(global.fname_base)-8 );
2790 global.fname_base[sizeof(global.fname_base)-1]=0;
2791 minsec = max(lSector, Get_AudioStartSector(i));
2792 maxsec = min(lSector + rectime*75.0 + 0.5, 1+Get_EndSector(i));
2793 if ((int)minsec == Get_AudioStartSector(i) &&
2794 (int)maxsec == 1+Get_EndSector(i)) {
2795 write_info_file(global.fname_base,i,(maxsec-minsec)*CD_FRAMESAMPLES, bulk && global.multiname == 0);
2798 "Partial length copy for track %d, no info file will be generated for this track!\n", i);
2802 reset_name_iterator();
2808 if (global.just_the_toc) exit(NO_ERROR);
2810 #ifdef ECHO_TO_SOUNDCARD
2811 if (user_sound_device[0] != '\0') {
2812 set_snd_device(user_sound_device);
2814 init_soundcard(rate, bits);
2815 #endif /* ECHO_TO_SOUNDCARD */
2817 if (global.userspeed > -1)
2818 global.speed = global.userspeed;
2820 if (global.speed != 0 && SelectSpeed != NULL) {
2821 SelectSpeed(get_scsi_p(), global.speed);
2824 current_track = track;
2826 if ( !global.no_file ) {
2830 myfname = get_next_name();
2832 if (myfname != NULL) {
2833 strncpy( global.fname_base, myfname, sizeof(global.fname_base)-8 );
2834 global.fname_base[sizeof(global.fname_base)-1]=0;
2838 /* strip audio_type extension */
2840 char *cp = global.fname_base;
2842 cp = strrchr(cp, '.');
2844 cp = global.fname_base + strlen(global.fname_base);
2848 if (bulk && global.multiname == 0) {
2849 sprintf(fname, "%s_%02u.%s",global.fname_base,current_track,audio_type);
2851 sprintf(fname, "%s.%s",global.fname_base,audio_type);
2854 OpenAudio( fname, rate, bits, global.channels,
2855 (unsigned)(SamplesToWrite*global.OutSampleSize*global.channels),
2859 global.Remainder = (75 % global.nsectors)+1;
2861 global.sh_bits = 16 - bits; /* shift counter */
2863 global.iloop = *nSamplesToDo;
2864 if (Halved && (global.iloop&1))
2867 BeginAtSample = lSector * CD_FRAMESAMPLES;
2870 if ( (global.verbose & SHOW_SUMMARY) && !global.just_the_toc &&
2871 (global.reads_illleadout == 0 ||
2872 lSector+*nSamplesToDo/CD_FRAMESAMPLES
2873 <= (unsigned) Get_AudioStartSector(cdtracks-1))) {
2875 fprintf(stderr, "samplefile size will be %lu bytes.\n",
2876 global.audio_out->GetHdrSize() +
2877 global.audio_out->InSizeToOutSize(SamplesToWrite*global.OutSampleSize*global.channels) );
2878 fprintf (stderr, "recording %d.%04d seconds %s with %d bits @ %5d.%01d Hz"
2879 ,(int)rectime , (int)(rectime * 10000) % 10000,
2880 global.channels == 1 ? "mono":"stereo", bits, (int)rate, (int)(rate*10)%10);
2881 if (!global.no_file && *global.fname_base)
2882 fprintf(stderr, " ->'%s'...", global.fname_base );
2883 fputs("\n", stderr);
2887 #if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)
2893 if (global.paranoia_selected) {
2896 global.cdp = paranoia_init(get_scsi_p(), global.nsectors);
2898 if (global.paranoia_parms.overlap >= 0) {
2899 int overlap = global.paranoia_parms.overlap;
2901 if (overlap > global.nsectors - 1)
2902 overlap = global.nsectors - 1;
2903 paranoia_overlapset(global.cdp, overlap);
2906 * Default to a minimum of dynamic overlapping == 0.5 sectors.
2907 * If we don't do this, we get the default from libparanoia
2908 * which is approx. 0.1.
2910 if (global.paranoia_parms.mindynoverlap < 0)
2911 paranoia_dynoverlapset(global.cdp, CD_FRAMEWORDS/2, -1);
2912 paranoia_dynoverlapset(global.cdp,
2913 global.paranoia_parms.mindynoverlap * CD_FRAMEWORDS,
2914 global.paranoia_parms.maxdynoverlap * CD_FRAMEWORDS);
2916 paranoia_mode = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP;
2918 if (global.paranoia_parms.disable_paranoia) {
2919 paranoia_mode = PARANOIA_MODE_DISABLE;
2921 if (global.paranoia_parms.disable_extra_paranoia) {
2922 paranoia_mode |= PARANOIA_MODE_OVERLAP;
2923 paranoia_mode &= ~PARANOIA_MODE_VERIFY;
2925 /* not yet implemented */
2926 if (global.paranoia_parms.disable_scratch_detect) {
2927 paranoia_mode &= ~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR);
2929 /* not yet implemented */
2930 if (global.paranoia_parms.disable_scratch_repair) {
2931 paranoia_mode &= ~PARANOIA_MODE_REPAIR;
2934 paranoia_modeset(global.cdp, paranoia_mode);
2936 fprintf(stderr, "using lib paranoia for reading.\n");
2937 paranoia_seek(global.cdp, lSector, SEEK_SET);
2941 #if defined(HAVE_FORK_AND_SHAREDMEM)
2943 /* Everything is set up. Now fork and let one process read cdda sectors
2944 and let the other one store them in a wav file */
2948 if (child_pid > 0 && global.gui > 0 && global.verbose > 0)
2949 fprintf( stderr, "child pid is %d\n", child_pid);
2951 /*********************** fork **************************************/
2952 if (child_pid == 0) {
2953 /* child WRITER section */
2956 /* Under BeOS a fork() with shared memory does not work as
2957 * it does under System V Rel. 4. The mapping of the child
2958 * works with copy on write semantics, so changes do not propagate
2959 * back and forth. The existing mapping has to be deleted
2960 * and replaced by an clone without copy on write semantics.
2961 * This is done with clone_area(...,B_CLONE_ADDRESS,...).
2962 * Thanks to file support.c from the postgreSQL project.
2966 /* iterate over all mappings to find our shared memory mapping. */
2967 while (get_next_area_info(0, &cook, &inf) == B_OK)
2969 /* check the name of the mapping. */
2970 if (!strcmp(inf.name, AREA_NAME))
2973 area_id area_parent;
2975 /* kill the cow mapping. */
2976 area_address = inf.address;
2977 if (B_OK != delete_area(inf.area))
2979 fprintf(stderr, "delete_area: no valid area.\n");
2982 /* get the parent mapping. */
2983 area_parent = find_area(inf.name);
2984 if (area_parent == B_NAME_NOT_FOUND)
2986 fprintf(stderr, "find_area: no such area name.\n");
2989 /* clone the parent mapping without cow. */
2990 if (B_OK > clone_area("shm_child", &area_address, B_CLONE_ADDRESS,
2991 B_READ_AREA | B_WRITE_AREA, area_parent))
2993 fprintf(stderr,"clone_area failed\n");
3000 if (DosGetSharedMem(he_fill_buffer, 3)) {
3001 comerr("DosGetSharedMem() failed.\n");
3004 global.have_forked = 1;
3007 DosFreeMem(he_fill_buffer);
3011 exit_wrapper(NO_ERROR);
3013 } else if (child_pid > 0) {
3014 /* parent READER section */
3016 global.have_forked = 1;
3017 switch_to_realtime_priority();
3023 aid = find_area(AREA_NAME);
3025 comerrno(aid, "find_area() failed.\n");
3031 DosFreeMem(he_fill_buffer);
3033 exit_wrapper(NO_ERROR);
3036 perror("fork error.");
3039 /* version without fork */
3041 global.have_forked = 0;
3043 if (!global.paranoia_selected) {
3044 fprintf(stderr, "REAL\n");
3045 switch_to_realtime_priority();
3048 fprintf(stderr, "a nonforking version is running...\n");
3050 exit_wrapper(NO_ERROR);
3054 if (global.paranoia_selected)
3055 paranoia_free(global.cdp);