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