1 /*
2     SPDX-FileCopyrightText: 2005-2008 Sebastian Trueg <trueg@k3b.org>
3     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
4     SPDX-FileCopyrightText: 1998-2010 Sebastian Trueg <trueg@k3b.org>
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "k3baudiocdtrackreader.h"
10 #include "k3baudiocdtracksource.h"
11 #include "k3baudiodoc.h"
12 #include "k3baudiotrack.h"
13 #include "k3bcdparanoialib.h"
14 #include "k3bcore.h"
15 #include "k3bdevice.h"
16 #include "k3btoc.h"
17 #include "k3bthreadwidget.h"
18 #include "k3b_i18n.h"
19 
20 #include <QDebug>
21 
22 #ifdef Q_OS_WIN32
23 #undef S_OK
24 #endif
25 
26 namespace K3b {
27 
28 class AudioCdTrackReader::Private
29 {
30 public:
Private(AudioCdTrackSource & s)31     Private( AudioCdTrackSource& s )
32     :
33         source( s ),
34         initialized( false ),
35         cdParanoiaLib( 0 )
36     {
37     }
38 
39     AudioCdTrackSource& source;
40     bool initialized;
41     QScopedPointer<CdparanoiaLib> cdParanoiaLib;
42 
43     bool initParanoia();
44     void closeParanoia();
45 };
46 
47 
initParanoia()48 bool AudioCdTrackReader::Private::initParanoia()
49 {
50     if( !initialized ) {
51         if( !cdParanoiaLib )
52             cdParanoiaLib.reset( CdparanoiaLib::create() );
53 
54         if( cdParanoiaLib ) {
55             Device::Device* device = source.searchForAudioCD();
56 
57             // ask here for the cd since searchForAudioCD() may also be called from outside
58             if( !device ) {
59                 // could not find the CD, so ask for it
60                 QString s = i18n("Please insert Audio CD %1%2"
61                                  ,QString::number(source.discId()),
62                                  source.cdTitle().isEmpty() || source.cdArtist().isEmpty()
63                                  ? QString()
64                                  : " (" + source.cdArtist() + " - " + source.cdTitle() + ')');
65 
66                 while( Device::Device* dev = ThreadWidget::selectDevice( source.track()->doc()->view(), s ) ) {
67                     if( dev->readToc().discId() == source.discId() ) {
68                         device = dev;
69                         break;
70                     }
71                 }
72             }
73 
74             // user canceled
75             if( !device )
76                 return false;
77 
78             source.setDevice( device );
79             k3bcore->blockDevice( device );
80 
81             if( source.toc().isEmpty() )
82                 source.setToc( device->readToc() );
83 
84             if( !cdParanoiaLib->initParanoia( device, source.toc() ) ) {
85                 k3bcore->unblockDevice( device );
86                 return false;
87             }
88 
89             if( source.doc() ) {
90                 cdParanoiaLib->setParanoiaMode( source.doc()->audioRippingParanoiaMode() );
91                 cdParanoiaLib->setNeverSkip( !source.doc()->audioRippingIgnoreReadErrors() );
92                 cdParanoiaLib->setMaxRetries( source.doc()->audioRippingRetries() );
93             }
94 
95             const int start = source.toc()[source.cdTrackNumber()-1].firstSector().lba();
96             cdParanoiaLib->initReading( start + source.startOffset().lba(),
97                                         start + source.lastSector().lba() );
98 
99             // we only block during the initialization because we cannot determine the end of the reading process :(
100             k3bcore->unblockDevice( device );
101 
102             initialized = true;
103             qDebug() << "cdParanoia initialized";
104         }
105     }
106 
107     return initialized;
108 }
109 
110 
closeParanoia()111 void AudioCdTrackReader::Private::closeParanoia()
112 {
113     if( cdParanoiaLib && initialized ) {
114         cdParanoiaLib->close();
115     }
116     initialized = false;
117 }
118 
119 
AudioCdTrackReader(AudioCdTrackSource & source,QObject * parent)120 AudioCdTrackReader::AudioCdTrackReader( AudioCdTrackSource& source, QObject* parent )
121     : QIODevice( parent ),
122       d( new Private( source ) )
123 {
124 }
125 
126 
~AudioCdTrackReader()127 AudioCdTrackReader::~AudioCdTrackReader()
128 {
129     close();
130 }
131 
132 
open(QIODevice::OpenMode mode)133 bool AudioCdTrackReader::open( QIODevice::OpenMode mode )
134 {
135     if( !mode.testFlag( QIODevice::WriteOnly ) &&
136         d->initParanoia() ) {
137         return QIODevice::open( mode );
138     }
139     else {
140         return false;
141     }
142 }
143 
144 
close()145 void AudioCdTrackReader::close()
146 {
147     d->closeParanoia();
148     QIODevice::close();
149 }
150 
151 
writeData(const char *,qint64)152 qint64 AudioCdTrackReader::writeData( const char* /*data*/, qint64 /*len*/ )
153 {
154     return -1;
155 }
156 
157 
readData(char * data,qint64)158 qint64 AudioCdTrackReader::readData( char* data, qint64 /*maxlen*/ )
159 {
160     if( d->cdParanoiaLib && d->initialized ) {
161         int status = 0;
162         char* buf = d->cdParanoiaLib->read( &status, 0, false /* big endian */ );
163         if( status == CdparanoiaLib::S_OK ) {
164             if( buf == 0 ) {
165                 // done
166                 d->closeParanoia();
167                 return -1;
168             }
169             else {
170                 ::memcpy( data, buf, CD_FRAMESIZE_RAW );
171                 return CD_FRAMESIZE_RAW;
172             }
173         }
174         else {
175             return -1;
176         }
177     }
178     return -1;
179 }
180 
181 
isSequential() const182 bool AudioCdTrackReader::isSequential() const
183 {
184     return false;
185 }
186 
187 
size() const188 qint64 AudioCdTrackReader::size() const
189 {
190     return d->source.length().audioBytes();
191 }
192 
193 
seek(qint64 pos)194 bool AudioCdTrackReader::seek( qint64 pos )
195 {
196     if( d->cdParanoiaLib && d->initialized ) {
197         Msf msfPos = Msf::fromAudioBytes( pos );
198         const int start = d->source.toc()[d->source.cdTrackNumber()-1].firstSector().lba();
199         d->cdParanoiaLib->initReading( start + d->source.startOffset().lba() + msfPos.lba(),
200                                        start + d->source.lastSector().lba() );
201         return QIODevice::seek( pos );
202     }
203     else {
204         return false;
205     }
206 }
207 
208 } // namespace K3b
209