1 /* cdrdao - write audio CD-Rs in disc-at-once mode
2 *
3 * Copyright (C) 1998-2002 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 "TocEdit.h"
21
22 #include <stddef.h>
23 #include <gnome.h>
24 #include <iostream>
25 #include <sstream>
26 #include <set>
27
28 #include "util.h"
29 #include "Toc.h"
30 #include "TocEditView.h"
31 #include "TrackData.h"
32 #include "TrackDataList.h"
33 #include "TrackDataScrap.h"
34
35 #include "guiUpdate.h"
36 #include "SampleManager.h"
37
TocEdit(Toc * t,const char * filename)38 TocEdit::TocEdit(Toc *t, const char *filename)
39 {
40 toc_ = NULL;
41 sampleManager_ = NULL;
42 filename_ = NULL;
43 trackDataScrap_ = NULL;
44 threadActive_ = false;
45 curState_ = TE_IDLE;
46 curConv_ = NULL;
47 cur_ = NULL;
48
49 updateLevel_ = 0;
50 editBlocked_ = false;
51
52 if (filename == NULL)
53 toc(t, "unnamed.toc");
54 else
55 toc(t, filename);
56 }
57
~TocEdit()58 TocEdit::~TocEdit()
59 {
60 if (toc_)
61 delete toc_;
62
63 if (sampleManager_)
64 delete sampleManager_;
65
66 if (filename_)
67 delete[] filename_;
68
69 if (trackDataScrap_)
70 delete trackDataScrap_;
71 }
72
toc(Toc * t,const char * filename)73 void TocEdit::toc(Toc *t, const char *filename)
74 {
75 if (toc_)
76 delete toc_;
77
78 if (t == NULL)
79 toc_ = new Toc;
80 else
81 toc_ = t;
82
83 if (filename != NULL) {
84 delete[] filename_;
85 filename_ = strdupCC(filename);
86 }
87
88 tocDirty_ = false;
89 editBlocked_ = false;
90
91 if (sampleManager_)
92 delete sampleManager_;
93 sampleManager_ = new SampleManager(588);
94
95 sampleManager_->setTocEdit(this);
96
97 if (toc_->length().samples() > 0) {
98
99 // First collect all filenames and queue their conversions to WAV
100 std::set<std::string> set;
101 toc_->collectFiles(set);
102 std::set<std::string>::iterator i = set.begin();
103 for (; i != set.end(); i++)
104 queueConversion((*i).c_str());
105
106 // Second, queue for toc scan.
107 unsigned long maxSample = toc_->length().samples() - 1;
108 queueScan(0, -1);
109 }
110
111 updateLevel_ = UPD_ALL;
112 }
113
toc() const114 Toc *TocEdit::toc() const
115 {
116 return toc_;
117 }
118
sampleManager()119 SampleManager *TocEdit::sampleManager()
120 {
121 return sampleManager_;
122 }
123
tocDirty(bool f)124 void TocEdit::tocDirty(bool f)
125 {
126 bool old = tocDirty_;
127
128 tocDirty_ = f;
129
130 if (old != tocDirty_)
131 updateLevel_ |= UPD_TOC_DIRTY;
132 }
133
blockEdit()134 void TocEdit::blockEdit()
135 {
136 if (editBlocked_ == 0)
137 updateLevel_ |= UPD_EDITABLE_STATE;
138
139 editBlocked_ += 1;
140 }
141
unblockEdit()142 void TocEdit::unblockEdit()
143 {
144 if (editBlocked_ > 0) {
145 editBlocked_ -= 1;
146
147 if (editBlocked_ == 0)
148 updateLevel_ |= UPD_EDITABLE_STATE;
149 }
150 }
151
updateLevel()152 unsigned long TocEdit::updateLevel()
153 {
154 unsigned long level = updateLevel_;
155
156 updateLevel_ = 0;
157 return level;
158 }
159
lengthSample() const160 unsigned long TocEdit::lengthSample() const
161 {
162 return toc_->length().samples();
163 }
164
filename(const char * fname)165 void TocEdit::filename(const char *fname)
166 {
167 if (fname != NULL && *fname != 0) {
168 char *s = strdupCC(fname);
169 delete[] filename_;
170 filename_ = s;
171
172 updateLevel_ |= UPD_TOC_DATA;
173 }
174 }
175
filename() const176 const char *TocEdit::filename() const
177 {
178 return filename_;
179 }
180
181
readToc(const char * fname)182 int TocEdit::readToc(const char *fname)
183 {
184 if (!editable())
185 return 0;
186
187 if (fname == NULL || *fname == 0)
188 return 1;
189
190 Toc *t = Toc::read(fname);
191
192 if (t != NULL) {
193
194 // Check and resolve input files paths
195 t->resolveFilenames(fname);
196
197 // Sometimes length fields are ommited. Make sure we got everything.
198 t->recomputeLength();
199
200 toc(t, fname);
201 return 0;
202 }
203
204 return 1;
205 }
206
saveToc()207 int TocEdit::saveToc()
208 {
209 int ret = toc_->write(filename_);
210
211 if (ret == 0)
212 tocDirty(0);
213
214 return ret;
215 }
216
saveAsToc(const char * fname)217 int TocEdit::saveAsToc(const char *fname)
218 {
219 int ret;
220
221 if (fname != NULL && *fname != 0) {
222 ret = toc_->write(fname);
223
224 if (ret == 0) {
225 filename(fname);
226 tocDirty(0);
227 updateLevel_ |= UPD_TOC_DATA;
228 }
229
230 return ret;
231 }
232
233 return 1;
234 }
235
236
moveTrackMarker(int trackNr,int indexNr,long lba)237 int TocEdit::moveTrackMarker(int trackNr, int indexNr, long lba)
238 {
239 if (!editable())
240 return 0;
241
242 int ret = toc_->moveTrackMarker(trackNr, indexNr, lba);
243
244 if (ret == 0) {
245 tocDirty(1);
246 updateLevel_ |= UPD_TRACK_DATA;
247 }
248
249 return ret;
250 }
251
addTrackMarker(long lba)252 int TocEdit::addTrackMarker(long lba)
253 {
254 if (!editable())
255 return 0;
256
257 int ret = toc_->addTrackMarker(lba);
258
259 if (ret == 0) {
260 tocDirty(1);
261 //llanero: different views
262 // updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
263 }
264
265 return ret;
266 }
267
addIndexMarker(long lba)268 int TocEdit::addIndexMarker(long lba)
269 {
270 if (!editable())
271 return 0;
272
273 int ret = toc_->addIndexMarker(lba);
274
275 if (ret == 0) {
276 tocDirty(1);
277 //llanero: different views
278 // updateLevel_ |= UPD_TRACK_DATA;
279 }
280
281 return ret;
282 }
283
addPregap(long lba)284 int TocEdit::addPregap(long lba)
285 {
286 if (!editable())
287 return 0;
288
289 int ret = toc_->addPregap(lba);
290
291 if (ret == 0) {
292 tocDirty(1);
293 //llanero: different views
294 // updateLevel_ |= UPD_TRACK_DATA;
295 }
296
297 return ret;
298 }
299
removeTrackMarker(int trackNr,int indexNr)300 int TocEdit::removeTrackMarker(int trackNr, int indexNr)
301 {
302 if (!editable())
303 return 0;
304
305 int ret = toc_->removeTrackMarker(trackNr, indexNr);
306
307 if (ret == 0) {
308 tocDirty(1);
309 //llanero: different views
310 // updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
311 }
312
313 return ret;
314 }
315
curScan()316 bool TocEdit::curScan()
317 {
318 // An end position of -1 means recompute the toc length and scan to
319 // the last sample position.
320
321 if (cur_->end == -1) {
322 // (Denis Leroy) The reason for this code is somewhat
323 // complex. When importing a CUE file with MP3s, the length of the
324 // last track is not known until the MP3 is actually converted to
325 // a WAV (because, unlike TOC files, CUE files don't specify
326 // explicitely the length of each track). Unlike WAV, you can't
327 // guess the length of the track based on the mp3 file size
328 // without scanning the whole thing, which we don't want to do
329 // twice obviously. So the semantic of the "scan" job is changed
330 // to integrate a length recalculation when the end is not
331 // specified. It would be cleaner to create a specific job task to
332 // do this.
333 toc_->recomputeLength();
334 cur_->end = toc_->length().samples() - 1;
335 updateLevel_ |= UPD_SAMPLES;
336 }
337 int ret = sampleManager_->scanToc(cur_->pos, cur_->end);
338
339 if (ret == 0)
340 return true;
341
342 if (ret == 2) {
343 signalError("Unable to open or read from input audio files");
344 return false;
345 }
346
347 return false;
348 }
349
curAppendTrack()350 bool TocEdit::curAppendTrack()
351 {
352 TrackData* data;
353 int ret = curCreateAudioData(&data);
354 if (ret != 0)
355 return false;
356
357 TrackDataList list;
358 long start, end;
359 list.append(data);
360 toc_->appendTrack(&list, &start, &end);
361 tocDirty(1);
362 sampleManager_->scanToc(Msf(start).samples(), Msf(end).samples() - 1);
363 return true;
364 }
365
curAppendFile()366 bool TocEdit::curAppendFile()
367 {
368 TrackData* data;
369 int ret = curCreateAudioData(&data);
370 if (ret != 0)
371 return false;
372
373 TrackDataList list;
374 long start, end;
375 list.append(data);
376 if (toc_->appendTrackData(&list, &start, &end) != 0) {
377 delete data;
378 return false;
379 }
380 tocDirty(1);
381 sampleManager_->scanToc(Msf(start).samples(), Msf(end).samples() - 1);
382 return true;
383 }
384
curInsertFile()385 bool TocEdit::curInsertFile()
386 {
387 TrackData* data;
388 int ret = curCreateAudioData(&data);
389 if (ret != 0)
390 return false;
391
392 TrackDataList list;
393 list.append(data);
394 if (toc_->insertTrackData(cur_->pos, &list) != 0) {
395 signalError(_("Cannot insert file into a data track"));
396 delete data;
397 return false;
398 }
399 cur_->len = list.length();
400 sampleManager_->insertSamples(cur_->pos, cur_->len, NULL);
401 sampleManager_->scanToc(cur_->pos, cur_->pos + cur_->len);
402 tocDirty(1);
403 return true;
404 }
405
406 // Creates an audio data object for given filename. Errors are send to
407 // status line.
408 // data: filled with newly allocated TrackData object on success
409 // return: 0: OK
410 // 1: cannot open file
411 // 2: file has wrong format
curCreateAudioData(TrackData ** data)412 int TocEdit::curCreateAudioData(TrackData **data)
413 {
414 unsigned long len;
415 std::string msg;
416
417 switch (TrackData::checkAudioFile(cur_->cfile.c_str(), &len)) {
418 case 1:
419 msg = _("Could not open file \"");
420 msg += cur_->cfile;
421 msg += "\"";
422 signalError(msg.c_str());
423 return 1; // Cannot open file
424
425 case 2:
426 msg = _("Could not open file \"");
427 msg += cur_->cfile;
428 msg += "\" : wrong file format";
429 signalError(msg.c_str());
430 return 2; // File format error
431 }
432
433 *data = new TrackData(cur_->file.c_str(), 0, len);
434 (*data)->effectiveFilename(cur_->cfile.c_str());
435
436 return 0;
437 }
438
curSignalConversionError(FormatSupport::Status err)439 void TocEdit::curSignalConversionError(FormatSupport::Status err)
440 {
441 std::string msg = _("Unable to decode audio file \"");
442 msg += cur_->file;
443 msg += "\" : ";
444 switch (err) {
445 case FormatSupport::FS_DISK_FULL:
446 msg += _("disk is full");
447 break;
448 case FormatSupport::FS_OUTPUT_PROBLEM:
449 msg += _("error creating output file");
450 break;
451 default:
452 msg += _("read error or wrong file format");
453 }
454 signalError(msg.c_str());
455 }
456
queueConversion(const char * filename)457 void TocEdit::queueConversion(const char* filename)
458 {
459 QueueJob* job = new QueueJob("convert");
460 job->file = filename;
461 queue_.push_back(job);
462
463 if (!threadActive_)
464 activateQueue();
465 }
466
queueAppendTrack(const char * filename)467 void TocEdit::queueAppendTrack(const char* filename)
468 {
469 QueueJob* job = new QueueJob("aptrack");
470 job->op = "aptrack";
471 job->file = filename;
472 queue_.push_back(job);
473
474 if (!threadActive_)
475 activateQueue();
476 }
477
queueAppendFile(const char * filename)478 void TocEdit::queueAppendFile(const char* filename)
479 {
480 QueueJob* job = new QueueJob("apfile");
481 job->file = filename;
482 queue_.push_back(job);
483
484 if (!threadActive_)
485 activateQueue();
486 }
487
queueInsertFile(const char * filename,unsigned long pos)488 void TocEdit::queueInsertFile(const char* filename, unsigned long pos)
489 {
490 QueueJob* job = new QueueJob("infile");
491 job->file = filename;
492 job->pos = pos;
493 queue_.push_back(job);
494
495 if (!threadActive_)
496 activateQueue();
497 }
498
queueScan(long start,long end)499 void TocEdit::queueScan(long start, long end)
500 {
501 QueueJob* job = new QueueJob("scan");
502 job->pos = start;
503 job->end = end;
504 queue_.push_back(job);
505
506 if (!threadActive_)
507 activateQueue();
508 }
509
activateQueue()510 void TocEdit::activateQueue()
511 {
512 if (!threadActive_) {
513 threadActive_ = true;
514 blockEdit();
515 signalCancelEnable(true);
516 Glib::signal_idle().connect(sigc::mem_fun(*this,
517 &TocEdit::queueThread));
518 guiUpdate();
519 }
520 }
521
queueAbort()522 void TocEdit::queueAbort()
523 {
524 if (threadActive_) {
525 queue_.clear();
526 curState_ = TE_IDLE;
527 if (curConv_) {
528 curConv_->convertAbort();
529 delete curConv_;
530 curConv_ = NULL;
531 signalStatusMessage("");
532 }
533 }
534 }
535
isQueueActive()536 bool TocEdit::isQueueActive()
537 {
538 return threadActive_;
539 }
540
541 // The queueThread is run by the Gtk idle thread when asynchronous
542 // work has to be done, such as decoding an MP3 file or reading
543 // samples from a WAV file.
544 //
545 // Asynchronous work (i.e. CPU-intensive work that has do be done in
546 // the background without blocking the GUI) can be scheduled by adding
547 // a new QueueJob object in the queue_ (see queueXXX methods above).
548
queueThread()549 bool TocEdit::queueThread()
550 {
551 static int pulse = 0;
552
553 // If we're idle, pop next queue entry.
554 if (curState_ == TE_IDLE) {
555
556 // Queue empty ? Stop queue thread.
557 if (queue_.empty()) {
558 threadActive_ = false;
559 unblockEdit();
560 signalProgressFraction(0.0);
561 signalCancelEnable(false);
562 guiUpdate();
563 return false; // false means disconnect idle thread
564 }
565
566 if (cur_)
567 delete cur_;
568 cur_ = queue_.front();
569 queue_.pop_front();
570
571 if (cur_->op == "scan") {
572 if (curScan()) {
573 curState_ = TE_READING;
574 signalStatusMessage("Scanning audio data");
575 } else {
576 curState_ = TE_IDLE;
577 return true;
578 }
579 } else {
580
581 if (curConv_)
582 delete curConv_;
583
584 FormatSupport::Status err;
585 curConv_ = formatConverter.newConverterStart(cur_->file.c_str(),
586 cur_->cfile, &err);
587 if (curConv_) {
588 std::string msg = "Decoding audio file ";
589 msg += cur_->file;
590 curState_ = TE_CONVERTING;
591 signalStatusMessage(msg.c_str());
592
593 } else {
594
595 if (err != FormatSupport::FS_SUCCESS) {
596 curSignalConversionError(err);
597 curState_ = TE_IDLE;
598 return true;
599 }
600 // File is already converted, or can't be converted (it's a WAV
601 // or RAW file already).
602 if (cur_->cfile.empty())
603 cur_->cfile = cur_->file;
604 curState_ = TE_CONVERTED;
605 }
606 }
607 }
608
609 // ------------------ TE_CONVERTING state: do file format conversion
610
611 if (curState_ == TE_CONVERTING) {
612 // Perform incremental file conversion.
613 FormatSupport::Status status = curConv_->convertContinue();
614 if (pulse++ > 5) {
615 signalProgressPulse();
616 pulse = 0;
617 }
618
619 // Still in progress, likely exit here.
620 if (status == FormatSupport::FS_IN_PROGRESS)
621 return true;
622
623 // Conversion done.
624 delete curConv_;
625 curConv_ = NULL;
626
627 if (status == FormatSupport::FS_SUCCESS)
628 curState_ = TE_CONVERTED;
629 else {
630 curSignalConversionError(status);
631 // Conversion failed, move on with next queue entry.
632 curState_ = TE_IDLE;
633 }
634
635 return true;
636 }
637
638 // ------------------- TE_CONVERTED state: conversion done, prepare reading
639
640 if (curState_ == TE_CONVERTED) {
641
642 // Sanity check: the cfile (converted filename) will be read as
643 // either a WAV file or a file containing raw samples. If the
644 // extension is still that of an encoded audio file, return an
645 // error. Otherwise it'll be read as a RAW samples file which is
646 // not was users expect.
647
648 TrackData::FileType ctype = TrackData::audioFileType(cur_->cfile.c_str());
649 if (ctype != TrackData::RAW && ctype != TrackData::WAVE) {
650 std::string msg = _("Cannot decode file");
651 msg += " \"";
652 msg += cur_->cfile;
653 msg += "\" : ";
654 msg += _("unsupported audio format");
655 signalError(msg.c_str());
656 curState_ = TE_IDLE;
657 return true;
658 }
659
660 // If all we wanted to do was format conversion, we're done.
661 if (cur_->op == "convert") {
662 toc_->markFileConversion(cur_->file.c_str(), cur_->cfile.c_str());
663 curState_ = TE_IDLE;
664 return true;
665 }
666
667 curState_ = TE_READING;
668 signalProgressFraction(0.0);
669
670 if (cur_->op == "aptrack") {
671 if (!curAppendTrack()) {
672 curState_ = TE_IDLE;
673 return true;
674 }
675 } else if (cur_->op == "apfile") {
676 if (!curAppendFile()) {
677 curState_ = TE_IDLE;
678 return true;
679 }
680
681 } else if (cur_->op == "infile") {
682 if (!curInsertFile()) {
683 curState_ = TE_IDLE;
684 return true;
685 }
686 }
687
688 std::string msg = "Scanning audio file ";
689 msg += cur_->file;
690 signalStatusMessage(msg.c_str());
691 }
692
693 // ------------------- TE_READING state : read/scan WAV samples
694
695 if (curState_ == TE_READING) {
696
697 int result = sampleManager_->readSamples();
698
699 if (result != 0) {
700
701 if (result < 0)
702 signalError(_("An error occured while reading audio data"));
703
704 else {
705 // Post operating code here.
706 if (cur_->op == "aptrack") {
707 updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
708 signalFullView();
709 std::string msg = "Appended track ";
710 msg += cur_->file;
711 signalStatusMessage(msg.c_str());
712 guiUpdate();
713 } else if (cur_->op == "apfile") {
714 updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
715 signalFullView();
716 std::string msg = "Appended file ";
717 msg += cur_->file;
718 signalStatusMessage(msg.c_str());
719 guiUpdate();
720 } else if (cur_->op == "infile") {
721 updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
722 signalFullView();
723 std::string msg = "Inserted file ";
724 msg += cur_->file;
725 signalStatusMessage(msg.c_str());
726 signalSampleSelection(cur_->pos, cur_->pos + cur_->len - 1);
727 guiUpdate();
728 } else if (cur_->op == "scan") {
729 std::stringstream ss;
730 ss << "Scanned ";
731 ss << (cur_->end - cur_->pos + 1);
732 ss << " samples of data";
733 std::string msg = ss.str();
734 signalStatusMessage(msg.c_str());
735 updateLevel_ |= UPD_SAMPLES;
736 }
737 }
738 curState_ = TE_IDLE;
739 }
740 return true;
741 }
742 return true;
743 }
744
appendSilence(unsigned long length)745 int TocEdit::appendSilence(unsigned long length)
746 {
747 if (!editable())
748 return 0;
749
750 if (length > 0) {
751 long start, end;
752
753 TrackData *data = new TrackData(length);
754 TrackDataList list;
755 list.append(data);
756
757 if (toc_->appendTrackData(&list, &start, &end) == 0) {
758
759 sampleManager_->scanToc(Msf(start).samples(), Msf(end).samples() - 1,
760 true);
761
762 tocDirty(1);
763 updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
764 }
765 }
766
767 return 0;
768 }
769
770 // Return: 0: OK
771 // 1: No modify allowed
772 // 2: error?
insertSilence(unsigned long length,unsigned long pos)773 int TocEdit::insertSilence(unsigned long length, unsigned long pos)
774 {
775 if (!editable())
776 return 1;
777
778 if (length > 0) {
779 TrackData *data = new TrackData(length);
780 TrackDataList list;
781
782 list.append(data);
783
784 if (toc_->insertTrackData(pos, &list) == 0) {
785 sampleManager_->insertSamples(pos, length, NULL);
786 sampleManager_->scanToc(pos, pos + length, true);
787
788 tocDirty(1);
789 updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
790 return 0;
791 }
792 }
793
794 return 2;
795 }
796
797
setTrackCopyFlag(int trackNr,int flag)798 void TocEdit::setTrackCopyFlag(int trackNr, int flag)
799 {
800 if (!editable())
801 return;
802
803 Track *t = toc_->getTrack(trackNr);
804
805 if (t != NULL) {
806 t->copyPermitted(flag);
807 tocDirty(1);
808 updateLevel_ |= UPD_TRACK_DATA;
809 }
810 }
811
setTrackPreEmphasisFlag(int trackNr,int flag)812 void TocEdit::setTrackPreEmphasisFlag(int trackNr, int flag)
813 {
814 if (!editable())
815 return;
816
817 Track *t = toc_->getTrack(trackNr);
818
819 if (t != NULL) {
820 t->preEmphasis(flag);
821 tocDirty(1);
822 updateLevel_ |= UPD_TRACK_DATA;
823 }
824 }
825
setTrackAudioType(int trackNr,int flag)826 void TocEdit::setTrackAudioType(int trackNr, int flag)
827 {
828 if (!editable())
829 return;
830
831 Track *t = toc_->getTrack(trackNr);
832
833 if (t != NULL) {
834 t->audioType(flag);
835 tocDirty(1);
836 updateLevel_ |= UPD_TRACK_DATA;
837 }
838
839 }
840
setTrackIsrcCode(int trackNr,const char * s)841 void TocEdit::setTrackIsrcCode(int trackNr, const char *s)
842 {
843 if (!editable())
844 return;
845
846 Track *t = toc_->getTrack(trackNr);
847
848 if (t != NULL) {
849 if (t->isrc(s) == 0) {
850 tocDirty(1);
851 updateLevel_ |= UPD_TRACK_DATA;
852 }
853 }
854 }
855
setCdTextItem(int trackNr,CdTextItem::PackType type,int blockNr,const char * s)856 void TocEdit::setCdTextItem(int trackNr, CdTextItem::PackType type,
857 int blockNr, const char *s)
858 {
859 if (!editable())
860 return;
861
862 if (s != NULL) {
863 CdTextItem *item = new CdTextItem(type, blockNr, s);
864
865 toc_->addCdTextItem(trackNr, item);
866 }
867 else {
868 toc_->removeCdTextItem(trackNr, type, blockNr);
869 }
870
871 tocDirty(1);
872
873 updateLevel_ |= (trackNr == 0) ? UPD_TOC_DATA : UPD_TRACK_DATA;
874
875 }
876
setCdTextGenreItem(int blockNr,int code1,int code2,const char * description)877 void TocEdit::setCdTextGenreItem(int blockNr, int code1, int code2,
878 const char *description)
879 {
880 if (code1 > 255 || code2 > 255)
881 return;
882
883 if (!editable())
884 return;
885
886 if (code1 < 0 || code2 < 0) {
887 toc_->removeCdTextItem(0, CdTextItem::CDTEXT_GENRE, blockNr);
888 }
889 else {
890 CdTextItem *item = new CdTextItem(blockNr, (unsigned char)code1,
891 (unsigned char)code2, description);
892
893 toc_->addCdTextItem(0, item);
894 }
895
896 tocDirty(1);
897
898 updateLevel_ |= UPD_TOC_DATA;
899 }
900
901
setCdTextLanguage(int blockNr,int langCode)902 void TocEdit::setCdTextLanguage(int blockNr, int langCode)
903 {
904 if (!editable())
905 return;
906
907 toc_->cdTextLanguage(blockNr, langCode);
908 tocDirty(1);
909
910 updateLevel_ |= UPD_TOC_DATA;
911
912 }
913
setCatalogNumber(const char * s)914 void TocEdit::setCatalogNumber(const char *s)
915 {
916 if (!editable())
917 return;
918
919 if (toc_->catalog(s) == 0) {
920 tocDirty(1);
921 updateLevel_ |= UPD_TOC_DATA;
922 }
923
924 }
925
setTocType(Toc::TocType type)926 void TocEdit::setTocType(Toc::TocType type)
927 {
928 if (!editable())
929 return;
930
931 toc_->tocType(type);
932 tocDirty(1);
933 updateLevel_ |= UPD_TOC_DATA;
934 }
935
936
937 // Removes selected track data
938 // Return: 0: OK
939 // 1: no selection
940 // 2: selection crosses track boundaries
941 // 3: cannot modify data track
removeTrackData(TocEditView * view)942 int TocEdit::removeTrackData(TocEditView *view)
943 {
944 TrackDataList *list;
945 unsigned long selMin, selMax;
946
947 if (!editable())
948 return 0;
949
950 if (!view->sampleSelection(&selMin, &selMax))
951 return 1;
952
953 switch (toc_->removeTrackData(selMin, selMax, &list)) {
954 case 0:
955 if (list != NULL) {
956 if (list->length() > 0) {
957 delete trackDataScrap_;
958 trackDataScrap_ = new TrackDataScrap(list);
959 }
960 else {
961 delete list;
962 }
963
964 sampleManager_->removeSamples(selMin, selMax, trackDataScrap_);
965
966 view->sampleSelectionClear();
967 view->sampleMarker(selMin);
968
969 tocDirty(1);
970 }
971 break;
972
973 case 1:
974 return 2;
975 break;
976
977 case 2:
978 return 3;
979 break;
980 }
981 return 0;
982 }
983
984 // Inserts track data from scrap
985 // Return: 0: OK
986 // 1: no scrap data to paste
insertTrackData(TocEditView * view)987 int TocEdit::insertTrackData(TocEditView *view)
988
989 {
990 if (!editable())
991 return 0;
992
993 if (trackDataScrap_ == NULL)
994 return 1;
995
996 unsigned long len = trackDataScrap_->trackDataList()->length();
997 unsigned long marker;
998
999 if (view->sampleMarker(&marker) && marker < toc_->length().samples()) {
1000 if (toc_->insertTrackData(marker, trackDataScrap_->trackDataList())
1001 == 0) {
1002
1003 sampleManager_->insertSamples(marker, len, trackDataScrap_);
1004 sampleManager_->scanToc(marker, marker, true);
1005 sampleManager_->scanToc(marker + len - 1, marker + len - 1, true);
1006
1007 view->sampleSelect(marker, marker + len - 1);
1008
1009 tocDirty(1);
1010 //llanero: different views
1011 // updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
1012 }
1013 }
1014 else {
1015 long start, end;
1016
1017 if (toc_->appendTrackData(trackDataScrap_->trackDataList(), &start, &end)
1018 == 0) {
1019
1020 sampleManager_->insertSamples(Msf(start).samples(),
1021 Msf(end - start).samples(),
1022 trackDataScrap_);
1023
1024 sampleManager_->scanToc(Msf(start).samples(), Msf(start).samples(),
1025 true);
1026 if (end > 0)
1027 sampleManager_->scanToc(Msf(start).samples() + len,
1028 Msf(end).samples() - 1, true);
1029
1030 view->sampleSelect(Msf(start).samples(), Msf(end).samples() - 1);
1031
1032 tocDirty(1);
1033 updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
1034 }
1035 }
1036 return 0;
1037 }
1038