File indexing completed on 2024-04-28 16:06:17
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 "cddacdio.h" 0009 #include "cddaextractthread.h" 0010 0011 #include <QDebug> 0012 #include <cdio/track.h> 0013 0014 /* some of this code in here is based on k3b 0.8.x sourcecode */ 0015 0016 CDDACDIO::CDDACDIO(QObject *parent) 0017 : QObject(parent) 0018 { 0019 Q_UNUSED(parent); 0020 0021 paranoia = nullptr; 0022 drive = nullptr; 0023 cdio = nullptr; 0024 0025 enableParanoiaNeverSkip(false); 0026 setParanoiaMaxRetries(20); 0027 enableParanoiaFullMode(); 0028 } 0029 0030 CDDACDIO::~CDDACDIO() 0031 { 0032 p_free(); 0033 } 0034 0035 bool CDDACDIO::init(const QString &deviceFile) 0036 { 0037 reset(); 0038 p_free(); 0039 0040 device_file = deviceFile; 0041 0042 drive = cdda_identify(device_file.toLocal8Bit().constData(), 0, nullptr); 0043 if (!drive) { 0044 qDebug() << "Failed to find device."; 0045 Q_EMIT error(i18n("Device error (1)."), i18n("Check your device. Is it really \"%1\"? Also check your permissions on \"%1\".", device_file)); 0046 return false; 0047 } 0048 0049 cdda_open(drive); 0050 paranoia = paranoia_init(drive); 0051 if (paranoia == nullptr) { 0052 Q_EMIT error(i18n("Device error (2)."), i18n("Check your device. Is it really \"%1\"? Also check your permissions on \"%1\".", device_file)); 0053 p_free(); 0054 return false; 0055 } 0056 0057 cdio = cdio_open(device_file.toLocal8Bit().constData(), DRIVER_UNKNOWN); 0058 if (!cdio) { 0059 qDebug() << "Unable to create cdio object."; 0060 Q_EMIT error(i18n("Device error (3)."), i18n("Check your device. Is it really \"%1\"? Also check your permissions on \"%1\".", device_file)); 0061 p_free(); 0062 return false; 0063 } 0064 0065 cdio_drive_read_cap_t read_caps; 0066 cdio_drive_write_cap_t write_caps; 0067 cdio_drive_misc_cap_t misc_caps; 0068 0069 cdio_get_drive_cap(cdio, &read_caps, &write_caps, &misc_caps); 0070 0071 capabilities.clear(); 0072 if (misc_caps & CDIO_DRIVE_CAP_MISC_CLOSE_TRAY) 0073 capabilities.insert(DriveCapability::CLOSE_TRAY); 0074 if (misc_caps & CDIO_DRIVE_CAP_MISC_EJECT) 0075 capabilities.insert(DriveCapability::EJECT); 0076 if (misc_caps & CDIO_DRIVE_CAP_MISC_LOCK) 0077 capabilities.insert(DriveCapability::LOCK); 0078 if (misc_caps & CDIO_DRIVE_CAP_MISC_SELECT_SPEED) 0079 capabilities.insert(DriveCapability::SELECT_SPEED); 0080 if (misc_caps & CDIO_DRIVE_CAP_MISC_SELECT_DISC) 0081 capabilities.insert(DriveCapability::SELECT_DISC); 0082 if (misc_caps & CDIO_DRIVE_CAP_MISC_MULTI_SESSION) 0083 capabilities.insert(DriveCapability::READ_MULTI_SESSION); 0084 if (misc_caps & CDIO_DRIVE_CAP_MISC_MEDIA_CHANGED) 0085 capabilities.insert(DriveCapability::MEDIA_CHANGED); 0086 0087 if (read_caps & CDIO_DRIVE_CAP_READ_CD_DA) 0088 capabilities.insert(DriveCapability::READ_CDDA); 0089 if (read_caps & CDIO_DRIVE_CAP_READ_C2_ERRS) 0090 capabilities.insert(DriveCapability::C2_ERRS); 0091 if (read_caps & CDIO_DRIVE_CAP_READ_MODE2_FORM1) 0092 capabilities.insert(DriveCapability::READ_MODE2_FORM1); 0093 if (read_caps & CDIO_DRIVE_CAP_READ_MODE2_FORM2) 0094 capabilities.insert(DriveCapability::READ_MODE2_FORM2); 0095 if (read_caps & CDIO_DRIVE_CAP_READ_MCN) 0096 capabilities.insert(DriveCapability::READ_MCN); 0097 if (read_caps & CDIO_DRIVE_CAP_READ_ISRC) 0098 capabilities.insert(DriveCapability::READ_ISRC); 0099 0100 p_detect_hardware(); 0101 return true; 0102 } 0103 0104 void CDDACDIO::reset() 0105 { 0106 mcn.clear(); 0107 track_isrcs.clear(); 0108 } 0109 0110 const QString CDDACDIO::getDeviceFile() const 0111 { 0112 return device_file; 0113 } 0114 0115 const QString CDDACDIO::getVendor() const 0116 { 0117 return vendor; 0118 } 0119 0120 const QString CDDACDIO::getModel() const 0121 { 0122 return model; 0123 } 0124 0125 const QString CDDACDIO::getRevision() const 0126 { 0127 return revision; 0128 } 0129 0130 const DriveCapabilities CDDACDIO::getDriveCapabilities() const 0131 { 0132 return capabilities; 0133 } 0134 0135 void CDDACDIO::enableParanoiaFullMode(const bool enabled) 0136 { 0137 if (enabled) 0138 paranoia_mode = PARANOIA_MODE_FULL; 0139 else 0140 paranoia_mode = PARANOIA_MODE_DISABLE; 0141 0142 if (paranoia_never_skip) 0143 paranoia_mode |= PARANOIA_MODE_NEVERSKIP; 0144 else 0145 paranoia_mode &= ~PARANOIA_MODE_NEVERSKIP; 0146 0147 if (paranoia) 0148 paranoia_modeset(paranoia, paranoia_mode); 0149 } 0150 0151 void CDDACDIO::enableParanoiaNeverSkip(const bool never_skip) 0152 { 0153 paranoia_never_skip = never_skip; 0154 if (paranoia_never_skip) 0155 paranoia_mode |= PARANOIA_MODE_NEVERSKIP; 0156 else 0157 paranoia_mode &= ~PARANOIA_MODE_NEVERSKIP; 0158 0159 if (paranoia) 0160 paranoia_modeset(paranoia, paranoia_mode); 0161 } 0162 0163 void CDDACDIO::setParanoiaMaxRetries(int max_retries) 0164 { 0165 paranoia_max_retries = max_retries; 0166 } 0167 0168 qint16 *CDDACDIO::paranoiaRead(void (*callback)(long, paranoia_cb_mode_t)) 0169 { 0170 if (paranoia) 0171 return cdio_paranoia_read_limited(paranoia, callback, paranoia_max_retries); 0172 return nullptr; 0173 } 0174 0175 int CDDACDIO::paranoiaSeek(const int sector, qint32 mode) 0176 { 0177 if (paranoia) 0178 return (int)cdio_paranoia_seek(paranoia, sector, mode); 0179 return -1; 0180 } 0181 0182 bool CDDACDIO::paranoiaError(QString &errorMsg) 0183 { 0184 errorMsg.clear(); 0185 if (drive) { 0186 char *msg = cdio_cddap_errors(drive); 0187 if (msg) { 0188 errorMsg = QString::fromLocal8Bit(msg, -1); 0189 cdio_cddap_free_messages(msg); 0190 return true; 0191 } 0192 } 0193 return false; 0194 } 0195 0196 bool CDDACDIO::mediaChanged() 0197 { 0198 if (cdio) 0199 return cdio_get_media_changed(cdio) == 1; 0200 return false; 0201 } 0202 0203 int CDDACDIO::firstSectorOfTrack(const int track) 0204 { 0205 if (cdio) 0206 return (int)cdio_get_track_lsn(cdio, track); 0207 return -1; 0208 } 0209 0210 int CDDACDIO::lastSectorOfTrack(const int track) 0211 { 0212 if (cdio) 0213 return (int)cdio_get_track_last_lsn(cdio, track); 0214 return -1; 0215 } 0216 0217 int CDDACDIO::firstSectorOfDisc() 0218 { 0219 return 0; 0220 } 0221 0222 int CDDACDIO::lastSectorOfDisc() 0223 { 0224 if (drive) 0225 return (int)cdio_get_disc_last_lsn(cdio) - 1; // last lsn ist start sector of leadout, so -1 0226 return -1; 0227 } 0228 0229 int CDDACDIO::firstTrackNum() 0230 { 0231 if (cdio) 0232 return (int)cdio_get_first_track_num(cdio); 0233 return -1; 0234 } 0235 0236 int CDDACDIO::lastTrackNum() 0237 { 0238 if (cdio) 0239 return (int)cdio_get_last_track_num(cdio); 0240 return -1; 0241 } 0242 0243 int CDDACDIO::numOfTracks() 0244 { 0245 if (cdio) 0246 return (int)cdio_get_num_tracks(cdio); 0247 return -1; 0248 } 0249 0250 int CDDACDIO::numOfAudioTracks() 0251 { 0252 if (cdio && numOfTracks() > 0) { 0253 int num = 0; 0254 for (int t = firstTrackNum(); t <= lastTrackNum(); ++t) 0255 if (isAudioTrack(t)) 0256 ++num; 0257 return num; 0258 } 0259 return 0; 0260 } 0261 0262 int CDDACDIO::length() 0263 { 0264 return numOfFrames() / FRAMES_PER_SECOND; 0265 } 0266 0267 int CDDACDIO::numOfFrames() 0268 { 0269 if (numOfTracks() > 0) { 0270 if (drive) 0271 return cdio_cddap_disc_lastsector(drive); 0272 } 0273 return 0; 0274 } 0275 0276 int CDDACDIO::lengthOfAudioTracks() 0277 { 0278 return lastSectorOfDisc() / FRAMES_PER_SECOND; 0279 } 0280 0281 int CDDACDIO::numOfFramesOfAudioTracks() 0282 { 0283 if (numOfTracks() > 0) { 0284 int frames = 0; 0285 for (int t = firstTrackNum(); t <= lastTrackNum(); ++t) 0286 if (isAudioTrack(t)) 0287 frames += numOfFramesOfTrack(t); 0288 return frames; 0289 } 0290 return 0; 0291 } 0292 0293 int CDDACDIO::numOfSkippedFrames(const int n) 0294 { 0295 if (cdio && numOfTracks() > 0) { 0296 int last = n; 0297 if (last < 1) 0298 last = 1; 0299 if (last > numOfTracks()) 0300 last = numOfTracks(); 0301 int frames = 0; 0302 for (int t = firstTrackNum(); t < last; ++t) 0303 if (!isAudioTrack(t)) 0304 frames += numOfFramesOfTrack(t); 0305 return frames; 0306 } 0307 return 0; 0308 } 0309 0310 int CDDACDIO::lengthOfTrack(const int track) 0311 { 0312 if (cdio && numOfTracks() > 0) { 0313 return numOfFramesOfTrack(track) / FRAMES_PER_SECOND; 0314 } 0315 return 0; 0316 } 0317 0318 int CDDACDIO::numOfFramesOfTrack(const int track) 0319 { 0320 if (cdio && numOfTracks() > 0) { 0321 int t = track; 0322 if (t < 1) 0323 t = 1; 0324 if (t > numOfTracks()) 0325 t = numOfTracks(); 0326 return lastSectorOfTrack(t) - firstSectorOfTrack(t); 0327 } 0328 return 0; 0329 } 0330 0331 const QString CDDACDIO::getMCN() 0332 { 0333 if (!capabilities.contains(DriveCapability::READ_MCN)) 0334 return QString(); 0335 0336 if (cdio && mcn.isEmpty()) { 0337 char *data = cdio_get_mcn(cdio); 0338 QString string = QString::fromLocal8Bit(data, -1); 0339 if (!string.isEmpty() && string != "0") 0340 mcn = string; 0341 cdio_free(data); 0342 } 0343 0344 return mcn; 0345 } 0346 0347 const QString CDDACDIO::getISRC(const int track) 0348 { 0349 if (!capabilities.contains(DriveCapability::READ_ISRC)) 0350 return QString(); 0351 0352 if (cdio && !track_isrcs.contains(track)) { 0353 char *data = cdio_get_track_isrc(cdio, track); 0354 QString string = QString::fromLocal8Bit(data, -1); 0355 if (!string.isEmpty() && string != "0") 0356 track_isrcs.insert(track, string); 0357 cdio_free(data); 0358 } 0359 0360 return track_isrcs.value(track, QString()); 0361 } 0362 0363 void CDDACDIO::fetchAndCacheSubchannelInfo() 0364 { 0365 getMCN(); 0366 for (int t = firstTrackNum(); t < numOfTracks(); ++t) 0367 getISRC(t); 0368 } 0369 0370 bool CDDACDIO::isPreemphasis(const int track) 0371 { 0372 if (drive && isAudioTrack(track)) 0373 return cdio_cddap_track_preemp(drive, track) == 1; 0374 return false; 0375 } 0376 0377 const QString CDDACDIO::msfOfTrack(const int track) 0378 { 0379 return LSN2MSF(firstSectorOfTrack(track)); 0380 } 0381 0382 qreal CDDACDIO::sizeOfTrack(const int track) 0383 { 0384 if (numOfTracks() > 0) { 0385 auto frame_size = (qreal)(numOfFramesOfTrack(track)); 0386 if (isAudioTrack(track)) { 0387 return (frame_size * 2352.0f) / (1024.0f * 1024.0f); 0388 } else { 0389 return (frame_size * 2048.0f) / (1024.0f * 1024.0f); 0390 } 0391 } 0392 return 0.0f; 0393 } 0394 0395 int CDDACDIO::frameOffsetOfTrack(const int track) 0396 { 0397 return firstSectorOfTrack(track); 0398 } 0399 0400 bool CDDACDIO::isAudioTrack(const int track) 0401 { 0402 if (cdio) 0403 return cdio_get_track_format(cdio, track) == TRACK_FORMAT_AUDIO; 0404 return true; 0405 } 0406 0407 bool CDDACDIO::isDataTrack(const int track) 0408 { 0409 if (cdio) 0410 return cdio_get_track_format(cdio, track) == TRACK_FORMAT_DATA; 0411 return true; 0412 } 0413 0414 bool CDDACDIO::isLastTrack(const int track) 0415 { 0416 return track == lastTrackNum(); 0417 } 0418 0419 bool CDDACDIO::isAudioDisc() 0420 { 0421 if (cdio) { 0422 for (track_t t = 1; t <= numOfTracks(); ++t) 0423 if (cdio_get_track_format(cdio, t) != TRACK_FORMAT_AUDIO) 0424 return false; 0425 } else { 0426 return false; 0427 } 0428 return true; 0429 } 0430 0431 QList<quint32> CDDACDIO::discSignature() 0432 { 0433 QList<quint32> result; 0434 0435 if (cdio) 0436 for (track_t t = 1; t <= numOfTracks() + 1; ++t) 0437 result.append(cdio_get_track_lba(cdio, t)); 0438 0439 return result; 0440 } 0441 0442 const QStringList CDDACDIO::prettyTOC() 0443 { 0444 QStringList result; 0445 0446 result.append(i18n("Track --, Start sector: 000000, Size in sectors: 000150, Type: Lead-in")); 0447 0448 if (cdio) 0449 for (track_t t = 1; t <= numOfTracks() + 1; ++t) { 0450 QString track; 0451 track = QString("%1").arg(t, 2, 10, QChar('0')); 0452 if (t == numOfTracks() + 1) 0453 track = "--"; 0454 0455 QString start = QString("%1").arg(cdio_get_track_lba(cdio, t), 6, 10, QChar('0')); 0456 0457 QString size; 0458 int size_i = cdio_get_track_last_lsn(cdio, t) - cdio_get_track_lba(cdio, t) + 150; 0459 if (t == numOfTracks() + 1) { 0460 if (isAudioDisc()) 0461 size_i = 6750; // lead out of an audio disc is always 90 seconds (== 6750 sectors) long 0462 else 0463 size_i = -1; 0464 } else if (t == 0) { 0465 size_i = 150; 0466 } 0467 if (size_i > -1) 0468 size = QString("%1").arg(size_i, 6, 10, QChar('0')); 0469 0470 QString type; 0471 switch (cdio_get_track_format(cdio, t)) { 0472 case TRACK_FORMAT_AUDIO: 0473 type = i18n("Audio"); 0474 break; 0475 case TRACK_FORMAT_CDI: 0476 type = i18n("Non-Audio: CD-i"); 0477 break; 0478 case TRACK_FORMAT_XA: 0479 type = i18n("Non-Audio: Data mode 2"); 0480 if (cdio_get_track_green(cdio, t)) 0481 type += i18n(", form 1"); 0482 else 0483 type += i18n(", form 2"); 0484 break; 0485 case TRACK_FORMAT_DATA: 0486 type = i18n("Non-Audio: Data mode 1"); 0487 break; 0488 case TRACK_FORMAT_PSX: 0489 type = i18n("Non-Audio: Playstation 1"); 0490 break; 0491 case TRACK_FORMAT_ERROR: 0492 type = i18n("Non-Audio: Unknown format"); 0493 break; 0494 } 0495 if (t == numOfTracks() + 1) 0496 type = i18n("Lead-out"); 0497 0498 result.append(i18n("Track %1, Start sector: %2, Size in sectors: %3, Type: %4", track, start, size, type)); 0499 } 0500 0501 return result; 0502 } 0503 0504 void CDDACDIO::p_detect_hardware() 0505 { 0506 char buf[36] = { 0507 0, 0508 }; 0509 mmc_cdb_t cdb = {{ 0510 0, 0511 }}; 0512 0513 CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY); 0514 cdb.field[4] = sizeof(buf); 0515 0516 int i_status = mmc_run_cmd(cdio, 100000, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), &buf); 0517 if (i_status == 0) { 0518 char psz_vendor[CDIO_MMC_HW_VENDOR_LEN + 1]; 0519 char psz_model[CDIO_MMC_HW_MODEL_LEN + 1]; 0520 char psz_rev[CDIO_MMC_HW_REVISION_LEN + 1]; 0521 0522 vendor = QByteArray(buf + 8, sizeof(psz_vendor) - 1).trimmed(); 0523 model = QByteArray(buf + 8 + CDIO_MMC_HW_VENDOR_LEN, sizeof(psz_model) - 1).trimmed(); 0524 revision = QByteArray(buf + 8 + CDIO_MMC_HW_VENDOR_LEN + CDIO_MMC_HW_MODEL_LEN, sizeof(psz_rev) - 1).trimmed(); 0525 0526 qDebug() << "Vendor:" << vendor << ", Model:" << model << ", Revision:" << revision; 0527 0528 } else { 0529 Q_EMIT error(i18n("Could not get device hardware information (vendor, model and firmware revision)."), 0530 i18n("Check your device. Is it really \"%1\"? Also check your permissions on \"%1\".", device_file)); 0531 } 0532 } 0533 0534 void CDDACDIO::p_free() 0535 { 0536 if (paranoia) { 0537 paranoia_free(paranoia); 0538 paranoia = nullptr; 0539 } 0540 if (drive) { 0541 cdio_cddap_close_no_free_cdio(drive); 0542 drive = nullptr; 0543 } 0544 if (cdio) { 0545 cdio_destroy(cdio); 0546 cdio = nullptr; 0547 } 0548 }