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"