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

0001 /*
0002     SPDX-FileCopyrightText: 2005-2008 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 "k3baudiotrackreader.h"
0010 #include "k3baudiodatasource.h"
0011 #include "k3baudiotrack.h"
0012 
0013 #include <QList>
0014 #include <QMutex>
0015 #include <QMutexLocker>
0016 
0017 namespace K3b {
0018 
0019 namespace {
0020     typedef QList< QIODevice* > IODevices;
0021 }
0022 
0023 class AudioTrackReader::Private
0024 {
0025 public:
0026     Private( AudioTrackReader& audioTrackReader, AudioTrack& t );
0027     void slotSourceAdded( int position );
0028     void slotSourceAboutToBeRemoved( int position );
0029 
0030     AudioTrackReader& q;
0031     AudioTrack& track;
0032     IODevices readers;
0033     int current;
0034 
0035     // used to make sure that no seek and read operation occur in parallel
0036     QMutex mutex;
0037 };
0038 
0039 
0040 AudioTrackReader::Private::Private( AudioTrackReader& audioTrackReader, AudioTrack& t )
0041 :
0042     q( audioTrackReader ),
0043     track( t ),
0044     current( -1 )
0045 {
0046 }
0047 
0048 
0049 void AudioTrackReader::Private::slotSourceAdded( int position )
0050 {
0051     if( q.isOpen() ) {
0052         QMutexLocker locker( &mutex );
0053         if( position >= 0 && position <= readers.size() ) { // No mistake here, "position" can have size() value
0054             if( AudioDataSource* source = track.getSource( position ) ) {
0055                 readers.insert( position, source->createReader() );
0056                 readers.at( position )->open( q.openMode() );
0057                 if( position == current )
0058                     readers.at( position )->seek( 0 );
0059             }
0060         }
0061     }
0062 }
0063 
0064 
0065 void AudioTrackReader::Private::slotSourceAboutToBeRemoved( int position )
0066 {
0067     if( q.isOpen() ) {
0068         QMutexLocker locker( &mutex );
0069         if( position >= 0 && position < readers.size() ) {
0070             if( position == current )
0071                 ++current;
0072             readers.removeAt( position );
0073         }
0074     }
0075 }
0076 
0077 
0078 AudioTrackReader::AudioTrackReader( AudioTrack& track, QObject* parent )
0079     : QIODevice( parent ),
0080       d( new Private( *this, track ) )
0081 {
0082     connect( &track, SIGNAL(sourceAdded(int)),
0083              this, SLOT(slotSourceAdded(int)) );
0084     connect( &track, SIGNAL(sourceAboutToBeRemoved(int)),
0085              this, SLOT(slotSourceAboutToBeRemoved(int)) );
0086     connect( &track, SIGNAL(changed()),
0087              this, SLOT(slotTrackChanged()) );
0088 }
0089 
0090 
0091 AudioTrackReader::~AudioTrackReader()
0092 {
0093     close();
0094     d->current = -1;
0095 }
0096 
0097 
0098 const AudioTrack& AudioTrackReader::track() const
0099 {
0100     return d->track;
0101 }
0102 
0103 
0104 AudioTrack& AudioTrackReader::track()
0105 {
0106     return d->track;
0107 }
0108 
0109 
0110 bool AudioTrackReader::open( QIODevice::OpenMode mode )
0111 {
0112     if( !mode.testFlag( QIODevice::WriteOnly ) && d->readers.empty() && d->track.numberSources() > 0 ) {
0113 
0114         for( AudioDataSource* source = d->track.firstSource(); source != 0; source = source->next() ) {
0115             d->readers.push_back( source->createReader() );
0116             if( !d->readers.back()->open( mode ) ) {
0117                 d->readers.clear();
0118                 return false;
0119             }
0120         }
0121 
0122         QIODevice::seek( 0 );
0123         if( !d->readers.isEmpty() ) {
0124             d->current = 0;
0125             d->readers.at( d->current )->seek( 0 );
0126         }
0127 
0128         return QIODevice::open( mode );
0129     }
0130     else {
0131         return false;
0132     }
0133 }
0134 
0135 
0136 void AudioTrackReader::close()
0137 {
0138     qDeleteAll( d->readers );
0139     d->readers.clear();
0140     d->current = -1;
0141     QIODevice::close();
0142 }
0143 
0144 
0145 bool AudioTrackReader::isSequential() const
0146 {
0147     return false;
0148 }
0149 
0150 
0151 qint64 AudioTrackReader::size() const
0152 {
0153     return d->track.length().audioBytes();
0154 }
0155 
0156 
0157 bool AudioTrackReader::seek( qint64 pos )
0158 {
0159     QMutexLocker locker( &d->mutex );
0160 
0161     int next = 0;
0162     qint64 curPos = 0;
0163 
0164     for( ; next < d->readers.size() && curPos + d->readers.at( next )->size() < pos; ++next ) {
0165         curPos += d->readers.at( next )->size();
0166     }
0167 
0168     if( next < d->readers.size() ) {
0169         d->current = next;
0170         d->readers.at( next )->seek( pos - curPos );
0171         return QIODevice::seek( pos );
0172     }
0173     else {
0174         return false;
0175     }
0176 }
0177 
0178 
0179 qint64 AudioTrackReader::writeData( const char* /*data*/, qint64 /*len*/ )
0180 {
0181     return -1;
0182 }
0183 
0184 
0185 qint64 AudioTrackReader::readData( char* data, qint64 maxlen )
0186 {
0187     QMutexLocker locker( &d->mutex );
0188 
0189     while( d->current >= 0 && d->current < d->readers.size() ) {
0190         qint64 readData = d->readers.at( d->current )->read( data, maxlen );
0191 
0192         if( readData >= 0 ) {
0193             return readData;
0194         }
0195         else {
0196             ++d->current;
0197             if( d->current >= 0 && d->current < d->readers.size() ) {
0198                 d->readers.at( d->current )->seek( 0 );
0199             }
0200         }
0201     }
0202 
0203     return -1;
0204 }
0205 
0206 
0207 void AudioTrackReader::slotTrackChanged()
0208 {
0209     QMutexLocker locker( &d->mutex );
0210     if( pos() >= size() && pos() > 0 ) {
0211         QIODevice::seek( size() - 1LL );
0212     }
0213 }
0214 
0215 } // namespace K3b
0216 
0217 #include "moc_k3baudiotrackreader.cpp"