summaryrefslogtreecommitdiffstats
path: root/libk3b/projects/audiocd/k3baudiotrack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libk3b/projects/audiocd/k3baudiotrack.cpp')
-rw-r--r--libk3b/projects/audiocd/k3baudiotrack.cpp628
1 files changed, 628 insertions, 0 deletions
diff --git a/libk3b/projects/audiocd/k3baudiotrack.cpp b/libk3b/projects/audiocd/k3baudiotrack.cpp
new file mode 100644
index 0000000..a1d12e4
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiotrack.cpp
@@ -0,0 +1,628 @@
+/*
+ *
+ * $Id: k3baudiotrack.cpp 620139 2007-01-05 11:59:05Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3baudiotrack.h"
+#include "k3baudiodoc.h"
+#include "k3baudiodatasource.h"
+
+#include <k3baudiodecoder.h>
+#include <k3bcore.h>
+#include <k3bcdtextvalidator.h>
+
+#include <qstring.h>
+
+#include <kdebug.h>
+
+
+
+class K3bAudioTrack::Private
+{
+public:
+ Private() {
+ cdTextValidator = new K3bCdTextValidator();
+ }
+
+ ~Private() {
+ delete cdTextValidator;
+ }
+
+ K3bCdTextValidator* cdTextValidator;
+};
+
+
+K3bAudioTrack::K3bAudioTrack()
+ : m_parent(0),
+ m_copy(false),
+ m_preEmp(false),
+ m_index0Offset(150),
+ m_prev(0),
+ m_next(0),
+ m_firstSource(0),
+ m_currentSource(0),
+ m_alreadyReadBytes(0),
+ m_currentlyDeleting(false)
+{
+ d = new Private;
+}
+
+
+K3bAudioTrack::K3bAudioTrack( K3bAudioDoc* parent )
+ : m_parent(parent),
+ m_copy(false),
+ m_preEmp(false),
+ m_index0Offset(150),
+ m_prev(0),
+ m_next(0),
+ m_firstSource(0),
+ m_currentSource(0),
+ m_alreadyReadBytes(0),
+ m_currentlyDeleting(false)
+{
+ d = new Private;
+}
+
+
+K3bAudioTrack::~K3bAudioTrack()
+{
+ kdDebug() << "(K3bAudioTrack::~K3bAudioTrack) " << this << endl;
+ //
+ // It is crucial that we do not emit the changed signal here because otherwise
+ // the doc will delete us again once we are empty!
+ //
+ m_currentlyDeleting = true;
+
+ // fix the list
+ take();
+
+ kdDebug() << "(K3bAudioTrack::~K3bAudioTrack) deleting sources." << endl;
+
+ // delete all sources
+ while( m_firstSource )
+ delete m_firstSource->take();
+
+ kdDebug() << "(K3bAudioTrack::~K3bAudioTrack) finished" << endl;
+
+ delete d;
+}
+
+
+void K3bAudioTrack::emitChanged()
+{
+ if( m_parent )
+ m_parent->slotTrackChanged( this );
+}
+
+
+void K3bAudioTrack::setArtist( const QString& a )
+{
+ setPerformer( a );
+}
+
+
+void K3bAudioTrack::setPerformer( const QString& a )
+{
+ QString s( a );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setPerformer(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setTitle( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setTitle(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setArranger( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setArranger(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setSongwriter( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setSongwriter(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setComposer( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setComposer(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setIsrc( const QString& t )
+{
+ m_cdText.setIsrc(t);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setCdTextMessage( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setMessage(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setCdText( const K3bDevice::TrackCdText& cdtext )
+{
+ m_cdText = cdtext;
+ emitChanged();
+}
+
+
+K3bAudioDataSource* K3bAudioTrack::lastSource() const
+{
+ K3bAudioDataSource* s = m_firstSource;
+ while( s && s->next() )
+ s = s->next();
+ return s;
+}
+
+
+bool K3bAudioTrack::inList() const
+{
+ if( doc() )
+ return ( doc()->firstTrack() == this || m_prev != 0 );
+ else
+ return false;
+}
+
+
+K3b::Msf K3bAudioTrack::length() const
+{
+ K3b::Msf length;
+ K3bAudioDataSource* source = m_firstSource;
+ while( source ) {
+ length += source->length();
+ source = source->next();
+ }
+ return length;
+}
+
+
+KIO::filesize_t K3bAudioTrack::size() const
+{
+ return length().audioBytes();
+}
+
+
+unsigned int K3bAudioTrack::trackNumber() const
+{
+ if( m_prev )
+ return m_prev->trackNumber() + 1;
+ else
+ return 1;
+}
+
+
+K3b::Msf K3bAudioTrack::index0() const
+{
+ // we save the index0Offset as length of the resulting pregap
+ // this way the length of the track does not need to be ready
+ // when creating the track.
+ return length() - m_index0Offset;
+}
+
+
+K3b::Msf K3bAudioTrack::postGap() const
+{
+ if( next() )
+ return m_index0Offset;
+ else
+ return 0;
+}
+
+
+void K3bAudioTrack::setIndex0( const K3b::Msf& msf )
+{
+ if( msf == 0 )
+ m_index0Offset = 0;
+ else
+ m_index0Offset = length() - msf;
+}
+
+
+K3bAudioTrack* K3bAudioTrack::take()
+{
+ if( inList() ) {
+ if( !m_prev )
+ doc()->setFirstTrack( m_next );
+ if( !m_next )
+ doc()->setLastTrack( m_prev );
+
+ if( m_prev )
+ m_prev->m_next = m_next;
+ if( m_next )
+ m_next->m_prev = m_prev;
+
+ m_prev = m_next = 0;
+
+ // remove from doc
+ if( m_parent )
+ m_parent->slotTrackRemoved(this);
+ m_parent = 0;
+ }
+
+ return this;
+}
+
+
+void K3bAudioTrack::moveAfter( K3bAudioTrack* track )
+{
+ kdDebug() << "(K3bAudioTrack::moveAfter( " << track << " )" << endl;
+ if( !track ) {
+ if( !doc() ) {
+ kdDebug() << "(K3bAudioTrack::moveAfter) no parent set" << endl;
+ return;
+ }
+
+ // make sure we do not mess up the list
+ if( doc()->lastTrack() )
+ moveAfter( doc()->lastTrack() );
+ else {
+ doc()->setFirstTrack( take() );
+ doc()->setLastTrack( this );
+ }
+ }
+ else if( track == this ) {
+ kdDebug() << "(K3bAudioTrack::moveAfter) trying to move this after this." << endl;
+ return;
+ }
+ else {
+ // remove this from the list
+ take();
+
+ // set the new parent doc
+ m_parent = track->doc();
+
+ K3bAudioTrack* oldNext = track->m_next;
+
+ // set track as prev
+ track->m_next = this;
+ m_prev = track;
+
+ // set oldNext as next
+ if( oldNext )
+ oldNext->m_prev = this;
+ m_next = oldNext;
+
+ if( !m_prev )
+ doc()->setFirstTrack( this );
+ if( !m_next )
+ doc()->setLastTrack( this );
+ }
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::moveAhead( K3bAudioTrack* track )
+{
+ if( !track ) {
+ if( !doc() ) {
+ kdDebug() << "(K3bAudioTrack::moveAfter) no parent set" << endl;
+ return;
+ }
+
+ // make sure we do not mess up the list
+ if( doc()->firstTrack() )
+ moveAhead( doc()->firstTrack() );
+ else {
+ doc()->setFirstTrack( take() );
+ doc()->setLastTrack( this );
+ }
+ }
+ else if( track == this ) {
+ kdDebug() << "(K3bAudioTrack::moveAhead) trying to move this ahead of this." << endl;
+ return;
+ }
+ else {
+ // remove this from the list
+ take();
+
+ // set the new parent doc
+ m_parent = track->doc();
+
+ K3bAudioTrack* oldPrev = track->m_prev;
+
+ // set track as next
+ m_next = track;
+ track->m_prev = this;
+
+ // set oldPrev as prev
+ m_prev = oldPrev;
+ if( oldPrev )
+ oldPrev->m_next = this;
+
+ if( !m_prev )
+ doc()->setFirstTrack( this );
+ if( !m_next )
+ doc()->setLastTrack( this );
+ }
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::merge( K3bAudioTrack* trackToMerge, K3bAudioDataSource* sourceAfter )
+{
+ kdDebug() << "(K3bAudioTrack::merge) " << trackToMerge << " into " << this << endl;
+ if( this == trackToMerge ) {
+ kdDebug() << "(K3bAudioTrack::merge) trying to merge this with this." << endl;
+ return;
+ }
+
+ // remove the track to merge to make sure it does not get deleted by the doc too early
+ trackToMerge->take();
+
+ // in case we prepend all of trackToMerge's sources
+ if( !sourceAfter ) {
+ kdDebug() << "(K3bAudioTrack::merge) merging " << trackToMerge->firstSource() << endl;
+ if( m_firstSource ) {
+ trackToMerge->firstSource()->moveAhead( m_firstSource );
+ }
+ else {
+ addSource( trackToMerge->firstSource()->take() );
+ }
+ sourceAfter = m_firstSource;
+ }
+
+ kdDebug() << "(K3bAudioTrack::merge) now merge the other sources." << endl;
+ // now merge all sources into this track
+ while( trackToMerge->firstSource() ) {
+ K3bAudioDataSource* s = trackToMerge->firstSource();
+ kdDebug() << "(K3bAudioTrack::merge) merging source " << s << " from track " << s->track() << " into track "
+ << this << " after source " << sourceAfter << endl;
+ s->moveAfter( sourceAfter );
+ sourceAfter = s;
+ }
+
+ // TODO: should we also merge the indices?
+
+ // now we can safely delete the track we merged
+ delete trackToMerge;
+
+ kdDebug() << "(K3bAudioTrack::merge) finished" << endl;
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setFirstSource( K3bAudioDataSource* source )
+{
+ // reset the reading stuff since this might be a completely new source list
+ m_currentSource = 0;
+ m_alreadyReadBytes = 0;
+
+ m_firstSource = source;
+ while( source ) {
+ source->m_track = this;
+ source = source->next();
+ }
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::addSource( K3bAudioDataSource* source )
+{
+ if( !source )
+ return;
+
+ K3bAudioDataSource* s = m_firstSource;
+ while( s && s->next() )
+ s = s->next();
+ if( s )
+ source->moveAfter( s );
+ else
+ setFirstSource( source->take() );
+}
+
+
+void K3bAudioTrack::sourceChanged( K3bAudioDataSource* )
+{
+ if( m_currentlyDeleting )
+ return;
+
+ // TODO: update indices
+
+ if( m_index0Offset > length() )
+ m_index0Offset = length()-1;
+
+ emitChanged();
+}
+
+
+int K3bAudioTrack::numberSources() const
+{
+ K3bAudioDataSource* source = m_firstSource;
+ int i = 0;
+ while( source ) {
+ source = source->next();
+ ++i;
+ }
+ return i;
+}
+
+
+bool K3bAudioTrack::seek( const K3b::Msf& msf )
+{
+ K3bAudioDataSource* source = m_firstSource;
+
+ K3b::Msf pos;
+ while( source && pos + source->length() < msf ) {
+ pos += source->length();
+ source = source->next();
+ }
+
+ if( source ) {
+ m_currentSource = source;
+ m_alreadyReadBytes = msf.audioBytes();
+ return source->seek( msf - pos );
+ }
+ else
+ return false;
+}
+
+
+int K3bAudioTrack::read( char* data, unsigned int max )
+{
+ if( !m_currentSource ) {
+ m_currentSource = m_firstSource;
+ if( m_currentSource )
+ m_currentSource->seek(0);
+ m_alreadyReadBytes = 0;
+ }
+
+ int readData = m_currentSource->read( data, max );
+ if( readData == 0 ) {
+ m_currentSource = m_currentSource->next();
+ if( m_currentSource ) {
+ m_currentSource->seek(0);
+ return read( data, max ); // read from next source
+ }
+ }
+
+ m_alreadyReadBytes += readData;
+
+ return readData;
+}
+
+
+K3bAudioTrack* K3bAudioTrack::copy() const
+{
+ K3bAudioTrack* track = new K3bAudioTrack();
+
+ track->m_copy = m_copy;
+ track->m_preEmp = m_preEmp;
+ track->m_index0Offset = m_index0Offset;
+ track->m_cdText = m_cdText;
+ K3bAudioDataSource* source = m_firstSource;
+ while( source ) {
+ track->addSource( source->copy() );
+ source = source->next();
+ }
+
+ return track;
+}
+
+
+K3bAudioTrack* K3bAudioTrack::split( const K3b::Msf& pos )
+{
+ if( pos < length() ) {
+ // search the source
+ // pos will be the first sector of the new track
+ K3b::Msf currentPos;
+ K3bAudioDataSource* source = firstSource();
+ while( source && currentPos + source->length() <= pos ) {
+ currentPos += source->length();
+ source = source->next();
+ }
+
+ K3bAudioDataSource* splitSource = 0;
+ if( currentPos > 0 && currentPos == pos ) {
+ // no need to split a source
+ splitSource = source;
+ }
+ else {
+ splitSource = source->split( pos - currentPos );
+ }
+
+ // the new track should include all sources from splitSource and below
+ K3bAudioTrack* splitTrack = new K3bAudioTrack();
+ splitTrack->m_cdText = m_cdText;
+ source = splitSource;
+ while( source ) {
+ K3bAudioDataSource* addSource = source;
+ source = source->next();
+ splitTrack->addSource( addSource );
+ }
+
+ kdDebug() << "(K3bAudioTrack) moving track " << splitTrack << " after this (" << this << ") with parent " << doc() << endl;
+ splitTrack->moveAfter( this );
+
+ return splitTrack;
+ }
+ else
+ return 0;
+}
+
+
+K3bDevice::Track K3bAudioTrack::toCdTrack() const
+{
+ if( !inList() )
+ return K3bDevice::Track();
+
+ K3b::Msf firstSector;
+ K3bAudioTrack* track = doc()->firstTrack();
+ while( track != this ) {
+ firstSector += track->length();
+ track = track->next();
+ }
+
+ K3bDevice::Track cdTrack( firstSector,
+ firstSector + length() - 1,
+ K3bDevice::Track::AUDIO );
+
+ // FIXME: auch im audiotrack copy permitted
+ cdTrack.setCopyPermitted( !copyProtection() );
+ cdTrack.setPreEmphasis( preEmp() );
+
+ // FIXME: add indices != 0
+
+ // no index 0 for the last track. Or should we allow this???
+ if( doc()->lastTrack() != this )
+ cdTrack.setIndex0( index0() );
+
+ // FIXME: convert to QCString
+ // cdTrack.setIsrc( isrc() );
+
+ return cdTrack;
+}
+
+
+void K3bAudioTrack::debug()
+{
+ kdDebug() << "Track " << this << endl
+ << " Prev: " << m_prev << endl
+ << " Next: " << m_next << endl
+ << " Sources:" << endl;
+ K3bAudioDataSource* s = m_firstSource;
+ while( s ) {
+ kdDebug() << " " << s << " - Prev: " << s->prev() << " Next: " << s->next() << endl;
+ s = s->next();
+ }
+}
+
+
+