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