You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
k3b/libk3b/projects/audiocd/k3baudiotrack.cpp

629 lines
13 KiB

/*
*
* $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 <tqstring.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 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;
}
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 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();
}
}