File indexing completed on 2024-05-12 04:51:02

0001 /*
0002     SPDX-FileCopyrightText: 2003-2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2009 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
0004     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0005     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 
0011 #include "k3baudiotrack.h"
0012 #include "k3baudiodoc.h"
0013 #include "k3baudiodatasource.h"
0014 #include "k3baudiotrackreader.h"
0015 
0016 #include "k3baudiodecoder.h"
0017 #include "k3bcore.h"
0018 #include "k3bcdtextvalidator.h"
0019 
0020 #include <QDebug>
0021 #include <QString>
0022 
0023 
0024 
0025 class K3b::AudioTrack::Private
0026 {
0027 public:
0028     Private( AudioDoc* p = 0 )
0029     :
0030       parent(p),
0031       copy(false),
0032       preEmp(false),
0033       index0Offset(150),
0034       prev(0),
0035       next(0),
0036       firstSource(0),
0037       currentlyDeleting(false) {
0038         cdTextValidator = new K3b::CdTextValidator();
0039     }
0040 
0041     ~Private() {
0042         delete cdTextValidator;
0043     }
0044 
0045     AudioDoc* parent;
0046 
0047     /** copy protection */
0048     bool copy;
0049     bool preEmp;
0050 
0051     Msf index0Offset;
0052 
0053     Device::TrackCdText cdText;
0054 
0055     // list
0056     AudioTrack* prev;
0057     AudioTrack* next;
0058 
0059     AudioDataSource* firstSource;
0060 
0061     bool currentlyDeleting;
0062 
0063     K3b::CdTextValidator* cdTextValidator;
0064 };
0065 
0066 
0067 K3b::AudioTrack::AudioTrack()
0068     : QObject(),
0069       d( new Private )
0070 {
0071 }
0072 
0073 
0074 K3b::AudioTrack::AudioTrack( K3b::AudioDoc* parent )
0075     : QObject(),
0076       d( new Private( parent ) )
0077 {
0078 }
0079 
0080 
0081 K3b::AudioTrack::~AudioTrack()
0082 {
0083     qDebug() << this;
0084 
0085     d->currentlyDeleting = true;
0086 
0087     // fix the list
0088     take();
0089 
0090     qDebug() << "deleting sources.";
0091 
0092     // delete all sources
0093     while( d->firstSource )
0094         delete d->firstSource->take();
0095 
0096     qDebug() << "finished";
0097 
0098     delete d;
0099 }
0100 
0101 
0102 K3b::AudioDoc* K3b::AudioTrack::doc() const
0103 {
0104     return d->parent;
0105 }
0106 
0107 
0108 void K3b::AudioTrack::emitChanged()
0109 {
0110     emit changed();
0111 
0112     if( d->parent && !d->currentlyDeleting )
0113         d->parent->slotTrackChanged( this );
0114 }
0115 
0116 
0117 void K3b::AudioTrack::setArtist( const QString& a )
0118 {
0119     setPerformer( a );
0120 }
0121 
0122 
0123 void K3b::AudioTrack::setPerformer( const QString& a )
0124 {
0125     if( performer() != a ) {
0126         QString s( a );
0127         d->cdTextValidator->fixup( s );
0128         d->cdText.setPerformer(s);
0129         emitChanged();
0130     }
0131 }
0132 
0133 
0134 void K3b::AudioTrack::setTitle( const QString& t )
0135 {
0136     if( title() != t ) {
0137         QString s( t );
0138         d->cdTextValidator->fixup( s );
0139         d->cdText.setTitle(s);
0140         emitChanged();
0141     }
0142 }
0143 
0144 
0145 void K3b::AudioTrack::setArranger( const QString& t )
0146 {
0147     if( arranger() != t ) {
0148         QString s( t );
0149         d->cdTextValidator->fixup( s );
0150         d->cdText.setArranger(s);
0151         emitChanged();
0152     }
0153 }
0154 
0155 
0156 void K3b::AudioTrack::setSongwriter( const QString& t )
0157 {
0158     if( songwriter() != t ) {
0159         QString s( t );
0160         d->cdTextValidator->fixup( s );
0161         d->cdText.setSongwriter(s);
0162         emitChanged();
0163     }
0164 }
0165 
0166 
0167 void K3b::AudioTrack::setComposer( const QString& t )
0168 {
0169     if( composer() != t ) {
0170         QString s( t );
0171         d->cdTextValidator->fixup( s );
0172         d->cdText.setComposer(s);
0173         emitChanged();
0174     }
0175 }
0176 
0177 
0178 void K3b::AudioTrack::setIsrc( const QString& t )
0179 {
0180     if( isrc() != t ) {
0181         d->cdText.setIsrc(t);
0182         emitChanged();
0183     }
0184 }
0185 
0186 
0187 void K3b::AudioTrack::setCdTextMessage( const QString& t )
0188 {
0189     if( cdTextMessage() != t ) {
0190         QString s( t );
0191         d->cdTextValidator->fixup( s );
0192         d->cdText.setMessage(s);
0193         emitChanged();
0194     }
0195 }
0196 
0197 
0198 void K3b::AudioTrack::setCdText( const K3b::Device::TrackCdText& cdtext )
0199 {
0200     d->cdText = cdtext;
0201     emitChanged();
0202 }
0203 
0204 
0205 void K3b::AudioTrack::setPreEmp( bool b )
0206 {
0207     if( d->preEmp != b ) {
0208         d->preEmp = b;
0209         emitChanged();
0210     }
0211 }
0212 
0213 
0214 void K3b::AudioTrack::setCopyProtection( bool b )
0215 {
0216     if( d->copy != b ) {
0217         d->copy = b;
0218         emitChanged();
0219     }
0220 }
0221 
0222 
0223 K3b::AudioDataSource* K3b::AudioTrack::firstSource() const
0224 {
0225     return d->firstSource;
0226 }
0227 
0228 
0229 K3b::AudioDataSource* K3b::AudioTrack::lastSource() const
0230 {
0231     K3b::AudioDataSource* s = d->firstSource;
0232     while( s && s->next() )
0233         s = s->next();
0234     return s;
0235 }
0236 
0237 
0238 bool K3b::AudioTrack::inList() const
0239 {
0240     if( doc() )
0241         return ( doc()->firstTrack() == this || d->prev != 0 );
0242     else
0243         return false;
0244 }
0245 
0246 
0247 K3b::Msf K3b::AudioTrack::length() const
0248 {
0249     K3b::Msf length;
0250     K3b::AudioDataSource* source = d->firstSource;
0251     while( source ) {
0252         length += source->length();
0253         source = source->next();
0254     }
0255     return length;
0256 }
0257 
0258 
0259 KIO::filesize_t K3b::AudioTrack::size() const
0260 {
0261     return length().audioBytes();
0262 }
0263 
0264 QString K3b::AudioTrack::artist() const
0265 {
0266     return d->cdText.performer();
0267 }
0268 
0269 
0270 QString K3b::AudioTrack::performer() const
0271 {
0272     return d->cdText.performer();
0273 }
0274 
0275 
0276 QString K3b::AudioTrack::title() const
0277 {
0278     return d->cdText.title();
0279 }
0280 
0281 
0282 QString K3b::AudioTrack::arranger() const
0283 {
0284     return d->cdText.arranger();
0285 }
0286 
0287 
0288 QString K3b::AudioTrack::songwriter() const
0289 {
0290     return d->cdText.songwriter();
0291 }
0292 
0293 
0294 QString K3b::AudioTrack::composer() const
0295 {
0296     return d->cdText.composer();
0297 }
0298 
0299 
0300 QString K3b::AudioTrack::isrc() const
0301 {
0302     return d->cdText.isrc();
0303 }
0304 
0305 
0306 QString K3b::AudioTrack::cdTextMessage() const
0307 {
0308     return d->cdText.message();
0309 }
0310 
0311 
0312 K3b::Device::TrackCdText K3b::AudioTrack::cdText() const
0313 {
0314     return d->cdText;
0315 }
0316 
0317 
0318 bool K3b::AudioTrack::copyProtection() const
0319 {
0320     return d->copy;
0321 }
0322 
0323 
0324 bool K3b::AudioTrack::preEmp() const
0325 {
0326     return d->preEmp;
0327 }
0328 
0329 
0330 unsigned int K3b::AudioTrack::trackNumber() const
0331 {
0332     if( d->prev )
0333         return d->prev->trackNumber() + 1;
0334     else
0335         return 1;
0336 }
0337 
0338 
0339 K3b::Msf K3b::AudioTrack::index0() const
0340 {
0341     // we save the index0Offset as length of the resulting pregap
0342     // this way the length of the track does not need to be ready
0343     // when creating the track.
0344     return length() - d->index0Offset;
0345 }
0346 
0347 
0348 K3b::Msf K3b::AudioTrack::postGap() const
0349 {
0350     if( next() )
0351         return d->index0Offset;
0352     else
0353         return 0;
0354 }
0355 
0356 
0357 void K3b::AudioTrack::setIndex0( const K3b::Msf& msf )
0358 {
0359     if( msf == 0 )
0360         d->index0Offset = 0;
0361     else
0362         d->index0Offset = length() - msf;
0363 }
0364 
0365 
0366 K3b::AudioTrack* K3b::AudioTrack::take()
0367 {
0368     if( inList() ) {
0369         const int position = trackNumber() - 1;
0370         if ( doc() )
0371             emit doc()->trackAboutToBeRemoved( position );
0372 
0373         if( !d->prev )
0374             doc()->setFirstTrack( d->next );
0375         if( !d->next )
0376             doc()->setLastTrack( d->prev );
0377 
0378         if( d->prev )
0379             d->prev->d->next = d->next;
0380         if( d->next )
0381             d->next->d->prev = d->prev;
0382 
0383         d->prev = d->next = 0;
0384 
0385         // remove from doc
0386         if( doc() )
0387             doc()->slotTrackRemoved( position );
0388 
0389         d->parent = 0;
0390     }
0391 
0392     return this;
0393 }
0394 
0395 
0396 void K3b::AudioTrack::moveAfter( K3b::AudioTrack* track )
0397 {
0398     qDebug() << "(K3b::AudioTrack::moveAfter( " << track << " )";
0399     if( !track ) {
0400         if( !doc() ) {
0401             qDebug() << "(K3b::AudioTrack::moveAfter) no parent set";
0402             return;
0403         }
0404 
0405         // make sure we do not mess up the list
0406         if( doc()->lastTrack() )
0407             moveAfter( doc()->lastTrack() );
0408         else {
0409             emit doc()->trackAboutToBeAdded( 0 );
0410             doc()->setFirstTrack( take() );
0411             doc()->setLastTrack( this );
0412             emit doc()->trackAdded( 0 );
0413         }
0414     }
0415     else if( track == this ) {
0416         qDebug() << "(K3b::AudioTrack::moveAfter) trying to move this after this.";
0417         return;
0418     }
0419     else {
0420         // remove this from the list
0421         take();
0422 
0423         emit track->doc()->trackAboutToBeAdded( track->trackNumber()-1 );
0424 
0425         // set the new parent doc
0426         d->parent = track->doc();
0427 
0428         K3b::AudioTrack* oldNext = track->d->next;
0429 
0430         // set track as prev
0431         track->d->next = this;
0432         d->prev = track;
0433 
0434         // set oldNext as next
0435         if( oldNext )
0436             oldNext->d->prev = this;
0437         d->next = oldNext;
0438 
0439         if( !d->prev )
0440             doc()->setFirstTrack( this );
0441         if( !d->next )
0442             doc()->setLastTrack( this );
0443 
0444         emit doc()->trackAdded( track->trackNumber()-1 );
0445     }
0446 
0447     emitChanged();
0448 }
0449 
0450 
0451 void K3b::AudioTrack::moveAhead( K3b::AudioTrack* track )
0452 {
0453     if( !track ) {
0454         if( !doc() ) {
0455             qDebug() << "(K3b::AudioTrack::moveAfter) no parent set";
0456             return;
0457         }
0458 
0459         // make sure we do not mess up the list
0460         if( doc()->firstTrack() )
0461             moveAhead( doc()->firstTrack() );
0462         else {
0463             emit doc()->trackAboutToBeAdded( 0 );
0464             doc()->setFirstTrack( take() );
0465             doc()->setLastTrack( this );
0466             emit doc()->trackAdded( 0 );
0467         }
0468     }
0469     else if( track == this ) {
0470         qDebug() << "(K3b::AudioTrack::moveAhead) trying to move this ahead of this.";
0471         return;
0472     }
0473     else {
0474         // remove this from the list
0475         take();
0476 
0477         emit track->doc()->trackAboutToBeAdded( track->trackNumber()-1 );
0478 
0479         // set the new parent doc
0480         d->parent = track->doc();
0481 
0482         K3b::AudioTrack* oldPrev = track->d->prev;
0483 
0484         // set track as next
0485         d->next = track;
0486         track->d->prev = this;
0487 
0488         // set oldPrev as prev
0489         d->prev = oldPrev;
0490         if( oldPrev )
0491             oldPrev->d->next = this;
0492 
0493         if( !d->prev )
0494             doc()->setFirstTrack( this );
0495         if( !d->next )
0496             doc()->setLastTrack( this );
0497 
0498         emit doc()->trackAdded( track->trackNumber()-1 );
0499     }
0500 
0501     emitChanged();
0502 }
0503 
0504 
0505 void K3b::AudioTrack::merge( K3b::AudioTrack* trackToMerge, K3b::AudioDataSource* sourceAfter )
0506 {
0507     qDebug() << "(K3b::AudioTrack::merge) " << trackToMerge << " into " << this;
0508     if( this == trackToMerge ) {
0509         qDebug() << "(K3b::AudioTrack::merge) trying to merge this with this.";
0510         return;
0511     }
0512 
0513     // remove the track to merge to make sure it does not get deleted by the doc too early
0514     trackToMerge->take();
0515 
0516     // in case we prepend all of trackToMerge's sources
0517     if( !sourceAfter ) {
0518         qDebug() << "(K3b::AudioTrack::merge) merging " << trackToMerge->firstSource();
0519         if( d->firstSource ) {
0520             trackToMerge->firstSource()->moveAhead( d->firstSource );
0521         }
0522         else {
0523             addSource( trackToMerge->firstSource()->take() );
0524         }
0525         sourceAfter = d->firstSource;
0526     }
0527 
0528     qDebug() << "(K3b::AudioTrack::merge) now merge the other sources.";
0529     // now merge all sources into this track
0530     while( trackToMerge->firstSource() ) {
0531         K3b::AudioDataSource* s = trackToMerge->firstSource();
0532         qDebug() << "(K3b::AudioTrack::merge) merging source " << s << " from track " << s->track() << " into track "
0533                  << this << " after source " << sourceAfter << Qt::endl;
0534         s->moveAfter( sourceAfter );
0535         sourceAfter = s;
0536     }
0537 
0538     // TODO: should we also merge the indices?
0539 
0540     // now we can safely delete the track we merged
0541     delete trackToMerge;
0542 
0543     qDebug() << "(K3b::AudioTrack::merge) finished";
0544 
0545     emitChanged();
0546 }
0547 
0548 
0549 K3b::AudioTrack* K3b::AudioTrack::prev() const
0550 {
0551     return d->prev;
0552 }
0553 
0554 
0555 K3b::AudioTrack* K3b::AudioTrack::next() const
0556 {
0557     return d->next;
0558 }
0559 
0560 
0561 void K3b::AudioTrack::setFirstSource( K3b::AudioDataSource* source )
0562 {
0563     d->firstSource = source;
0564     while( source ) {
0565         source->m_track = this;
0566         source = source->next();
0567     }
0568 
0569     emitChanged();
0570 }
0571 
0572 
0573 void K3b::AudioTrack::addSource( K3b::AudioDataSource* source )
0574 {
0575     if( !source )
0576         return;
0577 
0578     K3b::AudioDataSource* s = d->firstSource;
0579     while( s && s->next() )
0580         s = s->next();
0581     if( s )
0582         source->moveAfter( s );
0583     else
0584         setFirstSource( source->take() );
0585 }
0586 
0587 
0588 void K3b::AudioTrack::sourceChanged( K3b::AudioDataSource* )
0589 {
0590     if( d->currentlyDeleting )
0591         return;
0592 
0593     // TODO: update indices
0594 
0595     if( d->index0Offset > length() )
0596         d->index0Offset = length()-1;
0597 
0598     emitChanged();
0599 }
0600 
0601 
0602 int K3b::AudioTrack::numberSources() const
0603 {
0604     K3b::AudioDataSource* source = d->firstSource;
0605     int i = 0;
0606     while( source ) {
0607         source = source->next();
0608         ++i;
0609     }
0610     return i;
0611 }
0612 
0613 
0614 K3b::AudioTrack* K3b::AudioTrack::copy() const
0615 {
0616     K3b::AudioTrack* track = new K3b::AudioTrack();
0617 
0618     track->d->copy = d->copy;
0619     track->d->preEmp = d->preEmp;
0620     track->d->index0Offset = d->index0Offset;
0621     track->d->cdText = d->cdText;
0622     K3b::AudioDataSource* source = d->firstSource;
0623     while( source ) {
0624         track->addSource( source->copy() );
0625         source = source->next();
0626     }
0627 
0628     return track;
0629 }
0630 
0631 
0632 K3b::AudioTrack* K3b::AudioTrack::split( const K3b::Msf& pos )
0633 {
0634     if( pos < length() ) {
0635         // search the source
0636         // pos will be the first sector of the new track
0637         K3b::Msf currentPos;
0638         K3b::AudioDataSource* source = firstSource();
0639         while( source && currentPos + source->length() <= pos ) {
0640             currentPos += source->length();
0641             source = source->next();
0642         }
0643 
0644         K3b::AudioDataSource* splitSource = 0;
0645         if( currentPos > 0 && currentPos == pos ) {
0646             // no need to split a source
0647             splitSource = source;
0648         }
0649         else {
0650             if (source) 
0651                 splitSource = source->split( pos - currentPos );
0652         }
0653 
0654         // the new track should include all sources from splitSource and below
0655         K3b::AudioTrack* splitTrack = new K3b::AudioTrack();
0656         splitTrack->d->cdText = d->cdText;
0657         source = splitSource;
0658         while( source ) {
0659             K3b::AudioDataSource* addSource = source;
0660             source = source->next();
0661             splitTrack->addSource( addSource );
0662         }
0663 
0664         qDebug() << "(K3b::AudioTrack) moving track " << splitTrack << " after this (" << this << ") with parent " << doc();
0665         splitTrack->moveAfter( this );
0666 
0667         return splitTrack;
0668     }
0669     else
0670         return 0;
0671 }
0672 
0673 
0674 QIODevice* K3b::AudioTrack::createReader( QObject* parent )
0675 {
0676     return new AudioTrackReader( *this, parent );
0677 }
0678 
0679 
0680 K3b::Device::Track K3b::AudioTrack::toCdTrack() const
0681 {
0682     if( !inList() )
0683         return K3b::Device::Track();
0684 
0685     K3b::Msf firstSector;
0686     K3b::AudioTrack* track = doc()->firstTrack();
0687     while( track != this ) {
0688         firstSector += track->length();
0689         track = track->next();
0690     }
0691 
0692     K3b::Device::Track cdTrack( firstSector,
0693                               firstSector + length() - 1,
0694                               K3b::Device::Track::TYPE_AUDIO );
0695 
0696     // FIXME: auch im audiotrack copy permitted
0697     cdTrack.setCopyPermitted( !copyProtection() );
0698     cdTrack.setPreEmphasis( preEmp() );
0699 
0700     // FIXME: add indices != 0
0701 
0702     // no index 0 for the last track. Or should we allow this???
0703     if( doc()->lastTrack() != this )
0704         cdTrack.setIndex0( index0() );
0705 
0706     // FIXME: convert to QCString
0707     //    cdTrack.setIsrc( isrc() );
0708 
0709     return cdTrack;
0710 }
0711 
0712 
0713 void K3b::AudioTrack::debug()
0714 {
0715     qDebug() << "Track " << this << Qt::endl
0716              << "  Prev: " << d->prev << Qt::endl
0717              << "  Next: " << d->next << Qt::endl
0718              << "  Sources:" << Qt::endl;
0719     K3b::AudioDataSource* s = d->firstSource;
0720     while( s ) {
0721         qDebug() << "  " << s << " - Prev: " << s->prev() << " Next: " << s->next();
0722         s = s->next();
0723     }
0724 }
0725 
0726 
0727 K3b::AudioDataSource* K3b::AudioTrack::getSource( int index ) const
0728 {
0729     int i = 0;
0730     K3b::AudioDataSource* source = firstSource();
0731     while ( source && i < index ) {
0732         source = source->next();
0733         ++i;
0734     }
0735     return source;
0736 }
0737 
0738 
0739 void K3b::AudioTrack::emitSourceAboutToBeRemoved( AudioDataSource* source )
0740 {
0741     emit sourceAboutToBeRemoved( source->sourceIndex() );
0742 
0743     if ( doc() ) {
0744         emit doc()->sourceAboutToBeRemoved( this, source->sourceIndex() );
0745     }
0746 }
0747 
0748 
0749 void K3b::AudioTrack::emitSourceRemoved( K3b::AudioDataSource* source )
0750 {
0751     if ( doc() ) {
0752         // set the first source by hand (without using setFirstSource() )
0753         // just to avoid the model to read invalid firstSources
0754         if ( !source->prev() )
0755             d->firstSource = source->next();
0756 
0757         emit doc()->sourceRemoved( this, source->sourceIndex() );
0758     }
0759 
0760     emit sourceRemoved( source->sourceIndex() );
0761 
0762     // and now call the setFirstSource() to make sure the proper signals
0763     // are emitted
0764     if ( !source->prev() )
0765         setFirstSource( source->next() );
0766 }
0767 
0768 
0769 void K3b::AudioTrack::emitSourceAboutToBeAdded( int position )
0770 {
0771     emit sourceAboutToBeAdded( position );
0772 
0773     if ( doc() ) {
0774         emit doc()->sourceAboutToBeAdded( this, position );
0775     }
0776 }
0777 
0778 
0779 void K3b::AudioTrack::emitSourceAdded( AudioDataSource* source )
0780 {
0781     if ( doc() ) {
0782         emit doc()->sourceAdded( this, source->sourceIndex() );
0783         doc()->slotTrackChanged( this );
0784     } else {
0785         emit sourceAdded( source->sourceIndex() );
0786     }
0787 }
0788 
0789 
0790 void K3b::AudioTrack::setIndex0Offset( const Msf& index0Offset )
0791 {
0792     d->index0Offset = index0Offset;
0793 }
0794 
0795 
0796 void K3b::AudioTrack::setParent( K3b::AudioDoc* parent )
0797 {
0798     d->parent = parent;
0799 }
0800 
0801 #include "moc_k3baudiotrack.cpp"