1 /*
2 ********************************************************************************
3 File: data.c
4
5 Tab size: 4
6 Max line length: 80
7 Programmer: Volker Kuhlmann
8
9
10 wav2cdr 2.3.4 Copyright (C) 1997, 1998, 1999, 2000, 2006 Volker Kuhlmann
11 This program is free software under the terms of the GNU General Public License
12 version 2 (or later, at your option).
13 See the file COPYING for details about license terms and warranty.
14 <VolkerKuhlmann@gmx.de>
15
16
17 DESCRIPTION:
18
19 This module handels all data I/O: opening / closing, headers, data block I/O.
20
21
22 CONDITIONALS:
23 see wav2cdr.c
24
25
26 HISTORY:
27 see wav2cdr.c, and ChangeLog
28
29 ********************************************************************************
30 */
31
32
33
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <time.h>
39 #include <limits.h>
40
41 #include "chelp.h"
42
43 #include "wav2cdr.h"
44
45
46
47 static cmdarg_t
48 cmdinfo; /* the cmd line args */
49
50 static process_t
51 procinfo; /* how and what processing should be done */
52
53 static unsigned long
54 firstsec, /* sector of first and last cut */
55 lastsec;
56
57 static unsigned long
58 total_bytes_out; /* bytes written to all files */
59
60 static time_t
61 /*instart,*/ /* start time of processing of current input file */
62 outstart; /* start time of processing of current output file */
63
64 static BOOL
65 out_is_open; /* flag whether output is open */
66
67 extern FILE
68 *msgfile; /* where to display cmd args and progress */
69
70 extern const unsigned long
71 bytes_in,
72 bytes_out;
73
74 extern const string
75 outname[];
76
77
78
79 /* local function prototypes */
80 static void check_for_fadeout (unsigned long nsecs, unsigned long endsecnum);
81
82
83
84 /*
85 Open output
86 */
open_out(int track)87 void open_out (int track)
88 {
89 outstart = time (NULL);
90
91 open_output_file (cmdinfo.outfilename, track);
92 out_is_open = TRUE;
93 DBGPRINTF1 ("open_out: %s\n", STRNULL(cmdinfo.outfilename));
94
95 write_header ();
96
97 } /* open_out() */
98
99
100
101 /*
102 Close output and print stats.
103 Can be called if output not open (can e.g. be the case if there
104 was no data to write, or at end of input when the last cut has been)
105 */
close_out(void)106 void close_out (void)
107 {
108 time_t runtime;
109 const string *to, strstdout[] = "(stdout)";
110 string stime[20];
111
112 /* if output not open do nothing */
113 if (NOT out_is_open)
114 return;
115
116 write_trailer ();
117 close_output_file ();
118 out_is_open = FALSE;
119
120 runtime = difftime (time (NULL), outstart);
121 total_bytes_out += bytes_out;
122
123 to = (cmdinfo.outfilename == NULL) ? strstdout : outname;
124 timeprintf (stime, bytes_out);
125 if (cmdinfo.outformat == AF_cdr)
126 fprintf (msgfile,
127 "Wrote bytes: %li (%li C, %s min) -> %s\n",
128 (long) bytes_out,
129 (long) bytes_out / CDAUDIOSECTORSIZE,
130 stime,
131 to);
132 else
133 fprintf (msgfile,
134 "Wrote bytes: %li -> %s\n",
135 (long) bytes_out,
136 to);
137
138 fprintf (msgfile, "Execution time: %02li:%02li:%02li h (%li s)\n"
139 "-\n",
140 runtime / 3600, (runtime / 60) % 60, runtime % 60, runtime);
141
142 } /* close_out() */
143
144
145
146 /*
147 Return the size of the header of the given audio format
148 In: audio format
149 Out: ---
150 Return: size of header, or -1
151 */
get_af_header_size(audioformat_t af)152 int get_af_header_size (audioformat_t af)
153 {
154 switch (af) {
155 case AF_raw:
156 return 0;
157 case AF_cdr:
158 return 0;
159 case AF_wav:
160 return sizeof (wav_header_t);
161 default:
162 return -1;
163 }
164 } /* get_af_header_size() */
165
166
167
168 /*
169 Read header of the current input file format
170 */
read_header(void)171 void read_header (void)
172 {
173 wav_header_t wh;
174
175 switch (cmdinfo.informat) {
176 case AF_wav:
177 read_wav_header (&wh);
178 break;
179 case AF_cdr:
180 case AF_raw:
181 default:
182 ;
183 }
184 } /* read_header() */
185
186
187
188 /*
189 Write header for the current output file format.
190 Used: struct cmdinfo
191 Side: bytes written updated
192 In: ---
193 Out: ---
194 Return: ---
195 */
write_header(void)196 void write_header (void)
197 {
198 wav_header_t wh;
199
200 DBGPRINTF1 ("write_header: %i\n", cmdinfo.outformat);
201 switch (cmdinfo.outformat) {
202 case AF_wav:
203 make_wav_header (&wh, 0L);
204 write_wav_header (&wh);
205 break;
206 case AF_cdr:
207 case AF_raw:
208 default:
209 ;
210 }
211
212 add_silence (cmdinfo.startsilence);
213
214 } /* write_header() */
215
216
217
218 /*
219 Write trailer for the current output file format.
220 This can be e.g. updating a header, or padding the last sector.
221 This functions is always called at the end of an output file.
222 Used: struct cmdinfo
223 Side: ---
224 In: ---
225 Out: ---
226 Return: ---
227 */
write_trailer(void)228 void write_trailer (void)
229 {
230 wav_header_t wh;
231 size_t last;
232 BORCRAP_largevar char buffer[CDAUDIOSECTORSIZE];
233
234 DBGPRINTF1 ("write_trailer: %i\n", cmdinfo.outformat);
235 add_silence (cmdinfo.endsilence);
236
237 switch (cmdinfo.outformat) {
238 case AF_wav:
239 /* update wav header */
240 make_wav_header (&wh, bytes_out - sizeof (wh));
241 write_wav_header (&wh);
242 break;
243 case AF_cdr:
244 /* pad last sector */
245 last = (size_t) (bytes_out % CDAUDIOSECTORSIZE);
246 if (last == 0)
247 break;
248 last = CDAUDIOSECTORSIZE - last;
249 if (last > 0) {
250 memset (buffer, 0, last);
251 write_block (buffer, last);
252 fprintf (msgfile, "cdr: padding %li bytes\n", (long) last);
253 }
254 break;
255 case AF_raw:
256 default:
257 ;
258 }
259
260 } /* write_trailer() */
261
262
263
264 /*
265 Add silence...
266 In: number of bytes to write
267 Out: ---
268 Return: ---
269 */
add_silence(unsigned long bytes)270 void add_silence (unsigned long bytes)
271 {
272 size_t w;
273 char buffer[CDAUDIOSECTORSIZE]; /* note buffer must be < max size_t! */
274
275 DBGPRINTF1 ("add_silence: %li\n", bytes);
276 memset (buffer, 0, sizeof (buffer));
277 do {
278 w = (bytes > sizeof (buffer)) ? sizeof (buffer) : (size_t) bytes;
279 w = write_block (buffer, w);
280 bytes -= w;
281 } while (bytes > 0);
282
283 } /* add_silence() */
284
285
286
287 /*
288 */
check_for_fadeout(unsigned long nsecs,unsigned long endsecnum)289 static void check_for_fadeout (unsigned long nsecs, unsigned long endsecnum)
290 {
291 unsigned long fadeoutblocks;
292
293 fadeoutblocks = cmdinfo.fadeout / CDAUDIOSECTORSIZE;
294 if (nsecs + fadeoutblocks == endsecnum)
295 procinfo.fadeout = fadeoutblocks;
296
297 } /* check_for_fadeout() */
298
299
300
301 /*
302 Decide whether to copy the current sector to the output or discard it, and
303 open/close files if necessary.
304 The first file is opened when necessary, the last file remains opened.
305 If cutting the input into pieces is off, all sectors are copied.
306 Cutting is turned on by specifying at least 2 cut numbers.
307 Cut numbers are the sector number from start of input, starting with 0.
308 One "cut" goes from one cut number to the next. The start cut number
309 belongs to the cut, but not the end cut number, as in [start..[end .
310 The first and the last cut are discarded, in other words all sectors
311 up to and not including the first cut number and after and including the
312 last cut number are discarded.
313 Cut numbers are assumed to be sorted in increasing order, or equal.
314 No consistency checking of cut numbers is done here.
315 In case adjacent cut numbers are equal, empty output is produced.
316 If output is to stdout, at most 1 cut / 2 cut numbers are allowed.
317 In: ---
318 Out: ---
319 Return: whether to copy this sector
320 */
copy_sector_check_file(void)321 BOOL copy_sector_check_file (void)
322 {
323 static unsigned long nsecs = 0, /* current sector of input */
324 endsecnum = 0; /* end sector num of current cut */
325 static int track = 1; /* counter of cutouts */
326
327 /* have not yet reached start sector - no show */
328 if (nsecs < firstsec) {
329 nsecs++; return FALSE;
330 }
331
332 /* open first output file to get started */
333 if (nsecs == firstsec) {
334 open_out (track);
335 procinfo.fadein = cmdinfo.fadein / CDAUDIOSECTORSIZE;
336 if (cmdinfo.numcuts != 0) {
337 endsecnum = get_cut_value (track);
338 DBGPRINTF1 ("endsecnum: %li\n", endsecnum);
339 } else if (cmdinfo.fadeout)
340 endsecnum = (unsigned long) get_input_size_in_cd_blocks ();
341 /* at this point we know that input size is available */
342 }
343
344 /* copy all sectors if there is no cutting */
345 if (cmdinfo.numcuts == 0) {
346 if (cmdinfo.fadeout)
347 check_for_fadeout (nsecs, endsecnum);
348 nsecs++; return TRUE;
349 }
350
351 /* if output is to stdout we can have at most 2 cut numbers */
352 if (cmdinfo.outfilename == NULL) {
353 if (cmdinfo.fadeout)
354 check_for_fadeout (nsecs, endsecnum);
355 nsecs++; return nsecs <= lastsec;
356 }
357
358 /* when reaching current end sector: close file and open next, unless
359 this was the last */
360 if (cmdinfo.fadeout)
361 check_for_fadeout (nsecs, endsecnum);
362 while (nsecs >= endsecnum) {
363 if (track < cmdinfo.numcuts - 1) {
364 close_out ();
365 open_out (++track);
366 procinfo.fadein = cmdinfo.fadein / CDAUDIOSECTORSIZE;
367 endsecnum = get_cut_value (track);
368 DBGPRINTF1 ("endsecnum: %li\n", endsecnum);
369 } else {
370 nsecs++; return FALSE;
371 }
372 }
373
374 nsecs++; return TRUE;
375
376 } /* copy_sector_check_file() */
377
378
379
380 /*
381 Scan the passed string for a silence value.
382 In: pointer to pointer to string, buffer for silence number
383 Out: pointer points to next, unscanned, number in string; silence number
384 Return: whether a silence number was found
385 */
got_silence_number(char ** pstr,UINT16 * ps)386 BOOL got_silence_number (char **pstr, UINT16 *ps)
387 {
388 int numbers_scanned, chars_scanned;
389
390 /*fprintf(stderr,"SILstr: len=%d <%s>\n", strlen(*pstr), *pstr);*/
391 /* scan over a possible silence value */
392 numbers_scanned = sscanf (*pstr, "%nS%hu%n%*c%n",
393 &chars_scanned, ps, &chars_scanned, &chars_scanned);
394 *pstr += chars_scanned;
395 /*if (numbers_scanned>0) fprintf(stderr,"SIL: %hu, <%s>\n", sil, *pstr);*/
396 return numbers_scanned >= 1;
397 } /* got_silence_number() */
398
399
400
401 /*
402 Scan the passed string for cut numbers. At most 1 is returned, and the
403 pointer to the string increased so that a subsequent call with the increased
404 pointer will return the next cut number.
405 In: pointer to pointer to string, buffer for cut number
406 Out: pointer points to next, unscanned, number in string; cut number
407 Return: whether a cut number was found
408 */
got_cut_number(char ** pstr,unsigned long * pi)409 BOOL got_cut_number (char **pstr, unsigned long *pi)
410 {
411 int numbers_scanned, chars_scanned;
412
413 numbers_scanned = sscanf (*pstr, "%n%lu%n%*c%n",
414 &chars_scanned, pi, &chars_scanned, &chars_scanned);
415 *pstr += chars_scanned;
416 return numbers_scanned >= 1;
417 } /* got_cut_number() */
418
419
420
sprint_cutinfo(void * buffer,unsigned long start,unsigned long end,BOOL is_audio_interval)421 void sprint_cutinfo (void *buffer, unsigned long start, unsigned long end,
422 BOOL is_audio_interval)
423 {
424 cdseccount_t diff;
425 string st1[TIMESTRSIZE], st2[TIMESTRSIZE], st3[TIMESTRSIZE];
426
427 diff = end - start;
428 timeprintf (st1, start * CDAUDIOSECTORSIZE);
429 timeprintf (st2, end * CDAUDIOSECTORSIZE);
430 timeprintf (st3, diff * CDAUDIOSECTORSIZE);
431 sprintf ((char *) buffer + strlen (buffer),
432 " %-5s %9li b, %6li C, %4li s, %s min\n"
433 " DIFF %9li b, %6li C, %4li s, %s min\n"
434 " --> %9li b, %6li C, %4li s, %s min\n\n",
435 is_audio_interval ? "AUDIO" : "silnc",
436 start * CDAUDIOSECTORSIZE, start,
437 start / CDSECTORSPERSEC, st1,
438 diff * CDAUDIOSECTORSIZE, diff,
439 diff / CDSECTORSPERSEC, st3,
440 end * CDAUDIOSECTORSIZE, end,
441 end / CDSECTORSPERSEC, st2
442 );
443 } /* sprint_cutinfo() */
444
445
446
447 /*
448 Generate the silence information of the input file.
449 Currently this function does not work if the number of cuts is not even;
450 fulfilled by processing though.
451 */
silence_info(void * buffer,BOOL eof)452 void silence_info (void *buffer, BOOL eof)
453 {
454 cdseccount_t cut = 0U;
455 static cdseccount_t lastcut = (cdseccount_t) -1, lastsigend = 0U;
456 static BOOL didname = FALSE;
457 static BOOL hadcut = FALSE;
458 string *cutstring, *cuts;
459 UINT16 silval;
460
461 #if 0
462 const string *t[] = { "23", " 23", "23 ", " 4 ", " 55 ", "", "5 6 7",
463 "77C", "77c", "7C 8C", "x"};
464 string *s;
465 int i = 0;
466 while (t[i][0] != 'x') {
467 s = (/*no const*/ string *) t[i];
468 printf ("---+---\n");
469 while (got_cut_number (&s, &cut))
470 printf ("got cut number %ld\n", cut);
471 i++;
472 }
473 #endif
474
475 if (procinfo.n_returned == 0U AND NOT eof) /* speed things up a bit */
476 return;
477
478 cutstring = cuts = malloc (procinfo.n_returned + 1);
479 if (cuts == NULL)
480 exit_error (ERR_NOMEM, "Could not allocate memory", NULL);
481 strncpy (cuts, buffer, procinfo.n_returned);
482 cuts[procinfo.n_returned] = '\0';
483 #ifdef DEBUG
484 cuts = (strchr (cuts, '=') != NULL) ? strchr (cuts, '=') + 2 : cuts;
485 #endif
486
487 #define BUF(buf) ((char *) (buf) + strlen (buf))
488 * (char *) buffer = '\0';
489 if (NOT didname) {
490 sprintf (buffer, "%s:\n", STRSTDIN(cmdinfo.infilename));
491 didname = TRUE;
492 }
493
494 while (procinfo.n_returned > 1U) {
495 /* print silence value if there */
496 if (got_silence_number (&cuts, &silval) AND cmdinfo.verbose)
497 sprintf (BUF(buffer), "Silence = %i\n", silval);
498
499 /* if there is a cut number, collect and print out */
500 if (NOT got_cut_number (&cuts, &cut))
501 break;
502 if (lastcut == (cdseccount_t) -1) {
503 lastcut = cut;
504 continue;
505 }
506 hadcut = TRUE;
507 if (cut == 0U) /* last sector of file */
508 cut = bytes_in / CDAUDIOSECTORSIZE;
509 if (lastsigend < lastcut)
510 sprint_cutinfo (BUF(buffer), lastsigend, lastcut, FALSE);
511 sprint_cutinfo (BUF(buffer), lastcut, cut, TRUE);
512 lastsigend = cut;
513 lastcut = (cdseccount_t) -1;
514 }
515 if (eof AND lastsigend < bytes_in / CDAUDIOSECTORSIZE) {
516 sprint_cutinfo (buffer, lastsigend, bytes_in / CDAUDIOSECTORSIZE, FALSE);
517 }
518 free (cutstring);
519 procinfo.n_returned = strlen (buffer); /* ignore the '\0' here */
520 } /* silence_info() */
521
522
523
524 /*
525 Read + write all data sectors, but no headers etc.
526 Does not open/close input or output.
527 Assumes input is open, and is left open.
528 Assumes output is closed and will be opened/closed by
529 copy_sector_check_file(). Output will be left closed ???
530 In: ---
531 Out: ---
532 Return: ---
533 */
handle_sectors(void)534 void handle_sectors (void)
535 {
536 size_t numtoread, numread, numtowrite;
537 BORCRAP_largevar
538 byte buffer[CDAUDIOSECTORSIZE * PROCESS_MAX_MULTIPLY];
539
540 numtoread = CDAUDIOSECTORSIZE;
541
542 do {
543 numread = read_block (&buffer, numtoread);
544 /* go through the loop for empty input also */
545
546 /* can only process in multiples of 4 bytes, truncate */
547 if (numread BITAND 0x03) {
548 fprintf (msgfile,
549 "Can only process data in multiples of 4 bytes,"
550 " truncating %i bytes.\n", (int)(numread BITAND 0x03));
551 numread ABITAND (size_t) -4; /* = 0x...fffc */
552 }
553
554 /* handle cutting / cut numbers, and opening closing output files */
555 if (NOT copy_sector_check_file ())
556 continue;
557
558 /* do the number crunching */
559 procinfo.n_in = numread;
560 procinfo.last = read_eof ();
561 process_sector (&buffer, &procinfo);
562 if (cmdinfo.silenceinfo)
563 silence_info (buffer, procinfo.last);
564
565 /* write out buffer */
566 numtowrite = procinfo.n_returned;
567 DBGPRINTF2 ("handle_sectors: numread %i, numtowrite %i\n",
568 numread, numtowrite);
569 write_block (&buffer, numtowrite);
570
571 } while ((numread == numtoread) AND NOT read_eof ());
572
573 } /* handle_sectors() */
574
575
576
577 /*
578 Deal with an input file and write to output.
579 This is called after command line parsing and does the lot.
580 In: ---
581 Out: ---
582 Return: ---
583 */
do_data_io(void)584 void do_data_io (void)
585 {
586 time_t executionstart;
587 unsigned long runtime;
588 string stime[20];
589
590 /* init local variables */
591 total_bytes_out = 0;
592 firstsec = lastsec = 0;
593 outstart = (time_t) -1;
594 out_is_open = FALSE;
595
596 /* get current values */
597 get_cmdarg_info (&cmdinfo);
598 init_process_info (&procinfo);
599 if (cmdinfo.numcuts > 0) {
600 firstsec = get_cut_value (0);
601 lastsec = get_cut_value (cmdinfo.numcuts - 1);
602 }
603
604 /* START watch */
605 executionstart = time (NULL);
606
607 /* DO it */
608 open_input_file (cmdinfo.infilename);
609 read_header ();
610 /* open output is done by copy_sector_check_file() */
611 handle_sectors ();
612 close_input_file ();
613 close_out (); /* will be still open if not cutting */
614
615 /* STOP watch */
616 runtime = difftime (time (NULL), executionstart);
617
618 if (cmdinfo.outformat == AF_cdr) {
619 timeprintf (stime, total_bytes_out);
620 fprintf (msgfile,
621 "Wrote bytes total: %li (%li C, %s min)\n",
622 (long) total_bytes_out,
623 (long) total_bytes_out / CDAUDIOSECTORSIZE, stime);
624 } else
625 fprintf (msgfile,
626 "Wrote bytes total: %li\n",
627 (long) total_bytes_out);
628 fprintf (msgfile, "Exec time total: %02li:%02li:%02li h (%li s)\n",
629 runtime / 3600, (runtime / 60) % 60, runtime % 60, runtime);
630
631 } /* do_data_io() */
632
633
634
635 /* EOF dataio.c */
636 /******************************************************************************/
637