1 /*  cdrdao - write audio CD-Rs in disc-at-once mode
2  *
3  *  Copyright (C) 1998-2001  Andreas Mueller <andreas@daneb.de>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <errno.h>
30 
31 #include "TrackData.h"
32 #include "Msf.h"
33 #include "util.h"
34 #include "log.h"
35 
36 #ifdef UNIXWARE
37 extern "C" {
38   extern int      strcasecmp(const char *, const char *);
39 }
40 #endif
41 
42 // creates an object representing a portion of an audio data file
TrackData(const char * filename,long offset,unsigned long start,unsigned long length)43 TrackData::TrackData(const char *filename, long offset,
44 		     unsigned long start, unsigned long length)
45 {
46   init(filename, offset, start, length);
47 }
48 
TrackData(const char * filename,unsigned long start,unsigned long length)49 TrackData::TrackData(const char *filename, unsigned long start,
50 		     unsigned long length)
51 {
52   init(filename, 0, start, length);
53 }
54 
init(const char * filename,long offset,unsigned long start,unsigned long length)55 void TrackData::init(const char *filename, long offset,
56 		     unsigned long start, unsigned long length)
57 {
58   assert(offset >= 0);
59 
60   length_ = length;
61 
62   if (strcmp(filename, "-") == 0) {
63     type_ = STDIN;
64     fileType_ = RAW; // currently only raw data
65     filename_ = strdupCC("STDIN");
66   }
67   else {
68     type_ = DATAFILE;
69 
70     filename_ = strdupCC(filename);
71     fileType_ = audioFileType(filename_);
72   }
73 
74   effFilename_ = NULL;
75   offset_ = offset;
76   startPos_ = start;
77   swapSamples_ = 0;
78   mode_ = AUDIO;
79   subChannelMode_ = SUBCHAN_NONE;
80   audioCutMode_ = 1;
81 }
82 
83 // creates an AUDIO mode object that contains constant zero data
TrackData(unsigned long length)84 TrackData::TrackData(unsigned long length)
85 {
86   type_ = ZERODATA;
87   mode_ = AUDIO;
88   subChannelMode_ = SUBCHAN_NONE;
89   audioCutMode_ = 1;
90 
91   filename_ = NULL;
92   effFilename_ = NULL;
93   fileType_ = RAW;
94   startPos_ = 0;
95   offset_ = 0;
96   length_ = length;
97   swapSamples_ = 0;
98 }
99 
100 // creates an object that contains constant data with specified mode
TrackData(Mode m,SubChannelMode sm,unsigned long length)101 TrackData::TrackData(Mode m, SubChannelMode sm, unsigned long length)
102 {
103   type_ = ZERODATA;
104   mode_ = m;
105   subChannelMode_ = sm;
106   audioCutMode_ = 0;
107 
108   filename_ = NULL;
109   effFilename_ = NULL;
110   fileType_ = RAW;
111   startPos_ = 0;
112   offset_ = 0;
113   length_ = length;
114   swapSamples_ = 0;
115 }
116 
117 // creates a file object  with given mode
TrackData(Mode m,SubChannelMode sm,const char * filename,long offset,unsigned long length)118 TrackData::TrackData(Mode m, SubChannelMode sm, const char *filename,
119 		     long offset, unsigned long length)
120 {
121   init(m, sm, filename, offset, length);
122 }
123 
init(Mode m,SubChannelMode sm,const char * filename,long offset,unsigned long length)124 void TrackData::init(Mode m, SubChannelMode sm, const char *filename,
125 		     long offset, unsigned long length)
126 {
127   assert(offset >= 0);
128 
129   mode_ = m;
130   subChannelMode_ = sm;
131   audioCutMode_ = 0;
132 
133   if (strcmp(filename, "-") == 0) {
134     type_ = STDIN;
135     fileType_ = RAW; // currently only raw data
136     filename_ = strdupCC("STDIN");
137   }
138   else {
139     type_ = DATAFILE;
140     filename_ = strdupCC(filename);
141 
142     if (mode_ == AUDIO)
143       fileType_ = audioFileType(filename_);
144     else
145       fileType_ = RAW; // data files are always raw
146   }
147 
148   offset_ = offset;
149   length_ = length;
150   effFilename_ = NULL;
151   startPos_ = 0;
152   swapSamples_ = 0;
153 }
154 
TrackData(Mode m,SubChannelMode sm,const char * filename,unsigned long length)155 TrackData::TrackData(Mode m, SubChannelMode sm, const char *filename,
156 		     unsigned long length)
157 {
158   assert(filename != NULL && *filename != 0);
159 
160   mode_ = m;
161   subChannelMode_ = sm;
162   audioCutMode_ = 0;
163 
164   type_ = FIFO;
165   fileType_ = RAW;
166   filename_ = strdupCC(filename);
167   effFilename_ = NULL;
168   offset_ = 0;
169   length_ = length;
170 
171   startPos_ = 0;
172   swapSamples_ = 0;
173 }
174 
175 // copy constructor
TrackData(const TrackData & obj)176 TrackData::TrackData(const TrackData &obj)
177 {
178   type_ = obj.type_;
179   mode_ = obj.mode_;
180   subChannelMode_ = obj.subChannelMode_;
181   audioCutMode_ = obj.audioCutMode_;
182 
183   offset_ = obj.offset_;
184 
185   switch (type_) {
186   case DATAFILE:
187   case STDIN:
188   case FIFO:
189     filename_ = strdupCC(obj.filename_);
190     effFilename_ = (obj.effFilename_ ? strdupCC(obj.effFilename_) : NULL);
191     startPos_ = obj.startPos_;
192     fileType_ = obj.fileType_;
193     break;
194 
195   case ZERODATA:
196     filename_ = NULL;
197     effFilename_ = NULL;
198     startPos_ = 0;
199     fileType_ = RAW;
200     break;
201   }
202 
203   length_ = obj.length_;
204 
205   swapSamples_ = obj.swapSamples_;
206 }
207 
~TrackData()208 TrackData::~TrackData()
209 {
210   if (filename_) {
211     delete[] filename_;
212   }
213   if (effFilename_)
214     delete[] effFilename_;
215 }
216 
length() const217 unsigned long TrackData::length() const
218 {
219   return length_;
220 }
221 
222 // Determines length of data by inspecting the data file. The available data
223 // from the specified start point up to the end of file is stored in 'length_'.
224 // Return: 0: OK
225 //         1: cannot open or access the file (see 'errno')
226 //         2: start pos or offset exceeds length of file
227 //         3: track needs conversion to WAV
228 //         4: format is not supported
229 
determineLength()230 int TrackData::determineLength()
231 {
232   unsigned long len;
233 
234   if (type_ == DATAFILE) {
235     if (mode_ == AUDIO) {
236       switch (audioDataLength(filename_, offset_, &len)) {
237       case 1:
238 	log_message(-2, "Cannot open audio file \"%s\": %s", filename_,
239 		strerror(errno));
240 	return 1;
241 	break;
242 
243       case 2:
244 	log_message(-2, "Cannot determine length of audio file \"%s\": %s",
245 		filename_, strerror(errno));
246 	return 1;
247 	break;
248 
249       case 3:
250 	log_message(-2, "Header of audio file \"%s\" is corrupted.",
251 		filename_);
252 	return 1;
253 	break;
254 
255       case 4:
256 	log_message(-2, "Invalid offset %ld for audio file \"%s\".", offset_,
257 		filename_);
258 	return 2;
259 	break;
260       case 5:
261 #ifndef HAVE_MP3_SUPPORT
262           if (audioFileType(filename_) == MP3) {
263               log_message (-2, "Can't read file \"%s\": cdrdao was compiled "
264 			   "without MP3 support.", filename_);
265               return 4;
266           }
267 #endif
268 #ifndef HAVE_OGG_SUPPORT
269           if (audioFileType(filename_) == OGG) {
270               log_message (-2, "Can't read file \"%s\": cdrdao was compiled "
271 			   "without Ogg/Vorbis support.", filename_);
272               return 4;
273           }
274 #endif
275           return 3;
276       }
277 
278       if (audioCutMode()) {
279 	if (startPos_ < len) {
280 	  length_ = len - startPos_;
281 	}
282 	else {
283 	  log_message(-2,
284 		  "Start position %lu exceeds available data of file \"%s\".",
285 		  startPos_, filename_);
286 	  return 2;
287 	}
288       }
289       else {
290 	length_ = len * sizeof(Sample);
291       }
292     }
293     else {
294       switch (dataFileLength(filename_, offset_, &len)) {
295       case 1:
296 	log_message(-2, "Cannot open data file \"%s\": %s", filename_,
297 		strerror(errno));
298 	return 1;
299 	break;
300       case 2:
301 	log_message(-2, "Invalid offset %ld for audio file \"%s\".", offset_,
302 		filename_);
303 	return 2;
304 	break;
305       }
306 
307       length_ = len;
308     }
309   }
310 
311   return 0;
312 }
313 
314 
315 
316 // checks the consistency of object
317 // return: 0: OK
318 //         1: warning occured
319 //         2: error occured
check(int trackNr) const320 int TrackData::check(int trackNr) const
321 {
322   switch (type_) {
323   case ZERODATA:
324     // always OK
325     break;
326   case STDIN:
327     // cannot do much here...
328     break;
329   case FIFO:
330     if (access(filename_, R_OK) != 0) {
331       log_message(-2, "Track %d: Cannot access FIFO \"%s\": %s", trackNr,
332 	      filename_, strerror(errno));
333       return 2;
334     }
335     break;
336   case DATAFILE:
337     if (mode_ == AUDIO) {
338       unsigned long len = 0;
339 
340       if (fileType_ == WAVE && subChannelMode_ != SUBCHAN_NONE) {
341 	log_message(-2, "Track %d: WAVE audio files cannot contain sub-channel "
342                 "data.", trackNr);
343 	return 2;
344       }
345 
346       switch (audioDataLength(filename_, offset_, &len)) {
347       case 1:
348 	log_message(-2, "Track %d: Cannot open audio file \"%s\": %s", trackNr,
349 		filename_, strerror(errno));
350 	return 2;
351 	break;
352       case 2:
353 	log_message(-2, "Track %d: Cannot access audio file \"%s\": %s", trackNr,
354 		filename_, strerror(errno));
355 	return 2;
356 	break;
357       case 3:
358 	log_message(-2, "Track %d: %s: Unacceptable WAVE file.", trackNr,
359 		filename_);
360 	return 2;
361 	break;
362       case 4:
363 	log_message(-2, "Track %d: Invalid offset %ld for audio file \"%s\".",
364 		trackNr, offset_, filename_);
365 	return 2;
366 	break;
367       }
368 
369       if (length() == 0) {
370 	log_message(-2, "Track %d: Requested length for audio file \"%s\" is 0.",
371 		trackNr, filename_);
372 	return 2;
373       }
374 
375       if (audioCutMode()) {
376 	if (startPos_ + length() > len) {
377 	  // requested part exceeds file size
378 	  log_message(-2, "Track %d: Requested length (%lu + %lu samples) exceeds "
379                   "length of audio file \"%s\" (%lu samples at offset %ld).",
380 		  trackNr, startPos_, length(), filename_, len, offset_);
381 	  return 2;
382 	}
383       }
384       else {
385 	if (length() > len * sizeof(Sample)) {
386 	  log_message(-2, "Track %d: Requested length (%lu bytes) exceeds length of file \"%s\" (%lu bytes at offset %ld).",
387 		  trackNr, length(), filename_, len, offset_);
388 	  return 2;
389 	}
390       }
391     }
392     else {
393       // data mode
394       unsigned long len;
395 
396       switch (dataFileLength(filename_, offset_, &len)) {
397       case 1:
398 	log_message(-2, "Track %d: Cannot open data file \"%s\": %s", trackNr,
399 		filename_, strerror(errno));
400 	return 2;
401 	break;
402       case 2:
403 	log_message(-2, "Track %d: Invalid offset %ld for data file \"%s\".",
404 		trackNr, offset_, filename_);
405 	return 2;
406 	break;
407       }
408 
409       if (length() == 0) {
410 	log_message(-2, "Track %d: Requested length for data file \"%s\" is 0.",
411 		trackNr, filename_);
412 	return 2;
413       }
414 
415       if (length() > len) {
416 	log_message(-2, "Track %d: Requested length (%lu bytes) exceeds length of file \"%s\" (%lu bytes at offset %ld).",
417 		trackNr, length(), filename_, len, offset_);
418 	return 2;
419       }
420     }
421     break;
422   }
423 
424   return 0;
425 }
426 
effectiveFilename(const char * name)427 void TrackData::effectiveFilename(const char* name)
428 {
429   if (effFilename_)
430     delete effFilename_;
431 
432   effFilename_ = filename_;
433   filename_ = strdupCC(name);
434   fileType_ = audioFileType(filename_);
435 }
436 
437 // writes out contents of object in TOC file syntax
print(std::ostream & out,bool conversions) const438 void TrackData::print(std::ostream &out, bool conversions) const
439 {
440   unsigned long blen;
441   const char *s;
442 
443   if (audioCutMode()) {
444     // we're calculating in samples and not in bytes for audio data
445     blen = SAMPLES_PER_BLOCK;
446   }
447   else {
448     blen  = dataBlockSize(mode(), subChannelMode());
449   }
450 
451   switch (type()) {
452   case STDIN:
453   case DATAFILE:
454     if (audioCutMode()) {
455       if (type() == STDIN)
456 	out << "FILE \"-\" ";
457       else if (effFilename_ && !conversions)
458 	out << "FILE \"" << effFilename_ << "\" ";
459       else
460 	out << "FILE \"" << filename_ << "\" ";
461 
462       if (swapSamples_)
463 	out << "SWAP ";
464 
465       if (offset_ > 0)
466 	out << "#" << offset_ << " ";
467 
468       if (startPos() != 0 && (startPos() % blen) == 0)
469 	out << Msf(startPos() / blen).str();
470       else
471 	out << startPos();
472 
473       out << " ";
474     }
475     else {
476       // data mode
477       if (type() == STDIN)
478 	out << "DATAFILE \"-\" ";
479       else if (effFilename_ && !conversions)
480 	out << "DATAFILE \"" << effFilename_ << "\" ";
481       else
482 	out << "DATAFILE \"" << filename_ << "\" ";
483 
484       //out <<  mode2String(mode()) << " ";
485 
486       if (offset_ > 0)
487 	out << "#" << offset_ << " ";
488     }
489 
490 
491     if ((length() % blen) == 0)
492       out << Msf(length() / blen).str();
493     else
494       out << length();
495 
496     if (!audioCutMode())
497       out << " // length in bytes: " << length();
498 
499     out << std::endl;
500     break;
501 
502   case FIFO:
503     out << "FIFO \"" << filename_ << "\" ";
504 
505     if ((length() % blen) == 0) {
506       out << Msf(length() / blen).str();
507       out << " // length in bytes: " << length();
508     }
509     else {
510       out << length();
511     }
512 
513     out << std::endl;
514     break;
515 
516   case ZERODATA:
517     if (audioCutMode()) {
518       out << "SILENCE ";
519     }
520     else {
521       out << "ZERO " << mode2String(mode()) << " ";
522     }
523 
524     s = subChannelMode2String(subChannelMode());
525 
526     if (*s != 0)
527       out << s << " ";
528 
529     if ((length() % blen) == 0)
530       out << Msf(length() / blen).str();
531     else
532       out << length();
533 
534     out << std::endl;
535     break;
536   }
537 }
538 
split(unsigned long pos,TrackData ** part1,TrackData ** part2)539 void TrackData::split(unsigned long pos, TrackData **part1, TrackData **part2)
540 {
541   assert(mode_ == AUDIO);
542   assert(pos > 0 && pos < length_);
543 
544   *part1 = new TrackData(*this);
545   *part2 = new TrackData(*this);
546 
547   (*part1)->length_ = pos;
548 
549   (*part2)->length_ = length_ - pos;
550 
551   if (type_ == DATAFILE)
552     (*part2)->startPos_ = startPos_ + pos;
553 }
554 
merge(const TrackData * obj) const555 TrackData *TrackData::merge(const TrackData *obj) const
556 {
557 
558   if (mode_ != AUDIO || type_ != obj->type_ || mode_ != obj->mode_ ||
559       subChannelMode_ != obj->subChannelMode_ || offset_ != obj->offset_ ||
560       audioCutMode_ != obj->audioCutMode_)
561     return NULL;
562 
563   TrackData *data = NULL;
564 
565   switch (type_) {
566   case ZERODATA:
567     data = new TrackData(*this);
568     data->length_ += obj->length_;
569     break;
570 
571   case DATAFILE:
572     if (strcmp(filename_, obj->filename_) == 0 &&
573 	startPos_ + length_ == obj->startPos_) {
574       data = new TrackData(*this);
575       data->length_ += obj->length_;
576     }
577     break;
578 
579   case STDIN:
580   case FIFO:
581     // can't merge this type at all
582     break;
583   }
584 
585   return data;
586 }
587 
588 // Checks if given audio file is suitable for cdrdao. 'length' is filled
589 // with number of samples in audio file on success.
590 // return: 0: file is suitable
591 //         1: cannot open or access file
592 //         2: file has wrong format
593 
checkAudioFile(const char * fn,unsigned long * length)594 int TrackData::checkAudioFile(const char *fn, unsigned long *length)
595 {
596   int fd;
597   int ret;
598   struct stat buf;
599   long headerLength = 0;
600 
601   enum FileType ft = audioFileType(fn);
602   if (ft != WAVE && ft != RAW) {
603     log_message(-2, "Checking audio file \"%s\": format not supported", fn);
604     return 1;
605   }
606 
607   if ((fd = open(fn, O_RDONLY)) < 0)
608     return 1;
609 
610   ret = fstat(fd, &buf);
611 
612   close(fd);
613 
614   if (ret != 0)
615     return 1;
616 
617   if (ft == WAVE) {
618     if (waveLength(fn, 0, &headerLength, length) != 0)
619       return 2;
620   } else {
621     if (buf.st_size % sizeof(Sample) != 0) {
622       log_message(-1, "%s: Length is not a multiple of sample size (4).", fn);
623     }
624 
625     *length = buf.st_size / sizeof(Sample);
626   }
627 
628   return 0;
629 }
630 
631 
632 // Determines length of header and audio data for WAVE files. 'hdrlen' is
633 // filled with length of WAVE header in bytes. 'datalen' is filled with
634 // length of audio data in samples (if != NULL).
635 // return: 0: OK
636 //         1: error occured
637 //         2: illegal WAVE file
waveLength(const char * filename,long offset,long * hdrlen,unsigned long * datalen)638 int TrackData::waveLength(const char *filename, long offset,
639 			  long *hdrlen, unsigned long *datalen)
640 {
641   FILE *fp;
642   char magic[4];
643   long headerLen = 0;
644   long len;
645   short waveFormat;
646   short waveChannels;
647   long waveRate;
648   short waveBits;
649   struct stat sbuf;
650 
651 #ifdef __CYGWIN__
652   if ((fp = fopen(filename, "rb")) == NULL)
653 #else
654   if ((fp = fopen(filename, "r")) == NULL)
655 #endif
656   {
657     log_message(-2, "Cannot open audio file \"%s\" for reading: %s",
658 	    filename, strerror(errno));
659     return 1;
660   }
661 
662   if (offset != 0) {
663     if (fseek(fp, offset, SEEK_SET) != 0) {
664       log_message(-2, "Cannot seek to offset %ld in file \"%s\": %s",
665 	      offset, filename, strerror(errno));
666       return 1;
667     }
668   }
669 
670   if (fread(magic, sizeof(char), 4, fp) != 4 ||
671       strncmp("RIFF", magic, 4) != 0) {
672     log_message(-2, "%s: not a WAVE file.", filename);
673     fclose(fp);
674     return 2;
675   }
676 
677   readLong(fp);
678 
679   if (fread(magic, sizeof(char), 4, fp) != 4 ||
680       strncmp("WAVE", magic, 4) != 0) {
681     log_message(-2, "%s: not a WAVE file.", filename);
682     fclose(fp);
683     return 2;
684   }
685 
686   // search for format chunk
687   for (;;) {
688     if (fread(magic, sizeof(char), 4, fp) != 4) {
689       log_message(-2, "%s: corrupted WAVE file.", filename);
690       fclose(fp);
691       return 1;
692     }
693 
694     len = readLong(fp);
695     len += len & 1; // round to multiple of 2
696 
697     if (strncmp("fmt ", magic, 4) == 0) {
698       // format chunk found
699       break;
700     }
701 
702     // skip chunk data
703     if (fseek(fp, len, SEEK_CUR) != 0) {
704       log_message(-2, "%s: corrupted WAVE file.", filename);
705       fclose(fp);
706       return 1;
707     }
708   }
709 
710   if (len < 16) {
711     log_message(-2, "%s: corrupted WAVE file.", filename);
712     fclose(fp);
713     return 1;
714   }
715 
716   waveFormat = readShort(fp);
717 
718   if (waveFormat != 1) {
719     // not PCM format
720     log_message(-2, "%s: not in PCM format.", filename);
721     fclose(fp);
722     return 2;
723   }
724 
725   waveChannels = readShort(fp);
726   if (waveChannels != 2) {
727     log_message(-2, "%s: found %d channel(s), require 2 channels.",
728 	    filename, waveChannels);
729     fclose(fp);
730     return 2;
731   }
732 
733   waveRate = readLong(fp);
734   if (waveRate != 44100) {
735      log_message(-2, "%s: found sampling rate %ld, require 44100.",
736 	    filename, waveRate);
737      fclose(fp);
738      return 2;
739   }
740 
741   readLong(fp); // average bytes/second
742   readShort(fp); // block align
743 
744   waveBits = readShort(fp);
745   if (waveBits != 16) {
746     log_message(-2, "%s: found %d bits per sample, require 16.",
747 	    filename, waveBits);
748     fclose(fp);
749     return 2;
750   }
751 
752   len -= 16;
753 
754   // skip chunk data
755   if (fseek(fp, len, SEEK_CUR) != 0) {
756     log_message(-2, "%s: corrupted WAVE file.", filename);
757     fclose(fp);
758     return 1;
759   }
760 
761   // search wave data chunk
762   for (;;) {
763     if (fread(magic, sizeof(char), 4, fp) != 4) {
764       log_message(-2, "%s: corrupted WAVE file.", filename);
765       fclose(fp);
766       return 1;
767     }
768 
769     len = readLong(fp);
770 
771     if (strncmp("data", magic, 4) == 0) {
772       // found data chunk
773       break;
774     }
775 
776     len += len & 1; // round to multiple of 2
777 
778     // skip chunk data
779     if (fseek(fp, len, SEEK_CUR) != 0) {
780       log_message(-2, "%s: corrupted WAVE file.", filename);
781       fclose(fp);
782       return 1;
783     }
784   }
785 
786   if ((headerLen = ftell(fp)) < 0) {
787     log_message(-2, "%s: cannot determine file position: %s",
788 	    filename, strerror(errno));
789     fclose(fp);
790     return 1;
791   }
792 
793   headerLen -= offset;
794 
795   if (fstat(fileno(fp), &sbuf) != 0) {
796     log_message(-2, "Cannot fstat audio file \"%s\": %s", filename,
797 	    strerror(errno));
798     fclose(fp);
799     return 1;
800   }
801 
802   fclose(fp);
803 
804   if (len + headerLen + offset > sbuf.st_size) {
805     log_message(-1,	"%s: file length does not match length from WAVE header - using actual length.", filename);
806     len = sbuf.st_size - offset - headerLen;
807   }
808 
809   if (len % sizeof(Sample) != 0) {
810     log_message(-1,
811 	    "%s: length of data chunk is not a multiple of sample size (4).",
812 	    filename);
813   }
814 
815   *hdrlen = headerLen;
816 
817   if (datalen != NULL) {
818     *datalen = len / sizeof(Sample);
819   }
820 
821   return 0;
822 }
823 
824 // Returns length in samples for given audio file.
825 // return: 1: file cannot be opened
826 //         2: 'fstat' failed
827 //         3: file header corruption
828 //         4: invalid offset
829 //         5: file need conversion
830 //         0: OK
audioDataLength(const char * fname,long offset,unsigned long * length)831 int TrackData::audioDataLength(const char *fname, long offset,
832 			       unsigned long *length)
833 {
834   int fd;
835   struct stat buf;
836   long headerLength = 0;
837   int ret;
838 
839   *length = 0;
840 
841   if ((fd = open(fname, O_RDONLY)) < 0)
842     return 1;
843 
844   ret = fstat(fd, &buf);
845   close(fd);
846 
847   if (ret != 0)
848     return 2;
849 
850   if (offset > buf.st_size)
851     return 4;
852 
853   FileType ftype = audioFileType(fname);
854   if (ftype == WAVE) {
855     if (waveLength(fname, offset, &headerLength, length) != 0)
856       return 3;
857   } else if (ftype == MP3 || ftype == OGG) {
858     return 5;
859   } else {
860     if (((buf.st_size - offset) % sizeof(Sample)) != 0) {
861       log_message(-1,
862 	      "Length of file \"%s\" is not a multiple of sample size (4).",
863 	      fname);
864     }
865 
866     *length = (buf.st_size - offset) / sizeof(Sample);
867   }
868 
869   return 0;
870 }
871 
872 
873 // Sets 'length' to length of given data file.
874 // return: 0: OK
875 //         1: file cannot be opened or accessed
876 //         2: invalid offset
dataFileLength(const char * fname,long offset,unsigned long * length)877 int TrackData::dataFileLength(const char *fname, long offset,
878 			      unsigned long *length)
879 {
880   int fd;
881   struct stat buf;
882   int ret;
883   *length = 0;
884 
885   if ((fd = open(fname, O_RDONLY)) < 0)
886     return 1;
887 
888   ret = fstat(fd, &buf);
889   close(fd);
890 
891   if (ret != 0)
892     return 1;
893 
894   if (offset > buf.st_size)
895     return 2;
896 
897   *length = buf.st_size - offset;
898 
899   return 0;
900 }
901 
902 // determines type of audio file
903 // return: RAW: raw samples
904 //         WAVE: wave file
audioFileType(const char * filename)905 TrackData::FileType TrackData::audioFileType(const char *filename)
906 {
907   FileExtension p = fileExtension(filename);
908 
909   if (p == FE_WAV)
910     return WAVE;
911   if (p == FE_MP3)
912     return MP3;
913   if (p == FE_OGG)
914     return OGG;
915 
916   return RAW;
917 }
918 
subChannelSize(SubChannelMode sm)919 unsigned long TrackData::subChannelSize(SubChannelMode sm)
920 {
921   unsigned long b = 0;
922 
923   switch (sm) {
924   case SUBCHAN_NONE:
925     b = 0;
926     break;
927 
928   case SUBCHAN_RW:
929   case SUBCHAN_RW_RAW:
930     b = PW_SUBCHANNEL_LEN;
931     break;
932   }
933 
934   return b;
935 }
936 
dataBlockSize(Mode m,SubChannelMode sm)937 unsigned long TrackData::dataBlockSize(Mode m, SubChannelMode sm)
938 {
939   unsigned long b = 0;
940 
941   switch (m) {
942   case AUDIO:
943   case MODE1_RAW:
944   case MODE2_RAW:
945     b = AUDIO_BLOCK_LEN;
946     break;
947 
948   case MODE0:
949     b = MODE0_BLOCK_LEN;
950     break;
951 
952   case MODE1:
953     b = MODE1_BLOCK_LEN;
954     break;
955 
956   case MODE2:
957   case MODE2_FORM_MIX:
958     b = MODE2_BLOCK_LEN;
959     break;
960 
961   case MODE2_FORM1:
962     b = MODE2_FORM1_DATA_LEN;
963     break;
964 
965   case MODE2_FORM2:
966     b = MODE2_FORM2_DATA_LEN;
967     break;
968   }
969 
970   b += subChannelSize(sm);
971 
972   return b;
973 }
974 
mode2String(Mode m)975 const char *TrackData::mode2String(Mode m)
976 {
977   const char *ret = NULL;
978 
979   switch (m) {
980   case AUDIO:
981     ret = "AUDIO";
982     break;
983 
984   case MODE0:
985     ret = "MODE0";
986     break;
987 
988   case MODE1:
989     ret = "MODE1";
990     break;
991 
992   case MODE1_RAW:
993     ret = "MODE1_RAW";
994     break;
995 
996   case MODE2:
997     ret = "MODE2";
998     break;
999 
1000   case MODE2_RAW:
1001     ret = "MODE2_RAW";
1002     break;
1003 
1004   case MODE2_FORM1:
1005     ret = "MODE2_FORM1";
1006     break;
1007 
1008   case MODE2_FORM2:
1009     ret = "MODE2_FORM2";
1010     break;
1011 
1012   case MODE2_FORM_MIX:
1013     ret = "MODE2_FORM_MIX";
1014     break;
1015   }
1016 
1017   return ret;
1018 }
1019 
subChannelMode2String(SubChannelMode m)1020 const char *TrackData::subChannelMode2String(SubChannelMode m)
1021 {
1022   const char *ret = NULL;
1023 
1024   switch (m) {
1025   case SUBCHAN_NONE:
1026     ret = "";
1027     break;
1028 
1029   case SUBCHAN_RW:
1030     ret = "RW";
1031     break;
1032 
1033   case SUBCHAN_RW_RAW:
1034     ret = "RW_RAW";
1035     break;
1036   }
1037 
1038   return ret;
1039 }
1040 
TrackDataReader(const TrackData * d)1041 TrackDataReader::TrackDataReader(const TrackData *d)
1042 {
1043   trackData_ = d;
1044 
1045   open_ = 0;
1046   fd_ = -1;
1047   readPos_ = 0;
1048   headerLength_ = 0;
1049   readUnderRunMsgGiven_ = 0;
1050 }
1051 
~TrackDataReader()1052 TrackDataReader::~TrackDataReader()
1053 {
1054   if (open_) {
1055     closeData();
1056   }
1057 
1058   trackData_ = NULL;
1059 }
1060 
init(const TrackData * d)1061 void TrackDataReader::init(const TrackData *d)
1062 {
1063   if (open_) {
1064     closeData();
1065   }
1066 
1067   trackData_ = d;
1068 }
1069 
1070 // initiates reading audio data, an audio data file is opened
1071 // return: 0: OK
1072 //         1: file could not be opened
1073 //         2: could not seek to start position
openData()1074 int TrackDataReader::openData()
1075 {
1076   assert(open_ == 0);
1077   assert(trackData_ != NULL);
1078 
1079   if (trackData_->type_ == TrackData::DATAFILE) {
1080     if (trackData_->mode_ == TrackData::AUDIO) {
1081       long headerLength = 0;
1082 
1083       if (trackData_->fileType_ != TrackData::WAVE &&
1084           trackData_->fileType_ != TrackData::RAW) {
1085         log_message(-2, "Cannot open audio file \"%s\": unsupported format",
1086                 trackData_->filename_);
1087         return 1;
1088       }
1089 
1090 #ifdef __CYGWIN__
1091       if ((fd_ = open(trackData_->filename_, O_RDONLY | O_BINARY)) < 0)
1092 #else
1093       if ((fd_ = open(trackData_->filename_, O_RDONLY)) < 0)
1094 #endif
1095       {
1096 	log_message(-2, "Cannot open audio file \"%s\": %s", trackData_->filename_,
1097 		strerror(errno));
1098 	return 1;
1099       }
1100 
1101       if (trackData_->fileType_ == TrackData::WAVE) {
1102 	if (TrackData::waveLength(trackData_->filename_, trackData_->offset_,
1103 				  &headerLength) != 0) {
1104 	  log_message(-2, "%s: Unacceptable WAVE file.", trackData_->filename_);
1105 	  return 1;
1106 	}
1107       }
1108 
1109       if (lseek(fd_, trackData_->offset_ + headerLength + (trackData_->startPos_ * sizeof(Sample)), SEEK_SET) < 0) {
1110 	log_message(-2, "Cannot seek in audio file \"%s\": %s",
1111 		trackData_->filename_, strerror(errno));
1112 	return 2;
1113       }
1114 
1115       headerLength_ = headerLength;
1116     }
1117     else {
1118       // data mode
1119       headerLength_ = 0;
1120 
1121 #ifdef __CYGWIN__
1122       if ((fd_ = open(trackData_->filename_, O_RDONLY | O_BINARY)) < 0)
1123 #else
1124       if ((fd_ = open(trackData_->filename_, O_RDONLY)) < 0)
1125 #endif
1126       {
1127 	log_message(-2, "Cannot open data file \"%s\": %s", trackData_->filename_,
1128 		strerror(errno));
1129 	return 1;
1130       }
1131 
1132       if (trackData_->offset_ > 0) {
1133 	if (lseek(fd_, trackData_->offset_ , SEEK_SET) < 0) {
1134 	  log_message(-2, "Cannot seek to offset %ld in file \"%s\": %s",
1135 		  trackData_->offset_, trackData_->filename_, strerror(errno));
1136 	  return 2;
1137 	}
1138       }
1139     }
1140   }
1141   else if (trackData_->type_ == TrackData::FIFO) {
1142 #ifdef __CYGWIN__
1143     if ((fd_ = open(trackData_->filename_, O_RDONLY | O_BINARY)) < 0)
1144 #else
1145     if ((fd_ = open(trackData_->filename_, O_RDONLY)) < 0)
1146 #endif
1147     {
1148       log_message(-2, "Cannot open FIFO \"%s\": %s", trackData_->filename_,
1149               strerror(errno));
1150 	return 1;
1151     }
1152     headerLength_ = 0;
1153   }
1154   else if (trackData_->type_ == TrackData::STDIN) {
1155     headerLength_ = 0;
1156     fd_ = fileno(stdin);
1157   }
1158 
1159   readPos_ = 0;
1160   open_ = 1;
1161   readUnderRunMsgGiven_ = 0;
1162 
1163   return 0;
1164 }
1165 
1166 // ends reading audio data, an audio data file is closed
closeData()1167 void TrackDataReader::closeData()
1168 {
1169   if (open_ != 0) {
1170     if (trackData_->type_ == TrackData::DATAFILE ||
1171 	trackData_->type_ == TrackData::FIFO) {
1172       close(fd_);
1173     }
1174 
1175     fd_ = -1;
1176     open_ = 0;
1177     readPos_ = 0;
1178   }
1179 }
1180 
1181 // fills 'buffer' with 'len' samples (in case of audio mode) or with 'len'
1182 // bytes of data (for all other modes)
1183 // return: number of samples written to buffer
readData(Sample * buffer,long len)1184 long TrackDataReader::readData(Sample *buffer, long len)
1185 {
1186   long readLen = 0;
1187 
1188   assert(open_ != 0);
1189 
1190   if (len == 0) {
1191     return 0;
1192   }
1193 
1194   if (readPos_ + len > trackData_->length()) {
1195     if ((len = trackData_->length() - readPos_) <= 0) {
1196       return 0;
1197     }
1198   }
1199 
1200   switch (trackData_->type_) {
1201   case TrackData::ZERODATA:
1202     if (trackData_->audioCutMode())
1203       memset(buffer, 0, len * sizeof(Sample));
1204     else
1205       memset(buffer, 0, len);
1206 
1207     readLen = len;
1208     break;
1209 
1210   case TrackData::STDIN:
1211   case TrackData::DATAFILE:
1212   case TrackData::FIFO:
1213     if (trackData_->audioCutMode()) {
1214       readLen = fullRead(fd_, buffer, len * sizeof(Sample));
1215 
1216       if (readLen < 0) {
1217 	log_message(-2, "Read error while reading audio data from file \"%s\": %s",
1218 		trackData_->filename_, strerror(errno));
1219       }
1220       else if (readLen != (long)(len * sizeof(Sample))) {
1221 	long pad = len * sizeof(Sample) - readLen;
1222 
1223 	if (readUnderRunMsgGiven_ == 0) {
1224 	  log_message(-1, "Could not read expected amount of audio data from file \"%s\".", trackData_->filename_);
1225 	  log_message(-1, "Padding with zeros.");
1226 
1227 	  readUnderRunMsgGiven_ = 1;
1228 	}
1229 
1230 	// Adding zeros to the 'buffer'
1231 	memset(buffer + readLen, 0, pad);
1232 	readLen = len;
1233       }
1234       else {
1235 	readLen = len;
1236       }
1237     }
1238     else {
1239       readLen = fullRead(fd_, buffer, len);
1240       if (readLen < 0) {
1241 	log_message(-2, "Read error while reading data from file \"%s\": %s",
1242 		trackData_->filename_, strerror(errno));
1243       }
1244       else if (readLen != len) {
1245 	log_message(-2, "Could not read expected amount of data from file \"%s\".",
1246 		trackData_->filename_);
1247 	readLen = -1;
1248       }
1249     }
1250     break;
1251   }
1252 
1253   if (readLen > 0) {
1254     if (trackData_->mode_ == TrackData::AUDIO &&
1255 	trackData_->subChannelMode_ == TrackData::SUBCHAN_NONE) {
1256       int swap = 0;
1257 
1258       if (trackData_->fileType_ == TrackData::WAVE) {
1259 	// WAVE files contain little endian samples
1260 	swap = 1;
1261       }
1262 
1263       if (trackData_->swapSamples_)
1264 	swap = !swap;
1265 
1266       if (swap) {
1267 	// swap samples
1268 	if (trackData_->audioCutMode())
1269 	  swapSamples(buffer, readLen);
1270 	else
1271 	  swapSamples(buffer, readLen / sizeof(Sample));
1272       }
1273     }
1274 
1275     readPos_ += readLen;
1276   }
1277 
1278   return readLen;
1279 }
1280 
1281 // Seeks to specified sample.
1282 // return: 0: OK
1283 //        10: sample out of range
seekSample(unsigned long sample)1284 int TrackDataReader::seekSample(unsigned long sample)
1285 {
1286   assert(open_ != 0);
1287 
1288   if (sample >= trackData_->length())
1289     return 10;
1290 
1291   if (trackData_->type_ == TrackData::DATAFILE) {
1292     if (trackData_->audioCutMode()) {
1293       // 'sample' has samples as unit
1294       if (lseek(fd_,
1295 		trackData_->offset_ + headerLength_ +
1296 		(sample * sizeof(Sample)) +
1297 		(trackData_->startPos_ * sizeof(Sample)),
1298 		SEEK_SET) < 0) {
1299 	log_message(-2, "Cannot seek in audio file \"%s\": %s",
1300 		trackData_->filename_, strerror(errno));
1301 	return 10;
1302       }
1303     }
1304     else {
1305       // 'sample' has byte as unit
1306       if (lseek(fd_, trackData_->offset_ + headerLength_ + sample,
1307 		SEEK_SET) < 0) {
1308 	log_message(-2, "Cannot seek in audio file \"%s\": %s",
1309 		trackData_->filename_, strerror(errno));
1310 	return 10;
1311       }
1312     }
1313   }
1314 
1315   readPos_ = sample;
1316 
1317   return 0;
1318 }
1319 
1320 // Returns number of bytes that are still available for reading.
readLeft() const1321 unsigned long TrackDataReader::readLeft() const
1322 {
1323   assert(open_ != 0);
1324   assert(trackData_ != NULL);
1325 
1326   return trackData_->length() - readPos_;
1327 }
1328