1 /* cdpar.c -- routines for interacting with the Paranoia library
2  *
3  * Based on main.c from the cdparanoia distribution
4  *  (C) 1998 Monty <xiphmont@mit.edu>
5  *
6  * All changes Copyright 1999-2004 by Mike Oliphant (grip@nostatic.org)
7  *
8  *   http://www.nostatic.org/grip
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23  * USA
24  */
25 
26 #include "grip.h"
27 
28 #ifdef CDPAR
29 
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <getopt.h>
37 #include <errno.h>
38 #include <math.h>
39 #include <sys/time.h>
40 #include <sys/stat.h>
41 #include <glib.h>
42 #include "gain_analysis.h"
43 
44 #include "cdpar.h"
45 #include "common.h"
46 
47 static void PutNum(long num,int f,int endianness,int bytes);
48 static void WriteWav(int f,long bytes);
49 static void CDPCallback(long inpos, int function);
50 static void GainCalc(char *buffer);
51 static long CDPWrite(int outf, char *buffer);
52 
bigendianp(void)53 static inline int bigendianp(void){
54   int test=1;
55   char *hack=(char *)(&test);
56   if(hack[0])return(0);
57   return(1);
58 }
59 
swap16(size16 x)60 static inline size16 swap16(size16 x){
61   return((((unsigned size16)x & 0x00ffU) <<  8) |
62          (((unsigned size16)x & 0xff00U) >>  8));
63 }
64 
65 /* Ugly hack because we can't pass user data to the callback */
66 int *global_rip_smile_level;
67 
PutNum(long num,int f,int endianness,int bytes)68 static void PutNum(long num,int f,int endianness,int bytes)
69 {
70   int i;
71   unsigned char c;
72 
73   if(!endianness)
74     i=0;
75   else
76     i=bytes-1;
77   while(bytes--){
78     c=(num>>(i<<3))&0xff;
79     if(write(f,&c,1)==-1){
80       perror("Could not write to output.");
81       exit(1);
82     }
83     if(endianness)
84       i--;
85     else
86       i++;
87   }
88 }
89 
WriteWav(int f,long bytes)90 static void WriteWav(int f,long bytes)
91 {
92   /* quick and dirty - ignore compiler warnings*/
93 
94 #pragma GCC diagnostic push
95 #pragma GCC diagnostic ignored "-Wunused-result"
96 
97   write(f,"RIFF",4);               /*  0-3 */
98   PutNum(bytes+44-8,f,0,4);        /*  4-7 */
99   write(f,"WAVEfmt ",8);           /*  8-15 */
100   PutNum(16,f,0,4);                /* 16-19 */
101   PutNum(1,f,0,2);                 /* 20-21 */
102   PutNum(2,f,0,2);                 /* 22-23 */
103   PutNum(44100,f,0,4);             /* 24-27 */
104   PutNum(44100*2*2,f,0,4);         /* 28-31 */
105   PutNum(4,f,0,2);                 /* 32-33 */
106   PutNum(16,f,0,2);                /* 34-35 */
107   write(f,"data",4);               /* 36-39 */
108   PutNum(bytes,f,0,4);             /* 40-43 */
109 #pragma GCC diagnostic pop
110 }
111 
CDPCallback(long inpos,int function)112 static void CDPCallback(long inpos,int function)
113 {
114   static long c_sector=0;
115   static int last=0;
116   static long lasttime=0;
117   long sector,osector=0;
118   struct timeval thistime;
119   static int overlap=0;
120   static int slevel=0;
121   static int slast=0;
122   static int stimeout=0;
123   long test;
124 
125   osector=inpos;
126   sector=inpos/CD_FRAMEWORDS;
127 
128   if(function==-2){
129     return;
130   }
131 
132   if(function==-1){
133     last=8;
134     slevel=0;
135   } else
136     switch(function){
137     case PARANOIA_CB_VERIFY:
138       if(stimeout>=30) {
139 	if(overlap>CD_FRAMEWORDS)
140 	  slevel=2;
141 	else
142 	  slevel=1;
143       }
144       break;
145     case PARANOIA_CB_READ:
146       if(sector>c_sector)c_sector=sector;
147       break;
148 
149     case PARANOIA_CB_FIXUP_EDGE:
150       if(stimeout>=5) {
151 	if(overlap>CD_FRAMEWORDS)
152 	  slevel=2;
153 	else
154 	  slevel=1;
155       }
156       break;
157     case PARANOIA_CB_FIXUP_ATOM:
158       if(slevel<3 || stimeout>5)slevel=3;
159       break;
160     case PARANOIA_CB_READERR:
161       slevel=6;
162       break;
163     case PARANOIA_CB_SKIP:
164       slevel=8;
165       break;
166     case PARANOIA_CB_OVERLAP:
167       overlap=osector;
168       break;
169     case PARANOIA_CB_SCRATCH:
170       slevel=7;
171       break;
172     case PARANOIA_CB_DRIFT:
173       if(slevel<4 || stimeout>5)slevel=4;
174       break;
175     case PARANOIA_CB_FIXUP_DROPPED:
176     case PARANOIA_CB_FIXUP_DUPED:
177       slevel=5;
178       break;
179     }
180 
181 
182   gettimeofday(&thistime,NULL);
183   test=thistime.tv_sec*10+thistime.tv_usec/100000;
184 
185   if(lasttime!=test || function==-1 || slast!=slevel){
186     if(lasttime!=test || function==-1){
187       last++;
188       lasttime=test;
189       if(last>7)last=0;
190         stimeout++;
191     }
192     if(slast!=slevel){
193       stimeout=0;
194     }
195     slast=slevel;
196   }
197 
198   if(slevel<8&&slevel>0) *global_rip_smile_level=slevel-1;
199   else *global_rip_smile_level=0;
200 }
201 
202 /* Do the replay gain calculation on a sector */
GainCalc(char * buffer)203 static void GainCalc(char *buffer)
204 {
205   static Float_t l_samples[588];
206   static Float_t r_samples[588];
207   long count;
208   short *data;
209 
210   data=(short *)buffer;
211 
212   for(count=0;count<588;count++) {
213     l_samples[count]=(Float_t)data[count*2];
214     r_samples[count]=(Float_t)data[(count*2)+1];
215   }
216 
217   AnalyzeSamples(l_samples,r_samples,588,2);
218 }
219 
CDPWrite(int outf,char * buffer)220 static long CDPWrite(int outf,char *buffer)
221 {
222   long words=0,temp;
223   long num=CD_FRAMESIZE_RAW;
224 
225   while(words<num){
226     temp=write(outf,buffer+words,num-words);
227     if(temp==-1){
228       if(errno!=EINTR && errno!=EAGAIN)
229 	return(-1);
230       temp=0;
231     }
232     words+=temp;
233   }
234 
235   return(0);
236 }
237 
CDPRip(char * device,char * generic_scsi_device,int track,long first_sector,long last_sector,char * outfile,int paranoia_mode,int * rip_smile_level,gfloat * rip_percent_done,gboolean * stop_thread_rip_now,gboolean do_gain_calc)238 gboolean CDPRip(char *device,char *generic_scsi_device,int track,
239 		long first_sector,long last_sector,
240 		char *outfile,int paranoia_mode,int *rip_smile_level,
241 		gfloat *rip_percent_done,gboolean *stop_thread_rip_now,
242 		gboolean do_gain_calc)
243 {
244   int force_cdrom_endian=-1;
245   int force_cdrom_sectors=-1;
246   int force_cdrom_overlap=-1;
247   int output_endian=0; /* -1=host, 0=little, 1=big */
248 
249   /* full paranoia, but allow skipping */
250   int out;
251   int verbose=CDDA_MESSAGE_FORGETIT;
252   int i;
253   long cursor,offset;
254   cdrom_drive *d=NULL;
255   cdrom_paranoia *p=NULL;
256 
257   global_rip_smile_level=rip_smile_level;
258 
259   /* Query the cdrom/disc; */
260 
261   if(generic_scsi_device && *generic_scsi_device)
262     d=cdda_identify_scsi(generic_scsi_device,device,verbose,NULL);
263   else  d=cdda_identify(device,verbose,NULL);
264 
265   if(!d){
266     if(!verbose)
267       Debug(_("Unable to open cdrom drive.\n"));
268 
269     return FALSE;
270   }
271 
272   if(verbose)
273     cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_PRINTIT);
274   else
275     cdda_verbose_set(d,CDDA_MESSAGE_PRINTIT,CDDA_MESSAGE_FORGETIT);
276 
277   /* possibly force hand on endianness of drive, sector request size */
278   if(force_cdrom_endian!=-1){
279     d->bigendianp=force_cdrom_endian;
280     switch(force_cdrom_endian){
281     case 0:
282       Debug(_("Forcing CDROM sense to little-endian; ignoring preset and autosense\n"));
283       break;
284     case 1:
285       Debug(_("Forcing CDROM sense to big-endian; ignoring preset and autosense\n"));
286       break;
287     }
288   }
289 
290   if(force_cdrom_sectors!=-1){
291     if(force_cdrom_sectors<0 || force_cdrom_sectors>100){
292       Debug(_("Default sector read size must be 1<= n <= 100\n"));
293       cdda_close(d);
294 
295       return FALSE;
296     }
297 
298     Debug(_("Forcing default to read %d sectors; "
299 	    "ignoring preset and autosense\n"),force_cdrom_sectors);
300 
301     d->nsectors=force_cdrom_sectors;
302     d->bigbuff=force_cdrom_sectors*CD_FRAMESIZE_RAW;
303   }
304 
305   if(force_cdrom_overlap!=-1){
306     if(force_cdrom_overlap<0 || force_cdrom_overlap>75){
307       Debug(_("Search overlap sectors must be 0<= n <=75\n"));
308       cdda_close(d);
309 
310       return FALSE;
311     }
312 
313     Debug(_("Forcing search overlap to %d sectors; "
314 	    "ignoring autosense\n"),force_cdrom_overlap);
315   }
316 
317   switch(cdda_open(d)) {
318   case -2:case -3:case -4:case -5:
319     Debug(_("Unable to open disc. Is there an audio CD in the drive?\n"));
320     cdda_close(d);
321     return FALSE;
322   case -6:
323     Debug(_("Cdparanoia could not find a way to read audio from this drive.\n"));
324     cdda_close(d);
325     return FALSE;
326   case 0:
327     break;
328   default:
329     Debug(_("Unable to open disc.\n"));
330     cdda_close(d);
331     return FALSE;
332   }
333 
334   if(d->interface==GENERIC_SCSI && d->bigbuff<=CD_FRAMESIZE_RAW) {
335     Debug(_("WARNING: You kernel does not have generic SCSI 'SG_BIG_BUFF'\n"
336 	    "         set, or it is set to a very small value.  Paranoia\n"
337 	    "         will only be able to perform single sector reads\n"
338 	    "         making it very unlikely Paranoia can work.\n\n"
339 	    "         To correct this problem, the SG_BIG_BUFF define\n"
340 	    "         must be set in /usr/src/linux/include/scsi/sg.h\n"
341 	    "         by placing, for example, the following line just\n"
342 	    "         before the last #endif:\n\n"
343 	    "         #define SG_BIG_BUFF 65536\n\n"
344 	    "         and then recompiling the kernel.\n\n"
345 	    "         Attempting to continue...\n\n"));
346   }
347 
348   if(d->nsectors==1){
349     Debug(_("WARNING: The autosensed/selected sectors per read value is\n"
350 	    "         one sector, making it very unlikely Paranoia can \n"
351 	    "         work.\n\n"
352 	    "         Attempting to continue...\n\n"));
353   }
354 
355   if(!cdda_track_audiop(d,track)) {
356     Debug(_("Selected track is not an audio track. Aborting.\n\n"));
357     cdda_close(d);
358     return FALSE;
359   }
360 
361   offset=cdda_track_firstsector(d,track);
362   first_sector+=offset;
363   last_sector+=offset;
364 
365   p=paranoia_init(d);
366   paranoia_modeset(p,paranoia_mode);
367 
368   if(force_cdrom_overlap!=-1) paranoia_overlapset(p,force_cdrom_overlap);
369 
370   if(verbose)
371     cdda_verbose_set(d,CDDA_MESSAGE_LOGIT,CDDA_MESSAGE_LOGIT);
372   else
373     cdda_verbose_set(d,CDDA_MESSAGE_FORGETIT,CDDA_MESSAGE_FORGETIT);
374 
375   paranoia_seek(p,cursor=first_sector,SEEK_SET);
376 
377   /* this is probably a good idea in general */
378   /*  seteuid(getuid());
379       setegid(getgid());*/
380 
381   out=open(outfile,O_RDWR|O_CREAT|O_TRUNC,0666);
382   if(out==-1){
383     Debug(_("Cannot open default output file %s: %s\n"),outfile,
384 	    strerror(errno));
385     cdda_close(d);
386     paranoia_free(p);
387 
388     return FALSE;
389   }
390 
391   WriteWav(out,(last_sector-first_sector+1)*CD_FRAMESIZE_RAW);
392 
393   /* Off we go! */
394 
395   while(cursor<=last_sector){
396     /* read a sector */
397     gint16 *readbuf=paranoia_read(p,CDPCallback);
398     char *err=cdda_errors(d);
399     char *mes=cdda_messages(d);
400 
401     *rip_percent_done=(gfloat)cursor/(gfloat)last_sector;
402 
403     if(mes || err)
404       Debug("\r                               "
405 	    "                                           \r%s%s\n",
406 	    mes?mes:"",err?err:"");
407 
408     if(err)free(err);
409     if(mes)free(mes);
410 
411     if(*stop_thread_rip_now) {
412       *stop_thread_rip_now=FALSE;
413 
414       cdda_close(d);
415       paranoia_free(p);
416 
417       return FALSE;
418     }
419 
420 
421     if(readbuf==NULL){
422       Debug(_("paranoia_read: Unrecoverable error, bailing.\n"));
423       cursor=last_sector+1;
424       paranoia_seek(p,cursor,SEEK_SET);
425       break;
426     }
427 
428     cursor++;
429 
430     if(output_endian!=bigendianp()){
431       for(i=0;i<CD_FRAMESIZE_RAW/2;i++)
432 	readbuf[i]=swap16(readbuf[i]);
433     }
434 
435     CDPCallback(cursor*(CD_FRAMEWORDS)-1,-2);
436 
437     if(do_gain_calc)
438       GainCalc((char *)readbuf);
439 
440     if(CDPWrite(out,(char *)readbuf)){
441       Debug(_("Error writing output: %s"),strerror(errno));
442 
443       cdda_close(d);
444       paranoia_free(p);
445 
446       return FALSE;
447     }
448 
449     if(output_endian!=bigendianp()){
450       for(i=0;i<CD_FRAMESIZE_RAW/2;i++)readbuf[i]=swap16(readbuf[i]);
451     }
452   }
453 
454   CDPCallback(cursor*(CD_FRAMESIZE_RAW/2)-1,-1);
455   close(out);
456 
457   paranoia_free(p);
458 
459   cdda_close(d);
460 
461   return TRUE;
462 }
463 
464 #endif /* CDPAR */
465