/* * * $Id: k3baudiotrack.cpp 620139 2007-01-05 11:59:05Z trueg $ * Copyright (C) 2003 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * 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 #include #include #include #include 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 TQString& a ) { setPerformer( a ); } void K3bAudioTrack::setPerformer( const TQString& a ) { TQString s( a ); d->cdTextValidator->fixup( s ); m_cdText.setPerformer(s); emitChanged(); } void K3bAudioTrack::setTitle( const TQString& t ) { TQString s( t ); d->cdTextValidator->fixup( s ); m_cdText.setTitle(s); emitChanged(); } void K3bAudioTrack::setArranger( const TQString& t ) { TQString s( t ); d->cdTextValidator->fixup( s ); m_cdText.setArranger(s); emitChanged(); } void K3bAudioTrack::setSongwriter( const TQString& t ) { TQString s( t ); d->cdTextValidator->fixup( s ); m_cdText.setSongwriter(s); emitChanged(); } void K3bAudioTrack::setComposer( const TQString& t ) { TQString s( t ); d->cdTextValidator->fixup( s ); m_cdText.setComposer(s); emitChanged(); } void K3bAudioTrack::setIsrc( const TQString& t ) { m_cdText.setIsrc(t); emitChanged(); } void K3bAudioTrack::setCdTextMessage( const TQString& t ) { TQString 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; } TDEIO::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 TQCString // 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(); } }