File indexing completed on 2024-03-24 04:50:58
0001 /* AUDEX CDDA EXTRACTOR 0002 * SPDX-FileCopyrightText: Copyright (C) 2007 Marco Nelles 0003 * <https://userbase.kde.org/Audex> 0004 * 0005 * SPDX-License-Identifier: GPL-3.0-or-later 0006 */ 0007 0008 #include "cddaextractthread.h" 0009 #include "utils/cddacdio.h" 0010 0011 #include <QDebug> 0012 #include <cdio/sector.h> 0013 0014 static CDDAExtractThread *aet = nullptr; 0015 0016 void paranoia_callback(long sector, paranoia_cb_mode_t status) 0017 { 0018 aet->create_status(sector, status); 0019 } 0020 0021 CDDAExtractThread::CDDAExtractThread(QObject *parent, CDDACDIO *cdio) 0022 : QThread(parent) 0023 { 0024 p_cdio = cdio; 0025 if (!p_cdio) { 0026 qDebug() << "Paranoia object not found. low mem?"; 0027 Q_EMIT error(i18n("Internal device error."), i18n("Check your device and make a bug report.")); 0028 return; 0029 } 0030 connect(p_cdio, SIGNAL(error(const QString &, const QString &)), this, SLOT(slot_error(const QString &, const QString &))); 0031 0032 overall_sectors_read = 0; 0033 paranoia_full_mode = true; 0034 paranoia_retries = 20; 0035 paranoia_never_skip = false; 0036 skip_reading_errors = false; 0037 sample_offset = 0; 0038 track = 1; 0039 b_first_run = true; 0040 b_interrupt = false; 0041 b_error = false; 0042 status_previous_sector = -1; 0043 0044 silence.fill(0, CD_FRAMESIZE_RAW); 0045 } 0046 0047 CDDAExtractThread::~CDDAExtractThread() 0048 { 0049 } 0050 0051 void CDDAExtractThread::start() 0052 { 0053 QThread::start(); 0054 } 0055 0056 void CDDAExtractThread::run() 0057 { 0058 if (!p_cdio) 0059 return; 0060 0061 if (b_interrupt) 0062 return; 0063 0064 b_interrupt = false; 0065 b_error = false; 0066 0067 paranoia_status_count.clear(); 0068 paranoia_status_table.clear(); 0069 0070 if (b_first_run) { 0071 p_log.append(i18n("Drive Vendor: %1, Drive Model: %2, Drive Revision: %3", p_cdio->getVendor(), p_cdio->getModel(), p_cdio->getRevision())); 0072 p_log.append(i18n("TOC:")); 0073 p_log.append(p_cdio->prettyTOC()); 0074 b_first_run = false; 0075 } 0076 0077 if (track == 0) { 0078 first_sector = p_cdio->firstSectorOfDisc(); 0079 last_sector = p_cdio->lastSectorOfDisc(); 0080 } else { 0081 first_sector = p_cdio->firstSectorOfTrack(track) + sector_offset; 0082 last_sector = p_cdio->lastSectorOfTrack(track) + sector_offset; 0083 } 0084 0085 if (first_sector < 0 || last_sector < 0) { 0086 Q_EMIT info(i18n("Extracting finished.")); 0087 return; 0088 } 0089 0090 qDebug() << "Track:" << track; 0091 qDebug() << "Sample offset:" << sample_offset; 0092 qDebug() << "Sector offset:" << sector_offset; 0093 qDebug() << "Sample offset fraction:" << sample_offset_fraction; 0094 qDebug() << "First sector:" << first_sector; 0095 qDebug() << "Last sector:" << last_sector; 0096 0097 // track length 0098 sectors_all = last_sector - first_sector; 0099 sectors_all += sector_offset; 0100 sectors_read = 0; 0101 0102 p_cdio->enableParanoiaFullMode(paranoia_full_mode); 0103 p_cdio->enableParanoiaNeverSkip(paranoia_never_skip); 0104 p_cdio->setParanoiaMaxRetries(paranoia_retries); 0105 0106 QString paranoiaErrorMsg; 0107 0108 p_cdio->paranoiaSeek(first_sector, SEEK_SET); 0109 if (p_cdio->paranoiaError(paranoiaErrorMsg)) { 0110 p_log.append(i18n("Error occured while seeking at sector %1: %2", first_sector, paranoiaErrorMsg)); 0111 if (track > 0) 0112 Q_EMIT error(i18n("An error occured while ripping track %1. See log.", track)); 0113 else 0114 Q_EMIT error(i18n("An error occured while ripping. See log.")); 0115 return; 0116 } 0117 0118 current_sector = first_sector; 0119 0120 if (sample_offset > 0) 0121 p_log.append(i18n("Correction sample offset: %1", sample_offset)); 0122 0123 QString min = QString("%1").arg((sectors_all / SECTORS_PER_SECOND) / 60, 2, 10, QChar('0')); 0124 QString sec = QString("%1").arg((sectors_all / SECTORS_PER_SECOND) % 60, 2, 10, QChar('0')); 0125 0126 // fetch subchannel infos 0127 0128 if (p_cdio->getDriveCapabilities().contains(READ_MCN) || p_cdio->getDriveCapabilities().contains(READ_ISRC)) { 0129 Q_EMIT info(i18n("Fetching extra information from disc...")); 0130 p_cdio->fetchAndCacheSubchannelInfo(); 0131 p_log.append(i18n("Fetching subchannel infos from disc")); 0132 } 0133 0134 if (track > 0) { 0135 Q_EMIT info(i18n("Ripping track %1 (%2:%3)...", track, min, sec)); 0136 p_log.append(i18n("Start reading track %1 with %2 sectors", track, sectors_all)); 0137 } else { 0138 Q_EMIT info(i18n("Ripping whole disc (%1:%2)...", min, sec)); 0139 p_log.append(i18n("Start reading whole disc with %1 sectors", sectors_all)); 0140 p_log.append(i18n("Track %1 with start sector %2", p_cdio->firstTrackNum(), p_cdio->firstSectorOfTrack(p_cdio->firstTrackNum()) + sector_offset)); 0141 } 0142 0143 p_log.append(i18n("First sector: %1, Last sector: %2", first_sector, last_sector)); 0144 0145 p_cdio->mediaChanged(); 0146 0147 bool overread = false; 0148 while (current_sector <= last_sector || overread) { 0149 if (b_interrupt) { 0150 qDebug() << "Interrupt ripping"; 0151 break; 0152 } 0153 0154 // let the global paranoia callback have access to this to emit signals 0155 aet = this; 0156 0157 if (p_cdio->mediaChanged()) { 0158 b_interrupt = true; 0159 continue; 0160 } 0161 0162 int16_t *buf = p_cdio->paranoiaRead(paranoia_callback); 0163 if (p_cdio->paranoiaError(paranoiaErrorMsg)) { 0164 p_log.append(i18n("Error occured while reading sector %1 (track time pos %2): %3", 0165 current_sector, 0166 CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')), 0167 paranoiaErrorMsg)); 0168 if (!skip_reading_errors) { 0169 if (track > 0) 0170 Q_EMIT error(i18n("An error occured while ripping track %1 at position %2. See log.", 0171 track, 0172 CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0173 else 0174 Q_EMIT error(i18n("An error occured while ripping at position %1. See log.", CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0175 b_error = true; 0176 break; 0177 } else { 0178 if (track > 0) 0179 Q_EMIT warning(i18n("An error occured while ripping track %1 at position %2. See log.", 0180 track, 0181 CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0182 else 0183 Q_EMIT warning( 0184 i18n("An error occured while ripping at position %1. See log.", CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0185 } 0186 } 0187 if (!buf) { 0188 if (paranoiaErrorMsg.isEmpty()) { 0189 p_log.append(i18n("Error reading sector %1 (track time pos %2)", current_sector, CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0190 if (paranoia_never_skip) 0191 Q_EMIT error(i18n("An error occured while ripping at position %1. See log.", CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0192 else 0193 Q_EMIT warning( 0194 i18n("An error occured while ripping at position %1. See log.", CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0195 } 0196 if (!skip_reading_errors) { 0197 b_error = true; 0198 break; 0199 } 0200 p_log.append(i18n("Error reading sector %1 (%2): **Filling whole sector with silence**", 0201 current_sector, 0202 CDDACDIO::LSN2MSF(current_sector - first_sector, QChar('-')))); 0203 buf = reinterpret_cast<int16_t *>(silence.data()); 0204 } 0205 0206 if (sample_offset_fraction > 0 && current_sector == first_sector && track > 0) { 0207 Q_EMIT output(QByteArray((const char *)buf + (sample_offset_fraction * 4), CD_FRAMESIZE_RAW - (sample_offset_fraction * 4))); 0208 } else if (sample_offset_fraction < 0 && current_sector == first_sector && track > 0) { 0209 Q_EMIT output(QByteArray((const char *)buf + (CD_FRAMESIZE_RAW - (-sample_offset_fraction * 4)), (-sample_offset_fraction * 4))); 0210 } else if (sample_offset_fraction < 0 && current_sector == last_sector && track > 0) { 0211 Q_EMIT output(QByteArray((const char *)buf, CD_FRAMESIZE_RAW - (-sample_offset_fraction * 4))); 0212 } else if (overread) { 0213 Q_EMIT output(QByteArray((const char *)buf, sample_offset_fraction * 4)); 0214 overread = false; 0215 } else { 0216 Q_EMIT output(QByteArray((const char *)buf, CD_FRAMESIZE_RAW)); 0217 } 0218 0219 // if we have a positive sample offset we need to overread at the end 0220 if (sample_offset > 0 && current_sector == last_sector && track > 0) { 0221 if (p_cdio->mediaChanged()) { 0222 b_interrupt = true; 0223 break; 0224 } 0225 if (p_cdio->isLastTrack(track)) { // if we read into the leadout at the end of the disc then.. 0226 p_cdio->paranoiaSeek(current_sector, SEEK_SET); // flush the buffer (paranoia internal) 0227 if (p_cdio->paranoiaError(paranoiaErrorMsg)) { 0228 p_log.append(i18n("Error occured while seeking at sector %1: %2", current_sector, paranoiaErrorMsg)); 0229 if (track > 0) 0230 Q_EMIT error(i18n("An error occured while ripping track %1. See log.", track)); 0231 else 0232 Q_EMIT error(i18n("An error occured while ripping. See log.")); 0233 b_error = true; 0234 break; 0235 } 0236 } 0237 overread = true; 0238 } 0239 0240 ++current_sector; 0241 0242 ++sectors_read; 0243 ++overall_sectors_read; 0244 float fraction = 0.0f; 0245 if (sectors_all > 0) 0246 fraction = (float)sectors_read / (float)sectors_all; 0247 Q_EMIT progress((int)(100.0f * fraction), current_sector, overall_sectors_read); 0248 } 0249 0250 if (b_error) { 0251 Q_EMIT error(i18n("Ripping was canceled due to an error.")); 0252 p_log.append(i18n("Ripping was canceled due to an error.")); 0253 } else if (b_interrupt) { 0254 Q_EMIT error(i18n("User canceled extracting.")); 0255 p_log.append(i18n("Extraction interrupted")); 0256 } else { 0257 if (track > 0) { 0258 Q_EMIT info(i18n("Ripping OK (Track %1).", track)); 0259 } else { 0260 Q_EMIT info(i18n("Ripping OK.")); 0261 } 0262 p_log.append(i18n("Ripping finished")); 0263 } 0264 } 0265 0266 void CDDAExtractThread::cancel() 0267 { 0268 b_interrupt = true; 0269 } 0270 0271 bool CDDAExtractThread::isProcessing() 0272 { 0273 return !(b_interrupt || !isRunning()); 0274 } 0275 0276 const QStringList &CDDAExtractThread::log() 0277 { 0278 return p_log; 0279 } 0280 0281 void CDDAExtractThread::reset() 0282 { 0283 overall_sectors_read = 0; 0284 paranoia_full_mode = true; 0285 paranoia_retries = 20; 0286 paranoia_never_skip = true; 0287 sample_offset = 0; 0288 track = 1; 0289 b_first_run = true; 0290 b_interrupt = false; 0291 b_error = false; 0292 status_previous_sector = -1; 0293 } 0294 0295 void CDDAExtractThread::slot_error(const QString &message, const QString &details) 0296 { 0297 Q_EMIT error(message, details); 0298 } 0299 0300 void CDDAExtractThread::create_status(long sector, paranoia_cb_mode_t status) 0301 { 0302 if (status == PARANOIA_CB_READ || status == PARANOIA_CB_FINISHED) 0303 return; 0304 0305 paranoia_status_count[status]++; 0306 paranoia_status_table.insert(sector, status); 0307 } 0308 0309 /* Paranoia status explanations: 0310 * 0311 * PARANOIA_CB_READ : No error. 0312 * PARANOIA_CB_VERIFY : No error. Verifying jitter 0313 * PARANOIA_CB_FIXUP_EDGE : Recoverable minor error. Fixed edge jitter. 0314 * PARANOIA_CB_FIXUP_ATOM : Recoverable minor error. Fixed atom jitter. 0315 * PARANOIA_CB_SCRATCH : Unsupported with current paranoia implementation. Should not occur. 0316 * PARANOIA_CB_REPAIR : Unsupported with current paranoia implementation. Should not occur. 0317 * PARANOIA_CB_SKIP : Error. Skipped sector. 0318 * PARANOIA_CB_DRIFT : Error. Skipped sector. 0319 * PARANOIA_CB_BACKOFF : Unsupported with current paranoia implementation. Should not occur. 0320 * PARANOIA_CB_OVERLAP : No error. Dynamic overlap adjust. Sector does not seem to contain the current sector but the amount of overlapped data. 0321 * PARANOIA_CB_FIXUP_DROPPED : Recoverable error. Fixed dropped bytes. 0322 * PARANOIA_CB_FIXUP_DUPED : Recoverable error. Fixed duplicate bytes. 0323 * PARANOIA_CB_READERR : Error. Reading error. 0324 * PARANOIA_CB_CACHEERR : Cache error? 0325 * PARANOIA_CB_WROTE : No error. 0326 * PARANOIA_CB_FINISHED : No error. Just finished ripping. 0327 */