File indexing completed on 2024-05-12 04:50:59

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 "k3baudiocdtrackreader.h"
0010 #include "k3baudiocdtracksource.h"
0011 #include "k3baudiodoc.h"
0012 #include "k3baudiotrack.h"
0013 #include "k3bcdparanoialib.h"
0014 #include "k3bcore.h"
0015 #include "k3bdevice.h"
0016 #include "k3btoc.h"
0017 #include "k3bthreadwidget.h"
0018 #include "k3b_i18n.h"
0019 
0020 #include <QDebug>
0021 
0022 #ifdef Q_OS_WIN32
0023 #undef S_OK
0024 #endif
0025 
0026 namespace K3b {
0027 
0028 class AudioCdTrackReader::Private
0029 {
0030 public:
0031     Private( AudioCdTrackSource& s )
0032     :
0033         source( s ),
0034         initialized( false ),
0035         cdParanoiaLib( 0 )
0036     {
0037     }
0038 
0039     AudioCdTrackSource& source;
0040     bool initialized;
0041     QScopedPointer<CdparanoiaLib> cdParanoiaLib;
0042 
0043     bool initParanoia();
0044     void closeParanoia();
0045 };
0046 
0047 
0048 bool AudioCdTrackReader::Private::initParanoia()
0049 {
0050     if( !initialized ) {
0051         if( !cdParanoiaLib )
0052             cdParanoiaLib.reset( CdparanoiaLib::create() );
0053 
0054         if( cdParanoiaLib ) {
0055             Device::Device* device = source.searchForAudioCD();
0056 
0057             // ask here for the cd since searchForAudioCD() may also be called from outside
0058             if( !device ) {
0059                 // could not find the CD, so ask for it
0060                 QString s = i18n("Please insert Audio CD %1%2"
0061                                  ,QString::number(source.discId()),
0062                                  source.cdTitle().isEmpty() || source.cdArtist().isEmpty()
0063                                  ? QString()
0064                                  : " (" + source.cdArtist() + " - " + source.cdTitle() + ')');
0065 
0066                 while( Device::Device* dev = ThreadWidget::selectDevice( source.track()->doc()->view(), s ) ) {
0067                     if( dev->readToc().discId() == source.discId() ) {
0068                         device = dev;
0069                         break;
0070                     }
0071                 }
0072             }
0073 
0074             // user canceled
0075             if( !device )
0076                 return false;
0077 
0078             source.setDevice( device );
0079             k3bcore->blockDevice( device );
0080 
0081             if( source.toc().isEmpty() )
0082                 source.setToc( device->readToc() );
0083 
0084             if( !cdParanoiaLib->initParanoia( device, source.toc() ) ) {
0085                 k3bcore->unblockDevice( device );
0086                 return false;
0087             }
0088 
0089             if( source.doc() ) {
0090                 cdParanoiaLib->setParanoiaMode( source.doc()->audioRippingParanoiaMode() );
0091                 cdParanoiaLib->setNeverSkip( !source.doc()->audioRippingIgnoreReadErrors() );
0092                 cdParanoiaLib->setMaxRetries( source.doc()->audioRippingRetries() );
0093             }
0094 
0095             const int start = source.toc()[source.cdTrackNumber()-1].firstSector().lba();
0096             cdParanoiaLib->initReading( start + source.startOffset().lba(),
0097                                         start + source.lastSector().lba() );
0098 
0099             // we only block during the initialization because we cannot determine the end of the reading process :(
0100             k3bcore->unblockDevice( device );
0101 
0102             initialized = true;
0103             qDebug() << "cdParanoia initialized";
0104         }
0105     }
0106 
0107     return initialized;
0108 }
0109 
0110 
0111 void AudioCdTrackReader::Private::closeParanoia()
0112 {
0113     if( cdParanoiaLib && initialized ) {
0114         cdParanoiaLib->close();
0115     }
0116     initialized = false;
0117 }
0118 
0119 
0120 AudioCdTrackReader::AudioCdTrackReader( AudioCdTrackSource& source, QObject* parent )
0121     : QIODevice( parent ),
0122       d( new Private( source ) )
0123 {
0124 }
0125 
0126 
0127 AudioCdTrackReader::~AudioCdTrackReader()
0128 {
0129     close();
0130 }
0131 
0132 
0133 bool AudioCdTrackReader::open( QIODevice::OpenMode mode )
0134 {
0135     if( !mode.testFlag( QIODevice::WriteOnly ) &&
0136         d->initParanoia() ) {
0137         return QIODevice::open( mode );
0138     }
0139     else {
0140         return false;
0141     }
0142 }
0143 
0144 
0145 void AudioCdTrackReader::close()
0146 {
0147     d->closeParanoia();
0148     QIODevice::close();
0149 }
0150 
0151 
0152 qint64 AudioCdTrackReader::writeData( const char* /*data*/, qint64 /*len*/ )
0153 {
0154     return -1;
0155 }
0156 
0157 
0158 qint64 AudioCdTrackReader::readData( char* data, qint64 /*maxlen*/ )
0159 {
0160     if( d->cdParanoiaLib && d->initialized ) {
0161         int status = 0;
0162         char* buf = d->cdParanoiaLib->read( &status, 0, false /* big endian */ );
0163         if( status == CdparanoiaLib::S_OK ) {
0164             if( buf == 0 ) {
0165                 // done
0166                 d->closeParanoia();
0167                 return -1;
0168             }
0169             else {
0170                 ::memcpy( data, buf, CD_FRAMESIZE_RAW );
0171                 return CD_FRAMESIZE_RAW;
0172             }
0173         }
0174         else {
0175             return -1;
0176         }
0177     }
0178     return -1;
0179 }
0180 
0181 
0182 bool AudioCdTrackReader::isSequential() const
0183 {
0184     return false;
0185 }
0186 
0187 
0188 qint64 AudioCdTrackReader::size() const
0189 {
0190     return d->source.length().audioBytes();
0191 }
0192 
0193 
0194 bool AudioCdTrackReader::seek( qint64 pos )
0195 {
0196     if( d->cdParanoiaLib && d->initialized ) {
0197         Msf msfPos = Msf::fromAudioBytes( pos );
0198         const int start = d->source.toc()[d->source.cdTrackNumber()-1].firstSector().lba();
0199         d->cdParanoiaLib->initReading( start + d->source.startOffset().lba() + msfPos.lba(),
0200                                        start + d->source.lastSector().lba() );
0201         return QIODevice::seek( pos );
0202     }
0203     else {
0204         return false;
0205     }
0206 }
0207 
0208 } // namespace K3b