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

0001 /*
0002     SPDX-FileCopyrightText: 2004 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2010 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "k3baudiodocreader.h"
0010 #include "k3baudiodoc.h"
0011 #include "k3baudiotrack.h"
0012 #include "k3baudiotrackreader.h"
0013 
0014 #include <QList>
0015 #include <QMutex>
0016 #include <QMutexLocker>
0017 
0018 namespace K3b {
0019 
0020 namespace {
0021     typedef QList< AudioTrackReader* > AudioTrackReaders;
0022 }
0023 
0024 class AudioDocReader::Private
0025 {
0026 public:
0027     Private( AudioDocReader& audioDocReader, AudioDoc& d );
0028     void setCurrentReader( int position );
0029     void slotTrackAdded( int position );
0030     void slotTrackAboutToBeRemoved( int position );
0031 
0032     AudioDocReader& q;
0033     AudioDoc& doc;
0034     AudioTrackReaders readers;
0035     int current;
0036 
0037     // used to make sure that no seek and read operation occur in parallel
0038     QMutex mutex;
0039 };
0040 
0041 
0042 AudioDocReader::Private::Private( AudioDocReader& audioDocReader, AudioDoc& d )
0043 :
0044     q( audioDocReader ),
0045     doc( d ),
0046     current( -1 )
0047 {
0048 }
0049 
0050 
0051 void AudioDocReader::Private::setCurrentReader( int position )
0052 {
0053     if( position >= 0 && position < readers.size() && position != current ) {
0054         emit q.currentTrackChanged( readers.at( position )->track() );
0055     }
0056     current = position;
0057 }
0058 
0059 
0060 void AudioDocReader::Private::slotTrackAdded( int position )
0061 {
0062     QMutexLocker locker( &mutex );
0063     if( q.isOpen() && position >= 0 && position <= readers.size() ) { // No mistake here, "position" can have size() value
0064         if( AudioTrack* track = doc.getTrack( position + 1 ) ) {
0065             readers.insert( position, new AudioTrackReader( *track ) );
0066             readers.at( position )->open( q.openMode() );
0067             if( position == current )
0068                 readers.at( position )->seek( 0 );
0069         }
0070     }
0071 }
0072 
0073 
0074 void AudioDocReader::Private::slotTrackAboutToBeRemoved( int position )
0075 {
0076     QMutexLocker locker( &mutex );
0077     if( q.isOpen() ) {
0078         if( position >= 0 && position < readers.size() ) {
0079             readers.removeAt( position );
0080             if( position == current ) {
0081                 if( current < readers.size() - 1 )
0082                     setCurrentReader( ++current );
0083                 else
0084                     setCurrentReader( --current );
0085             }
0086         }
0087     }
0088 }
0089 
0090 
0091 AudioDocReader::AudioDocReader( AudioDoc& doc, QObject* parent )
0092     : QIODevice( parent ),
0093       d( new Private( *this, doc ) )
0094 {
0095     connect( &doc, SIGNAL(trackAdded(int)),
0096              this, SLOT(slotTrackAdded(int)) );
0097     connect( &doc, SIGNAL(trackAboutToBeRemoved(int)),
0098              this, SLOT(slotTrackAboutToBeRemoved(int)) );
0099 }
0100 
0101 
0102 AudioDocReader::~AudioDocReader()
0103 {
0104     close();
0105 }
0106 
0107 
0108 AudioTrackReader* AudioDocReader::currentTrackReader() const
0109 {
0110     if( d->current >=0 && d->current < d->readers.size() )
0111         return d->readers.at( d->current );
0112     else
0113         return 0;
0114 }
0115 
0116 
0117 bool AudioDocReader::setCurrentTrack( const AudioTrack& track )
0118 {
0119     for( int position = 0; position < d->readers.size(); ++position ) {
0120         AudioTrackReader* reader = d->readers.at( position );
0121         if( &reader->track() == &track ) {
0122             d->setCurrentReader( position );
0123             updatePos();
0124             reader->seek( 0 );
0125             return true;
0126         }
0127     }
0128     return false;
0129 }
0130 
0131 
0132 bool AudioDocReader::open( QIODevice::OpenMode mode )
0133 {
0134     if( !mode.testFlag( QIODevice::WriteOnly ) && d->readers.empty() && d->doc.numOfTracks() > 0 ) {
0135 
0136         for( AudioTrack* track = d->doc.firstTrack(); track != 0; track = track->next() ) {
0137             d->readers.push_back( new AudioTrackReader( *track ) );
0138             if( !d->readers.back()->open( mode ) ) {
0139                 close();
0140                 return false;
0141             }
0142         }
0143 
0144         QIODevice::seek( 0 );
0145         d->setCurrentReader( 0 );
0146         if( d->current >=0 && d->current < d->readers.size() ) {
0147             d->readers.at( d->current )->seek( 0 );
0148         }
0149 
0150         return QIODevice::open( mode );
0151     }
0152     else {
0153         return false;
0154     }
0155 }
0156 
0157 
0158 void AudioDocReader::close()
0159 {
0160     qDeleteAll( d->readers );
0161     d->readers.clear();
0162     d->current = -1;
0163     QIODevice::close();
0164 }
0165 
0166 
0167 bool AudioDocReader::isSequential() const
0168 {
0169     return false;
0170 }
0171 
0172 
0173 qint64 AudioDocReader::size() const
0174 {
0175     return d->doc.length().audioBytes();
0176 }
0177 
0178 
0179 bool AudioDocReader::seek( qint64 pos )
0180 {
0181     QMutexLocker locker( &d->mutex );
0182 
0183     int reader = 0;
0184     qint64 curPos = 0;
0185 
0186     for( ; reader < d->readers.size() && curPos + d->readers.at( reader )->size() < pos; ++reader ) {
0187         curPos += d->readers.at( reader )->size();
0188     }
0189 
0190     if( reader < d->readers.size() ) {
0191         d->setCurrentReader( reader );
0192         d->readers.at( reader )->seek( pos - curPos );
0193         return QIODevice::seek( pos );
0194     }
0195     else {
0196         return false;
0197     }
0198 }
0199 
0200 
0201 void AudioDocReader::nextTrack()
0202 {
0203     QMutexLocker locker( &d->mutex );
0204     if( d->current >= 0 && d->current < d->readers.size() ) {
0205         d->setCurrentReader( d->current + 1 );
0206         updatePos();
0207         if( d->current >= 0 && d->current < d->readers.size() ) {
0208             d->readers.at( d->current )->seek( 0 );
0209         }
0210     }
0211 }
0212 
0213 
0214 void AudioDocReader::previousTrack()
0215 {
0216     QMutexLocker locker( &d->mutex );
0217     if( d->current >= 0 && d->current < d->readers.size() ) {
0218         d->setCurrentReader( d->current - 1 );
0219         updatePos();
0220         if( d->current >= 0 && d->current < d->readers.size() ) {
0221             d->readers.at( d->current )->seek( 0 );
0222         }
0223     }
0224 }
0225 
0226 
0227 qint64 AudioDocReader::writeData( const char* /*data*/, qint64 /*len*/ )
0228 {
0229     return -1;
0230 }
0231 
0232 
0233 qint64 AudioDocReader::readData( char* data, qint64 maxlen )
0234 {
0235     QMutexLocker locker( &d->mutex );
0236 
0237     while( d->current >= 0 && d->current < d->readers.size() ) {
0238         qint64 readData = d->readers.at( d->current )->read( data, maxlen );
0239 
0240         if( readData >= 0 ) {
0241             return readData;
0242         }
0243         else {
0244             d->setCurrentReader( d->current + 1 );
0245             if( d->current >= 0 && d->current < d->readers.size() ) {
0246                 d->readers.at( d->current )->seek( 0 );
0247             }
0248         }
0249     }
0250 
0251     return -1;
0252 }
0253 
0254 
0255 void AudioDocReader::updatePos()
0256 {
0257     if( d->current >= 0 && d->current < d->readers.size() ) {
0258         qint64 newPos = 0LL;
0259         Q_FOREACH( AudioTrackReader* reader, d->readers ) {
0260             if( reader != d->readers.at( d->current ) )
0261                 newPos += reader->size();
0262             else
0263                 break;
0264         }
0265         QIODevice::seek( newPos );
0266     }
0267 }
0268 
0269 } // namespace K3b
0270 
0271 #include "moc_k3baudiodocreader.cpp"