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