File indexing completed on 2024-04-28 04:48:24

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 }