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 <assert.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <unistd.h>
28
29 #include <fstream>
30 #include <iostream>
31 #include <sstream>
32
33 #include "Toc.h"
34 #include "util.h"
35 #include "log.h"
36 #include "TrackDataList.h"
37 #include "CdTextItem.h"
38 #include "CueParser.h"
39 #include "FormatConverter.h"
40
41 #ifdef UNIXWARE
42 extern "C" {
43 extern int strcasecmp(const char *, const char *);
44 }
45 #endif
46
47 extern Toc *parseToc(FILE *fp, const char *filename);
48
Toc()49 Toc::Toc() : length_(0)
50 {
51 tocType_ = CD_DA;
52 nofTracks_ = 0; firstTrackNo_ = 0;
53 tracks_ = lastTrack_ = NULL;
54
55 catalogValid_ = 0;
56 }
57
58 // copy constructor
Toc(const Toc & obj)59 Toc::Toc(const Toc &obj) : length_(0), cdtext_(obj.cdtext_)
60 {
61 tocType_ = obj.tocType_;
62
63 if ((catalogValid_ = obj.catalogValid_))
64 memcpy(catalog_, obj.catalog_, 13);
65
66 nofTracks_ = 0;
67 firstTrackNo_ = obj.firstTrackNo_;
68 tracks_ = lastTrack_ = NULL;
69
70 // copy all tracks
71 TrackIterator itr(&obj);
72 const Track *trun;
73
74 for (trun = itr.first(); trun != NULL; trun = itr.next())
75 append(trun);
76 }
77
78
~Toc()79 Toc::~Toc()
80 {
81 TrackEntry *run = tracks_;
82 TrackEntry *next;
83
84 while (run != NULL) {
85 next = run->next;
86 delete run->track;
87 delete run;
88 run = next;
89 }
90 }
91
92
93 // appends track
94 // return: 0: OK
95 // 1: first track contains pregap
append(const Track * t)96 int Toc::append(const Track *t)
97 {
98 if (tracks_ == NULL) {
99 tracks_ = lastTrack_ = new TrackEntry;
100 }
101 else {
102 lastTrack_->next = new TrackEntry;
103 lastTrack_->next->pred = lastTrack_;
104 lastTrack_ = lastTrack_->next;
105 }
106
107 lastTrack_->track = new Track(*t);
108 nofTracks_ += 1;
109
110 update();
111 return 0;
112 }
113
update()114 void Toc::update()
115 {
116 TrackEntry *run;
117 long length = 0; // length of disc in blocks
118 long tlength; // length of single track in blocks
119 int tnum;
120
121 for (run = tracks_, tnum = 1; run != NULL; run = run->next, tnum++) {
122 tlength = run->track->length().lba();
123
124 run->absStart = Msf(length);
125 run->start = Msf(length + run->track->start().lba());
126 run->end = Msf(length + tlength);
127 run->trackNr = tnum;
128
129 length += tlength;
130 }
131
132 length_ = Msf(length);
133 }
134
135
136
read(const char * filename)137 Toc *Toc::read(const char *filename)
138 {
139 FILE *fp;
140 Toc *ret;
141 const char *p;
142
143 if ((fp = fopen(filename, "r")) == NULL) {
144 log_message(-2, "Cannot open toc file '%s' for reading: %s",
145 filename, strerror(errno));
146 return NULL;
147 }
148
149 if ((p = strrchr(filename, '.')) != NULL && strcasecmp(p, ".cue") == 0)
150 ret = parseCue(fp, filename);
151 else
152 ret = parseToc(fp, filename);
153
154 fclose(fp);
155
156 return ret;
157 }
158
resolveFilenames(const char * filename)159 bool Toc::resolveFilenames(const char* filename)
160 {
161 // Resolve all relative filenames to absoluate paths wrt to the toc
162 // file current directory.
163 std::string path = filename;
164 path = path.substr(0, path.rfind('/'));
165 if (path.empty()) path = ".";
166
167 for (TrackEntry* t = tracks_; t != NULL; t = t->next)
168 if (!t->track->resolveFilename(path.c_str()))
169 return false;
170
171 return true;
172 }
173
174 // Writes toc to file with given name.
175 // Return: 0: OK
176 // 1: error occured
177
write(const char * filename) const178 int Toc::write(const char *filename) const
179 {
180 assert(filename != NULL);
181 assert(*filename != 0);
182
183 std::ofstream out(filename);
184
185 if (!out) {
186 log_message(-2, "Cannot open file \"%s\" for writing: %s", filename,
187 strerror(errno));
188 return 1;
189 }
190
191 print(out);
192
193 return 0;
194 }
195
write(int fd,bool conversions) const196 bool Toc::write(int fd, bool conversions) const
197 {
198 assert(fd);
199
200 std::ostringstream oss(std::ostringstream::out);
201 print(oss, conversions);
202
203 std::string ossstr = oss.str();
204 const char* content = ossstr.c_str();
205 int written = ::write(fd, content, strlen(content));
206
207 return (written >= 0);
208 }
209
check() const210 int Toc::check() const
211 {
212 TrackEntry *t;
213 int trackNr;
214 int ret = 0;
215
216 for (t = tracks_, trackNr = 1; t != NULL; t = t->next, trackNr++) {
217 ret |= t->track->check(trackNr);
218 }
219
220 return ret;
221 }
222
recomputeLength()223 bool Toc::recomputeLength()
224 {
225 for (TrackEntry* t = tracks_; t; t = t->next) {
226 if (!t->track->recomputeLength())
227 return false;
228 }
229
230 update();
231 return true;
232 }
233
234 // Sets catalog number. 's' must be a string of 13 digits.
235 // return: 0: OK
236 // 1: illegal catalog string
catalog(const char * s)237 int Toc::catalog(const char *s)
238 {
239 int i;
240
241 if (s == NULL) {
242 catalogValid_ = 0;
243 return 0;
244 }
245
246 if (strlen(s) != 13) {
247 return 1;
248 }
249
250 for (i = 0; i < 13; i++) {
251 if (!isdigit(s[i]))
252 return 1;
253 }
254
255 for (i = 0; i < 13; i++)
256 catalog_[i] = s[i] - '0';
257
258 catalogValid_ = 1;
259
260 return 0;
261 }
262
263
catalog() const264 const char *Toc::catalog() const
265 {
266 static char buf[14];
267 int i;
268
269 if (!catalogValid_)
270 return NULL;
271
272 for (i = 0; i < 13; i++)
273 buf[i] = catalog_[i] + '0';
274
275 buf[13] = 0;
276
277 return buf;
278 }
279
280 // writes contents in TOC file syntax
print(std::ostream & out,bool conversions) const281 void Toc::print(std::ostream &out, bool conversions) const
282 {
283 int i;
284 TrackEntry *t;
285
286 out << tocType2String(tocType()) << "\n\n";
287
288 if (catalogValid()) {
289 out << "CATALOG \"";
290 for (i = 0; i < 13; i++) {
291 out << (char)(catalog(i) + '0');
292 }
293 out << "\"" << std::endl;
294 }
295
296 cdtext_.print(0, out);
297
298 for (t = tracks_, i = 1; t != NULL; t = t->next, i++) {
299 out << "\n// Track " << i << "\n";
300 t->track->print(out, conversions);
301 out << std::endl;
302 }
303 }
304
convertFilesToWav()305 bool Toc::convertFilesToWav()
306 {
307 FormatSupport::Status status = formatConverter.convert(this);
308
309 return (status == FormatSupport::FS_SUCCESS);
310 }
311
collectFiles(std::set<std::string> & set)312 void Toc::collectFiles(std::set<std::string>& set)
313 {
314 for (TrackEntry* t = tracks_; t != NULL; t = t->next)
315 t->track->collectFiles(set);
316 }
317
markFileConversion(const char * src,const char * dst)318 void Toc::markFileConversion(const char* src, const char* dst)
319 {
320 for (TrackEntry* t = tracks_; t != NULL; t = t->next)
321 t->track->markFileConversion(src, dst);
322 }
323
324 // find track entry that contains given sample number
325 // return: found track entry or 'NULL' if sample is out of range
findTrack(unsigned long sample) const326 Toc::TrackEntry *Toc::findTrack(unsigned long sample) const
327 {
328 TrackEntry *run;
329
330 for (run = tracks_; run != NULL; run = run->next) {
331 if (sample < run->end.samples())
332 return run;
333 }
334
335 return NULL;
336 }
337
338 // find track with given number
339 // return: found track entry or 'NULL' if 'trackNr' is out of range
findTrackByNumber(int trackNr) const340 Toc::TrackEntry *Toc::findTrackByNumber(int trackNr) const
341 {
342 TrackEntry *run;
343
344 for (run = tracks_; run != NULL; run = run->next) {
345 if (run->trackNr == trackNr)
346 return run;
347 }
348
349 return NULL;
350 }
351
getTrack(int trackNr)352 Track *Toc::getTrack(int trackNr)
353 {
354 TrackEntry *ent = findTrackByNumber(trackNr);
355
356 if (ent != NULL)
357 return ent->track;
358 else
359 return NULL;
360 }
361
362 // Moves specified track/index position to given LBA if possible.
363 // return: 0: OK
364 // 1: Cannot move pre-gap of first track
365 // 2: specified track/index position not found in toc
366 // 3: 'lba' is an illegal position for track/index mark
367 // 4: resulting track length would be short than 4 seconds
368 // 5: cannot cross track/index boundaries
369 // 6: cannot modify data track
moveTrackMarker(int trackNr,int indexNr,long lba)370 int Toc::moveTrackMarker(int trackNr, int indexNr, long lba)
371 {
372 TrackEntry *act;
373
374 if (trackNr == 1 && indexNr == 0)
375 return 1;
376
377 if ((act = findTrackByNumber(trackNr)) == NULL) {
378 return 2;
379 }
380
381 if (indexNr <= 1 &&
382 (act->track->type() != TrackData::AUDIO ||
383 act->track->subChannelType() != TrackData::SUBCHAN_NONE))
384 return 6;
385
386 if ((indexNr == 0 || (indexNr == 1 && act->track->start().lba() == 0)) &&
387 act->pred != NULL &&
388 (act->pred->track->type() != TrackData::AUDIO ||
389 act->track->subChannelType() != TrackData::SUBCHAN_NONE))
390 return 6;
391
392
393 if (indexNr == 0 && act->track->start().lba() == 0)
394 return 2;
395
396 if (indexNr > 1 && indexNr - 2 >= act->track->nofIndices())
397 return 2;
398
399 if (lba < 0 || lba >= length().lba())
400 return 3;
401
402 if ((indexNr == 1 && (act->track->start().lba() > 0 || trackNr == 1)) ||
403 indexNr > 1) {
404 // change track/index position within track
405 if (indexNr == 1) {
406 if (lba > act->end.lba() - 4 * 75)
407 return 4;
408
409 if (lba <= act->absStart.lba() && trackNr > 1)
410 return 3;
411 }
412 else {
413 if (lba - act->absStart.lba() <= 0)
414 return 3;
415 }
416
417 switch (act->track->moveIndex(indexNr, lba - act->absStart.lba())) {
418 case 1:
419 return 4;
420 case 2:
421 return 5;
422 }
423 }
424 else {
425 // move track start position, we need to shift audio data from
426 // on track to the other
427
428 // 'act->pred' is always non NULL in this case because track 1 is
429 // exhaustively handled above
430
431 TrackDataList *dataList; // audio data that is removed from one track
432
433 if (lba < act->absStart.lba()) {
434 // move to the left
435
436 if (lba < act->pred->start.lba() + 4 * 75)
437 return 4;
438
439 dataList =
440 act->pred->track->removeToEnd(Msf(lba - act->pred->absStart.lba()).samples());
441 act->track->prependTrackData(dataList);
442 delete dataList;
443
444 // adjust start of track
445 act->track->start(Msf(act->start.lba() - lba));
446
447 if (indexNr == 1)
448 act->track->moveIndex(1, 0); // remove intermediate pre-gap
449 }
450 else if (lba > act->absStart.lba()) {
451 // move to the right
452
453 if (indexNr == 1) {
454 // introduce an intermediate pre-gap that adjusts the index
455 // increments
456 switch (act->track->moveIndex(1, lba - act->absStart.lba())) {
457 case 1:
458 return 4;
459 case 2:
460 return 5;
461 }
462
463 // remove intermediate pre-gap
464 act->track->start(Msf(0));
465 }
466 else {
467 // adjust pre-gap
468 if (lba >= act->start.lba())
469 return 5;
470
471 act->track->start(Msf(act->start.lba() - lba));
472 }
473
474 dataList =
475 act->track->removeFromStart(Msf(lba - act->absStart.lba()).samples());
476 act->pred->track->appendTrackData(dataList);
477 delete dataList;
478
479 }
480 }
481
482 update();
483 checkConsistency();
484
485 return 0;
486 }
487
remove(TrackEntry * ent)488 void Toc::remove(TrackEntry *ent)
489 {
490 if (ent->pred != NULL)
491 ent->pred->next = ent->next;
492 else
493 tracks_ = ent->next;
494
495 if (ent->next != NULL)
496 ent->next->pred = ent->pred;
497 else
498 lastTrack_ = ent->pred;
499
500 ent->pred = ent->next = NULL;
501 ent->track = NULL;
502 delete ent;
503 }
504
505 // Removes specified track marker.
506 // return: 0: OK
507 // 1: cannot remove first track
508 // 2: specified track/index position not found in toc
509 // 3: cannot modify a data track
removeTrackMarker(int trackNr,int indexNr)510 int Toc::removeTrackMarker(int trackNr, int indexNr)
511 {
512 TrackEntry *act;
513
514 if (trackNr == 1 && indexNr == 1)
515 return 1;
516
517 if ((act = findTrackByNumber(trackNr)) == NULL) {
518 return 2;
519 }
520
521 if ((act->track->type() != TrackData::AUDIO ||
522 act->track->subChannelType() != TrackData::SUBCHAN_NONE) &&
523 indexNr <= 1)
524 return 3;
525
526 if (act->pred != NULL &&
527 (act->pred->track->type() != TrackData::AUDIO ||
528 act->track->subChannelType() != TrackData::SUBCHAN_NONE) &&
529 indexNr <= 1)
530 return 3;
531
532 if (trackNr == 1 && indexNr == 0) {
533 // pre-gap of first track
534
535 if (act->start.lba() == 0)
536 return 2; // no pre-gap
537
538 act->track->start(Msf(0));
539 }
540 else if (indexNr > 1) {
541 // index increment
542 if (act->track->removeIndex(indexNr - 2) != 0)
543 return 2;
544 }
545 else if (indexNr == 0) {
546 // remove pre-gap, audio data of pre-gap is appended to previous track
547 unsigned long len = act->track->start().samples();
548
549 if (len == 0)
550 return 2; // track has no pre-gap
551
552 act->track->start(Msf(0));
553 TrackDataList *dataList = act->track->removeFromStart(len);
554 act->pred->track->appendTrackData(dataList);
555 delete dataList;
556 }
557 else {
558 // index == 1, remove track completely
559
560 act->pred->track->appendTrackData(act->track);
561
562 Track *store = act->track;
563 remove(act);
564 delete store;
565
566 nofTracks_--;
567 }
568
569 update();
570 checkConsistency();
571
572 return 0;
573 }
574
575 // Adds index increment at given LBA.
576 // return: 0: OK
577 // 1: LBA out of range
578 // 2: cannot add index at this position
579 // 3: more than 98 index increments
addIndexMarker(long lba)580 int Toc::addIndexMarker(long lba)
581 {
582 TrackEntry *act = findTrack(Msf(lba).samples());
583
584 if (act == NULL)
585 return 1;
586
587 if (lba <= act->start.lba())
588 return 2;
589
590 switch (act->track->addIndex(Msf(lba - act->start.lba()))) {
591 case 1:
592 return 3;
593 case 2:
594 return 2;
595 }
596
597 return 0;
598 }
599
600 // Adds a track marker at given LBA.
601 // return: 0: OK
602 // 1: LBA out of range
603 // 2: cannot add track at this position
604 // 3: resulting track would be shorter than 4 seconds
605 // 4: previous track would be short than 4 seconds
606 // 5: cannot modify a data track
addTrackMarker(long lba)607 int Toc::addTrackMarker(long lba)
608 {
609 TrackEntry *act = findTrack(Msf(lba).samples());
610
611 if (act == NULL)
612 return 1;
613
614 if (act->track->type() != TrackData::AUDIO)
615 return 5;
616
617 if (act->track->subChannelType() != TrackData::SUBCHAN_NONE)
618 return 5;
619
620 if (lba <= act->start.lba())
621 return 2;
622
623 if (lba - act->start.lba() < 4 * 75)
624 return 4;
625
626 if (act->end.lba() - lba < 4 * 75)
627 return 3;
628
629
630 TrackDataList *dataList =
631 act->track->removeToEnd(Msf(lba - act->absStart.lba()).samples());
632
633 Track *t = new Track(act->track->type(), act->track->subChannelType());
634 t->appendTrackData(dataList);
635 delete dataList;
636
637 TrackEntry *ent = new TrackEntry;
638 ent->track = t;
639
640 ent->next = act->next;
641 if (ent->next != NULL)
642 ent->next->pred = ent;
643
644 ent->pred = act;
645 act->next = ent;
646
647 if (act == lastTrack_)
648 lastTrack_ = ent;
649
650 nofTracks_++;
651
652 update();
653 checkConsistency();
654
655 return 0;
656 }
657
658
659 // Adds pre-gap add given LBA.
660 // return: 0: OK
661 // 1: LBA out of range
662 // 2: cannot add pre-gap at this point
663 // 3: actual track would be shorter than 4 seconds
664 // 4: cannot modify a data track
addPregap(long lba)665 int Toc::addPregap(long lba)
666 {
667 TrackEntry *act = findTrack(Msf(lba).samples());
668
669 if (act == NULL)
670 return 1;
671
672 if (act->track->type() != TrackData::AUDIO)
673 return 4;
674
675 if (act->track->subChannelType() != TrackData::SUBCHAN_NONE)
676 return 4;
677
678 if (act->next == NULL)
679 return 2; // no next track where we could add pre-gap
680
681 if (act->next->track->type() != act->track->type() ||
682 act->next->track->subChannelType() != act->track->subChannelType())
683 return 4;
684
685 if (lba <= act->start.lba())
686 return 2;
687
688 if (act->next->track->start().lba() != 0)
689 return 2; // track has already a pre-gap
690
691 if (lba - act->start.lba() < 4 * 75)
692 return 3;
693
694 TrackDataList *dataList =
695 act->track->removeToEnd(Msf(lba - act->absStart.lba()).samples());
696 act->next->track->prependTrackData(dataList);
697 delete dataList;
698
699 act->next->track->start(Msf(act->next->start.lba() - lba));
700
701 update();
702 checkConsistency();
703
704 return 0;
705 }
706
fixLengths()707 void Toc::fixLengths()
708 {
709 TrackEntry* te;
710 int i;
711
712 for (i = 0 , te = tracks_; te; te = te->next, i++) {
713 printf("%d : Track %d\n", i, te->trackNr);
714 }
715 }
716
checkConsistency()717 void Toc::checkConsistency()
718 {
719 TrackEntry *run, *last = NULL;
720 long cnt = 0;
721
722 for (run = tracks_; run != NULL; last = run, run = run->next) {
723 cnt++;
724 if (run->pred != last)
725 log_message(-3, "Toc::checkConsistency: wrong pred pointer.");
726
727 run->track->checkConsistency();
728 }
729
730 if (last != lastTrack_)
731 log_message(-3, "Toc::checkConsistency: wrong last pointer.");
732
733 if (cnt != nofTracks_)
734 log_message(-3, "Toc::checkConsistency: wrong sub track counter.");
735 }
736
737
738 // Appends a track with given audio data. 'start' is filled with
739 // first LBA of new track, 'end' is filled with last LBA + 1 of new track.
appendTrack(const TrackDataList * list,long * start,long * end)740 void Toc::appendTrack(const TrackDataList *list, long *start, long *end)
741 {
742 Track t(TrackData::AUDIO, TrackData::SUBCHAN_NONE);
743 const TrackData *run;
744
745 for (run = list->first(); run != NULL; run = list->next())
746 t.append(SubTrack(SubTrack::DATA, *run));
747
748 // ensure that track lasts at least 4 seconds
749 unsigned long minTime = 4 * 75 * SAMPLES_PER_BLOCK; // 4 seconds
750 unsigned long len = list->length();
751
752 if (len < minTime)
753 t.append(SubTrack(SubTrack::DATA, TrackData(minTime - len)));
754
755 *start = length().lba();
756
757 append(&t);
758 checkConsistency();
759
760 *end = length().lba();
761 }
762
763 // Appends given audio data to last track. If no track exists 'appendTrack'
764 // will be called. 'start' and 'end' is filled with position of modified
765 // region.
766 // Return: 0: OK
767 // 1: cannot modify a data track
appendTrackData(const TrackDataList * list,long * start,long * end)768 int Toc::appendTrackData(const TrackDataList *list, long *start, long *end)
769 {
770 const TrackData *run;
771
772 if (lastTrack_ == NULL) {
773 appendTrack(list, start, end);
774 return 0;
775 }
776
777 if (lastTrack_->track->type() != TrackData::AUDIO)
778 return 1;
779
780 if (lastTrack_->track->subChannelType() != TrackData::SUBCHAN_NONE)
781 return 1;
782
783
784 *start = length().lba();
785
786 for (run = list->first(); run != NULL; run = list->next())
787 lastTrack_->track->append(SubTrack(SubTrack::DATA, *run));
788
789 update();
790 checkConsistency();
791
792 *end = length().lba();
793
794 return 0;
795 }
796
797 // Removes specified range of samples from a single track.
798 // Return: 0: OK
799 // 1: samples range covers more than one track
800 // 2: cannot modify a data track
801 // 3: illegal 'start' position
removeTrackData(unsigned long start,unsigned long end,TrackDataList ** list)802 int Toc::removeTrackData(unsigned long start, unsigned long end,
803 TrackDataList **list)
804 {
805 TrackEntry *tent = findTrack(start);
806
807 if (tent == NULL)
808 return 3;
809
810 if (tent->track->type() != TrackData::AUDIO)
811 return 2;
812
813 if (tent->track->subChannelType() != TrackData::SUBCHAN_NONE)
814 return 2;
815
816 if (tent != findTrack(end))
817 return 1;
818
819 *list = tent->track->removeTrackData(start - tent->absStart.samples(),
820 end - tent->absStart.samples());
821
822 update();
823 checkConsistency();
824
825 return 0;
826 }
827
828 // Inserts given track data at specified postion.
829 // Return: 0: OK
830 // 1: cannot modify a data track
831
insertTrackData(unsigned long pos,const TrackDataList * list)832 int Toc::insertTrackData(unsigned long pos, const TrackDataList *list)
833 {
834 TrackEntry *tent = findTrack(pos);
835
836 if (tent != NULL && tent->track->type() != TrackData::AUDIO)
837 return 1;
838
839 if (tent != NULL && tent->track->subChannelType() != TrackData::SUBCHAN_NONE)
840 return 1;
841
842
843 if (tent != NULL) {
844 tent->track->insertTrackData(pos - tent->absStart.samples(), list);
845
846 update();
847 checkConsistency();
848
849 return 0;
850 }
851 else {
852 long start, end;
853
854 return appendTrackData(list, &start, &end);
855 }
856
857 }
858
859
860 // Returns mode that should be used for lead-in. The mode of first track's
861 // first sub-track is used.
leadInMode() const862 TrackData::Mode Toc::leadInMode() const
863 {
864 const SubTrack *t;
865
866 if (tracks_ == NULL || (t = tracks_->track->firstSubTrack()) == NULL) {
867 // no track or track data available - return AUDIO in this case
868 return TrackData::AUDIO;
869 }
870
871 return t->mode();
872 }
873
874 // Returns mode that should be used for lead-out. The mode of last track's
875 // last sub-track is used
leadOutMode() const876 TrackData::Mode Toc::leadOutMode() const
877 {
878 const SubTrack *t;
879
880 if (lastTrack_ == NULL || (t = lastTrack_->track->lastSubTrack()) == NULL) {
881 // no track or track data available - return AUDIO in this case
882 return TrackData::AUDIO;
883 }
884
885 return t->mode();
886 }
887
tocType2String(TocType t)888 const char *Toc::tocType2String(TocType t)
889 {
890 const char *ret = NULL;
891 switch (t) {
892 case CD_DA:
893 ret = "CD_DA";
894 break;
895 case CD_ROM:
896 ret = "CD_ROM";
897 break;
898 case CD_I:
899 ret = "CD_I";
900 break;
901 case CD_ROM_XA:
902 ret = "CD_ROM_XA";
903 break;
904 }
905
906 return ret;
907 }
908
addCdTextItem(int trackNr,CdTextItem * item)909 void Toc::addCdTextItem(int trackNr, CdTextItem *item)
910 {
911 assert(trackNr >= 0 && trackNr <= 99);
912
913 if (trackNr == 0) {
914 cdtext_.add(item);
915 }
916 else {
917 TrackEntry *track = findTrackByNumber(trackNr);
918
919 if (track == NULL) {
920 log_message(-3, "addCdTextItem: Track %d is not available.", trackNr);
921 return;
922 }
923
924 track->track->addCdTextItem(item);
925 }
926 }
927
removeCdTextItem(int trackNr,CdTextItem::PackType type,int blockNr)928 void Toc::removeCdTextItem(int trackNr, CdTextItem::PackType type, int blockNr)
929 {
930 assert(trackNr >= 0 && trackNr <= 99);
931
932 if (trackNr == 0) {
933 cdtext_.remove(type, blockNr);
934 }
935 else {
936 TrackEntry *track = findTrackByNumber(trackNr);
937
938 if (track == NULL) {
939 log_message(-3, "addCdTextItem: Track %d is not available.", trackNr);
940 return;
941 }
942
943 track->track->removeCdTextItem(type, blockNr);
944 }
945 }
946
existCdTextBlock(int blockNr) const947 int Toc::existCdTextBlock(int blockNr) const
948 {
949 if (cdtext_.existBlock(blockNr))
950 return 1;
951
952 TrackEntry *run;
953
954 for (run = tracks_; run != NULL; run = run->next) {
955 if (run->track->existCdTextBlock(blockNr))
956 return 1;
957 }
958
959 return 0;
960 }
961
getCdTextItem(int trackNr,int blockNr,CdTextItem::PackType type) const962 const CdTextItem *Toc::getCdTextItem(int trackNr, int blockNr,
963 CdTextItem::PackType type) const
964 {
965 if (trackNr == 0) {
966 return cdtext_.getPack(blockNr, type);
967 }
968
969 TrackEntry *track = findTrackByNumber(trackNr);
970
971 if (track == NULL)
972 return NULL;
973
974 return track->track->getCdTextItem(blockNr, type);
975 }
976
cdTextLanguage(int blockNr,int lang)977 void Toc::cdTextLanguage(int blockNr, int lang)
978 {
979 cdtext_.language(blockNr, lang);
980
981 }
982
983
cdTextLanguage(int blockNr) const984 int Toc::cdTextLanguage(int blockNr) const
985 {
986 return cdtext_.language(blockNr);
987 }
988
989 // Check the consistency of the CD-TEXT data.
990 // Return: 0: OK
991 // 1: at least one warning occured
992 // 2: at least one error occured
checkCdTextData() const993 int Toc::checkCdTextData() const
994 {
995 TrackEntry *trun;
996 int err = 0;
997 int l;
998 int last;
999 int titleCnt, performerCnt, songwriterCnt, composerCnt, arrangerCnt;
1000 int messageCnt, isrcCnt, genreCnt;
1001 int languageCnt = 0;
1002
1003 genreCnt = 0;
1004
1005 // Check if language numbers are continuously used starting at 0
1006 for (l = 0, last = -1; l <= 7; l++) {
1007 if (cdTextLanguage(l) >= 0) {
1008 languageCnt++;
1009
1010 if (cdtext_.getPack(l, CdTextItem::CDTEXT_GENRE) != NULL)
1011 genreCnt++;
1012
1013 if (l - 1 != last) {
1014 if (last == -1)
1015 log_message(-2, "CD-TEXT: Language number %d: Language numbers must start at 0.", l);
1016 else
1017 log_message(-2, "CD-TEXT: Language number %d: Language numbers are not continuously used.", l);
1018
1019 if (err < 2)
1020 err = 2;
1021 }
1022
1023 last = l;
1024 }
1025 }
1026
1027 if (genreCnt > 0 && genreCnt != languageCnt) {
1028 log_message(-1, "CD-TEXT: %s field not defined for all languages.",
1029 CdTextItem::packType2String(1, CdTextItem::CDTEXT_GENRE));
1030 if (err < 1)
1031 err = 1;
1032 }
1033
1034
1035 for (l = 0, last = -1; l <= 7; l++) {
1036 if (cdTextLanguage(l) < 0)
1037 continue;
1038
1039 titleCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_TITLE) != NULL) ? 1 : 0;
1040 performerCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_PERFORMER) != NULL) ? 1 : 0;
1041 songwriterCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_SONGWRITER) != NULL) ? 1 : 0;
1042 composerCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_COMPOSER) != NULL) ? 1 : 0;
1043 arrangerCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_ARRANGER) != NULL) ? 1 : 0;
1044 messageCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_MESSAGE) != NULL) ? 1 : 0;
1045 isrcCnt = 0;
1046
1047 for (trun = tracks_; trun != NULL; trun = trun->next) {
1048 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_TITLE) != NULL)
1049 titleCnt++;
1050
1051 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_PERFORMER) != NULL)
1052 performerCnt++;
1053
1054 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_SONGWRITER) != NULL)
1055 songwriterCnt++;
1056
1057 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_COMPOSER) != NULL)
1058 composerCnt++;
1059
1060 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_ARRANGER) != NULL)
1061 arrangerCnt++;
1062
1063 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_MESSAGE) != NULL)
1064 messageCnt++;
1065
1066 if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_UPCEAN_ISRC) != NULL)
1067 isrcCnt++;
1068 }
1069
1070 if (titleCnt > 0 && titleCnt != nofTracks_ + 1) {
1071 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1072 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_TITLE));
1073 if (err < 2)
1074 err = 2;
1075 }
1076 else if (titleCnt == 0) {
1077 log_message(-1, "CD-TEXT: Language %d: %s field is not defined.",
1078 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_TITLE));
1079 if (err < 1)
1080 err = 1;
1081 }
1082
1083 if (performerCnt > 0 && performerCnt != nofTracks_ + 1) {
1084 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1085 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_PERFORMER));
1086 if (err < 2)
1087 err = 2;
1088 }
1089 else if (performerCnt == 0) {
1090 log_message(-1, "CD-TEXT: Language %d: %s field is not defined.",
1091 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_PERFORMER));
1092 if (err < 1)
1093 err = 1;
1094 }
1095
1096 if (songwriterCnt > 0 && songwriterCnt != nofTracks_ + 1) {
1097 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1098 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_SONGWRITER));
1099 if (err < 2)
1100 err = 2;
1101 }
1102
1103 if (composerCnt > 0 && composerCnt != nofTracks_ + 1) {
1104 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1105 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_COMPOSER));
1106 if (err < 2)
1107 err = 2;
1108 }
1109
1110 if (arrangerCnt > 0 && arrangerCnt != nofTracks_ + 1) {
1111 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1112 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_ARRANGER));
1113 if (err < 2)
1114 err = 2;
1115 }
1116
1117 if (messageCnt > 0 && messageCnt != nofTracks_ + 1) {
1118 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1119 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_MESSAGE));
1120 if (err < 2)
1121 err = 2;
1122 }
1123
1124 if ((isrcCnt > 0 && isrcCnt != nofTracks_) ||
1125 (isrcCnt == 0 &&
1126 cdtext_.getPack(l, CdTextItem::CDTEXT_UPCEAN_ISRC) != NULL)) {
1127 log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks.",
1128 l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_UPCEAN_ISRC));
1129 if (err < 2)
1130 err = 2;
1131 }
1132 }
1133
1134 return err;
1135 }
1136
1137
trackSummary(int * nofAudioTracks,int * nofMode1Tracks,int * nofMode2Tracks) const1138 void Toc::trackSummary(int *nofAudioTracks, int *nofMode1Tracks,
1139 int *nofMode2Tracks) const
1140 {
1141 TrackEntry *run;
1142
1143 if (nofAudioTracks != NULL)
1144 *nofAudioTracks = 0;
1145
1146 if (nofMode1Tracks != NULL)
1147 *nofMode1Tracks = 0;
1148
1149 if (nofMode2Tracks != NULL)
1150 *nofMode2Tracks = 0;
1151
1152 for (run = tracks_; run != NULL; run = run->next) {
1153 switch (run->track->type()) {
1154 case TrackData::AUDIO:
1155 if (nofAudioTracks != NULL)
1156 *nofAudioTracks += 1;
1157 break;
1158
1159 case TrackData::MODE1:
1160 case TrackData::MODE1_RAW:
1161 if (nofMode1Tracks != NULL)
1162 *nofMode1Tracks += 1;
1163 break;
1164
1165 case TrackData::MODE2:
1166 case TrackData::MODE2_FORM1:
1167 case TrackData::MODE2_FORM2:
1168 case TrackData::MODE2_FORM_MIX:
1169 case TrackData::MODE2_RAW:
1170 if (nofMode2Tracks != NULL)
1171 *nofMode2Tracks += 1;
1172 break;
1173
1174 case TrackData::MODE0:
1175 break;
1176 }
1177 }
1178 }
1179
1180 // Class TrackIterator
TrackIterator(const Toc * t)1181 TrackIterator::TrackIterator(const Toc *t)
1182 {
1183 toc_ = t;
1184 iterator_ = NULL;
1185 }
1186
~TrackIterator()1187 TrackIterator::~TrackIterator()
1188 {
1189 toc_ = NULL;
1190 iterator_ = NULL;
1191 }
1192
find(int trackNr,Msf & start,Msf & end)1193 const Track *TrackIterator::find(int trackNr, Msf &start, Msf &end)
1194 {
1195 Track *t;
1196
1197 iterator_ = toc_->findTrackByNumber(trackNr);
1198
1199 if (iterator_ != NULL) {
1200 start = iterator_->start;
1201 end = iterator_->end;
1202 t = iterator_->track;
1203 iterator_ = iterator_->next;
1204 return t;
1205 }
1206
1207 return NULL;
1208 }
1209
find(unsigned long sample,Msf & start,Msf & end,int * trackNr)1210 const Track *TrackIterator::find(unsigned long sample, Msf &start, Msf &end,
1211 int *trackNr)
1212 {
1213 Track *t;
1214
1215 iterator_ = toc_->findTrack(sample);
1216
1217 if (iterator_ != NULL) {
1218 start = iterator_->start;
1219 end = iterator_->end;
1220 *trackNr = iterator_->trackNr;
1221 t = iterator_->track;
1222 iterator_ = iterator_->next;
1223 return t;
1224 }
1225
1226 return NULL;
1227 }
1228
first(Msf & start,Msf & end)1229 const Track *TrackIterator::first(Msf &start, Msf &end)
1230 {
1231 iterator_ = toc_->tracks_;
1232
1233 return next(start, end);
1234 }
1235
first()1236 const Track *TrackIterator::first()
1237 {
1238 Msf start, end;
1239
1240 return first(start, end);
1241 }
1242
next(Msf & start,Msf & end)1243 const Track *TrackIterator::next(Msf &start, Msf &end)
1244 {
1245 Track *t;
1246
1247 if (iterator_ != NULL) {
1248 start = iterator_->start;
1249 end = iterator_->end;
1250 t = iterator_->track;
1251 iterator_ = iterator_->next;
1252 return t;
1253 }
1254 else {
1255 return NULL;
1256 }
1257 }
1258
next()1259 const Track *TrackIterator::next()
1260 {
1261 Msf start, end;
1262
1263 return next(start, end);
1264 }
1265
1266 // Class TocReader
1267
TocReader(const Toc * t)1268 TocReader::TocReader(const Toc *t) : reader(NULL)
1269 {
1270 toc_ = t;
1271
1272 readTrack_ = NULL;
1273 readPos_ = 0;
1274 readPosSample_ = 0;
1275 open_ = 0;
1276 }
1277
~TocReader()1278 TocReader::~TocReader ()
1279 {
1280 if (open_) {
1281 closeData();
1282 }
1283
1284 toc_ = NULL;
1285 readTrack_ = NULL;
1286 }
1287
init(const Toc * t)1288 void TocReader::init(const Toc *t)
1289 {
1290 if (open_) {
1291 closeData();
1292 }
1293
1294 reader.init(NULL);
1295
1296 toc_ = t;
1297 readTrack_ = NULL;
1298 }
1299
openData()1300 int TocReader::openData()
1301 {
1302 int ret = 0;
1303
1304 assert(open_ == 0);
1305 assert(toc_ != NULL);
1306
1307 readTrack_ = toc_->tracks_;
1308 readPos_ = 0;
1309 readPosSample_ = 0;
1310
1311 reader.init(readTrack_->track);
1312
1313 if (readTrack_ != NULL) {
1314 ret = reader.openData();
1315 }
1316
1317 open_ = 1;
1318
1319 return ret;
1320 }
1321
closeData()1322 void TocReader::closeData()
1323 {
1324 if (open_ != 0) {
1325 reader.closeData();
1326
1327 readTrack_ = NULL;
1328 readPos_ = 0;
1329 open_ = 0;
1330 readPosSample_ = 0;
1331 }
1332 }
1333
1334 #if 0
1335 long TocReader::readData(long lba, char *buf, long len)
1336 {
1337 long n;
1338 long nread = 0;
1339
1340 assert(open_ != 0);
1341
1342 if (readPos_ + len > toc_->length_.lba()) {
1343 if ((len = toc_->length_.lba() - readPos_) <= 0) {
1344 return 0;
1345 }
1346 }
1347
1348 do {
1349 n = reader.readData(0, lba, buf + (nread * AUDIO_BLOCK_LEN), len);
1350
1351 if (n < 0) {
1352 return -1;
1353 }
1354
1355 lba += n;
1356
1357 if (n != len) {
1358 // skip to next track
1359 readTrack_ = readTrack_->next;
1360
1361 assert(readTrack_ != NULL);
1362
1363 reader.init(readTrack_->track);
1364 if (reader.openData() != 0) {
1365 return -1;
1366 }
1367 }
1368
1369 nread += n;
1370 len -= n;
1371 } while (len > 0);
1372
1373 readPos_ += nread;
1374
1375 return nread;
1376 }
1377 #endif
1378
1379 // seeks to specified sample (absolute position)
1380 // return: 0: OK
1381 // 10: sample position out of range
1382 // return codes from 'Track::openData()'
seekSample(unsigned long sample)1383 int TocReader::seekSample(unsigned long sample)
1384 {
1385 int ret;
1386
1387 assert(open_ != 0);
1388
1389 // find track that contains 'sample'
1390 Toc::TrackEntry *tr = toc_->findTrack(sample);
1391
1392 if (tr == NULL)
1393 return 10;
1394
1395 // open track if necessary
1396 if (tr != readTrack_) {
1397 readTrack_ = tr;
1398 reader.init(readTrack_->track);
1399
1400 if ((ret = reader.openData() != 0))
1401 return ret;
1402 }
1403
1404 assert(sample >= readTrack_->absStart.samples());
1405
1406 unsigned long offset = sample - readTrack_->absStart.samples();
1407
1408 // seek in track
1409 if ((ret = reader.seekSample(offset)) != 0)
1410 return ret;
1411
1412 readPosSample_ = sample;
1413
1414 return 0;
1415 }
1416
readSamples(Sample * buf,long len)1417 long TocReader::readSamples(Sample *buf, long len)
1418 {
1419 long n;
1420 long nread = 0;
1421
1422 assert(open_ != 0);
1423
1424 if (readPosSample_ + (unsigned long)len > toc_->length_.samples()) {
1425 if ((len = toc_->length_.samples() - readPosSample_) <= 0)
1426 return 0;
1427 }
1428
1429 do {
1430 n = reader.readSamples(buf + nread , len);
1431
1432 if (n < 0)
1433 return -1;
1434
1435 if (n != len) {
1436 // skip to next track
1437 readTrack_ = readTrack_->next;
1438 reader.init(readTrack_->track);
1439
1440 assert(readTrack_ != NULL);
1441
1442 if (reader.openData() != 0) {
1443 return -1;
1444 }
1445 }
1446
1447 nread += n;
1448 len -= n;
1449 } while (len > 0);
1450
1451 readPosSample_ += nread;
1452
1453 return nread;
1454 }
1455
curFilename()1456 const char* TocReader::curFilename()
1457 {
1458 return reader.curFilename();
1459 }
1460