Hi,as some patches have been committed since i started implementation of netmd support i rebased my local master branch now and updated my local work branch. You can find my work at netmd_integration_latest branch on my github account [1]. Also i made a patch which can be applied to the current master repository attached to this email.
Thomas [1] http://github.com/tharp/linux-minidisc
>From 6444acb4e5fdc7c4b77d37cf26306a639722fe96 Mon Sep 17 00:00:00 2001 From: Thomas Arp <manner.moe@gmx.de> Date: Wed, 9 Jan 2013 23:57:41 +0100 Subject: [PATCH] implement netmd support in qhimdtransfer, also some basic rearrangement needed for switching between himd and netmd devices, move himd transfer routines to new class QHiMDDevice, some minor bugs fixed --- qhimdtransfer/qhimddetection.cpp | 262 +++++++++++++++++--- qhimdtransfer/qhimddetection.h | 106 +++++--- qhimdtransfer/qhimdmainwindow.cpp | 444 ++++++++------------------------- qhimdtransfer/qhimdmainwindow.h | 30 +-- qhimdtransfer/qhimdmainwindow.ui | 18 +- qhimdtransfer/qhimdmodel.cpp | 289 ---------------------- qhimdtransfer/qhimdmodel.h | 68 ----- qhimdtransfer/qhimdtransfer.pro | 13 +- qhimdtransfer/qhimduploaddialog.cpp | 4 +- qhimdtransfer/qhimduploaddialog.h | 4 +- qhimdtransfer/qhimdwindetection.cpp | 203 ++++++++++----- qhimdtransfer/qmddevice.cpp | 480 ++++++++++++++++++++++++++++++++++++ qhimdtransfer/qmddevice.h | 98 ++++++++ qhimdtransfer/qmdmodel.cpp | 358 +++++++++++++++++++++++++++ qhimdtransfer/qmdmodel.h | 80 ++++++ qhimdtransfer/qmdtrack.cpp | 204 +++++++++++++++ qhimdtransfer/qmdtrack.h | 81 ++++++ 17 files changed, 1888 insertions(+), 854 deletions(-) delete mode 100644 qhimdtransfer/qhimdmodel.cpp delete mode 100644 qhimdtransfer/qhimdmodel.h create mode 100644 qhimdtransfer/qmddevice.cpp create mode 100644 qhimdtransfer/qmddevice.h create mode 100644 qhimdtransfer/qmdmodel.cpp create mode 100644 qhimdtransfer/qmdmodel.h create mode 100644 qhimdtransfer/qmdtrack.cpp create mode 100644 qhimdtransfer/qmdtrack.h diff --git a/qhimdtransfer/qhimddetection.cpp b/qhimdtransfer/qhimddetection.cpp index d563587..d0839bb 100644 --- a/qhimdtransfer/qhimddetection.cpp +++ b/qhimdtransfer/qhimddetection.cpp @@ -1,73 +1,279 @@ #include <QtCore/QDebug> #include "qhimddetection.h" +#include <QDebug> + +void QHiMDDetection::clearDeviceList() +{ + QMDDevice * mddev; + QNetMDDevice * nmddev; + int i = 0; + + while( i < dlist.count() ) + { + mddev = dlist.at(i); + if(mddev->deviceType() == NETMD_DEVICE) + { + nmddev = static_cast<QNetMDDevice *>(mddev); + if(nmddev->isOpen()) + nmddev->close(); + delete nmddev; + nmddev = NULL; + dlist.removeAt(i); + continue; + } + else if(mddev->deviceType() == HIMD_DEVICE) + { + remove_himddevice(mddev->path()); // uses platform dependent function if available + continue; + } + } + + if(!dlist.isEmpty()) + dlist.clear(); + emit deviceListChanged(dlist); +} QHiMDDetection::QHiMDDetection(QObject *parent) : QObject(parent) { } -himd_device *QHiMDDetection::find_by_path(QString path) +QHiMDDetection::~QHiMDDetection() { - for (int i = 0; i < device_list.size(); i++) - if(device_list.at(i)->path == path) - return device_list.at(i); + clearDeviceList(); + cleanup_netmd_list(); +} - return NULL; +void QHiMDDetection::cleanup_netmd_list() +{ + if(dev_list != NULL) + netmd_clean(&dev_list); +} + +void QHiMDDetection::rescan_netmd_devices() +{ + QNetMDDevice * dev; + int i = 0; + + // find and remove netmd devices + while(i < dlist.count()) + { + if(dlist.at(i)->deviceType() != NETMD_DEVICE) + { + i++; + continue; + } + dev = static_cast<QNetMDDevice *>(dlist.at(i)); + if(dev->isOpen()) + dev->close(); + + delete dev; + dev = NULL; + dlist.removeAt(i); + } + + netmd_clean(&dev_list); + dev_list = NULL; + + emit deviceListChanged(dlist); + scan_for_netmd_devices(); } -// slots +void QHiMDDetection::scan_for_minidisc_devices() +{ + os_data data; -void QHiMDDetection::himd_busy(QString path) + /* create device entry for disc images first */ + QHiMDDevice * mddev = new QHiMDDevice(); + data.md_inserted = true; + mddev->setName("disc image"); + mddev->setPlatformData(data); + dlist.append(mddev); + emit deviceListChanged(dlist); + + scan_for_himd_devices(); + scan_for_netmd_devices(); +} + +void QHiMDDetection::remove_himddevice(QString path) { - himd_device * dev = find_by_path(path); - if (!dev) + QHiMDDevice * dev = static_cast<QHiMDDevice *>(find_by_path(path)); + int i = dlist.indexOf(find_by_path(path)); + + if(i < 0) return; - dev->is_busy = true; - qDebug() << "himd device at " + dev->path + " : device busy, starting transfer"; + if(dev->isOpen()) + dev->close(); + delete dev; + dev = NULL; + + dlist.removeAt(i); } -void QHiMDDetection::himd_idle(QString path) +void QHiMDDetection::scan_for_netmd_devices() { - himd_device * dev = find_by_path(path); - if (!dev) + netmd_device * md; + netmd_error error = netmd_init(&dev_list); + struct libusb_device_descriptor desc; + QNetMDDevice * mddev; + + if (error != NETMD_NO_ERROR) return; - dev->is_busy = false; - qDebug() << "himd device at " + dev->path + " : device idle, transfer complete"; + md = dev_list; // pick first device + + while( md != NULL) { + libusb_get_device_descriptor(md->usb_dev, &desc); + mddev = new QNetMDDevice(); + mddev->setName(identify_usb_device(desc.idVendor, desc.idProduct)); + mddev->setUsbDevice(md); + dlist.append(mddev); + emit deviceListChanged(dlist); + md = md->link; // pick next device + } +} + +QMDDevice *QHiMDDetection::find_by_path(QString path) +{ + QMDDevice * mddev; + + foreach(mddev, dlist) + { + if(mddev->path() == path) + return mddev; + } + return NULL; +} + +QMDDevice *QHiMDDetection::find_by_name(QString name) +{ + QMDDevice * mddev; + + foreach(mddev, dlist) + { + if(mddev->name() == name) + return mddev; + } + return NULL; } const char * identify_usb_device(int vid, int pid) { + if(vid == SHARP) + { + switch(pid) + { + case IM_MT880H: + return "SHARP IM-MT880H / IM-MT899H (NetMD)"; + case IM_DR400: + return "SHARP IM-DR400 / IM-DR410 (NetMD)"; + case IM_DR80: + return "SHARP IM-DR80 / IM-DR420/ IM-DR580 or KENWOOD DMC-S9NET (NetMD)"; + } + } + if (vid != SONY) return NULL; switch (pid) { - case MZ_NH1: + case MZ_NH1_HIMD: return "SONY MZ-NH1"; - case MZ_NH3D: + case MZ_NH3D_HIMD: return "SONY MZ-NH3D"; - case MZ_NH900: + case MZ_NH900_HIMD: return "SONY MZ-NH900"; - case MZ_NH700: + case MZ_NH700_HIMD: return "SONY MZ-NH700 / MZ-NH800"; - case MZ_NH600: + case MZ_NH600_HIMD: return "SONY MZ-NH600(D)"; - case LAM_3: + case LAM_3_HIMD: return "SONY LAM-3"; - case MZ_DH10P: + case MZ_DH10P_HIMD: return "SONY MZ-DH10P"; - case MZ_RH10: + case MZ_RH10_HIMD: return "SONY MZ-RH10"; - case MZ_RH910: + case MZ_RH910_HIMD: return "SONY MZ-RH910"; - case CMT_AH10: + case CMT_AH10_HIMD: return "SONY CMT-AH10"; - case DS_HMD1: + case DS_HMD1_HIMD: return "SONY DS-HMD1"; - case MZ_RH1: + case MZ_RH1_HIMD: return "SONY MZ-RH1"; + case PCLK_XX: + return "SONY PCLK-XX (NetMD)"; + case UNKNOWN_A: + return "SONY (unknown model, NetMD)"; + case MZ_N1: + return "SONY MZ-N1 (NetMD)"; + case UNKNOWN_B: + return "SONY (unknown model, NetMD)"; + case LAM_1: + return "Sony LAM-1 (NetMD)"; + case MDS_JE780: + return "SONY MDS-JE780 / MDS-JE980 (NetMD)"; + case MZ_N505: + return "SONY MZ-N505 (NetMD)"; + case MZ_S1: + return "SONY MZ-S1 (NetMD)"; + case MZ_N707: + return "SONY MZ-N707 (NetMD)"; + case CMT_C7NT: + return "SONY CMT-C7NT (NetMD)"; + case PCGA_MDN1: + return "SONY PCGA-MDN1 (NetMD)"; + case CMT_L7HD: + return "SONY CMT-L7HD (NetMD)"; + case MZ_N10: + return "SONY MZ-N10 (NetMD)"; + case MZ_N910: + return "SONY MZ-N910 (NetMD)"; + case MZ_N710: + return "SONY MZ-N710 / MZ-NE810 / MZ-NF810 (NetMD)"; + case MZ_N510: + return "SONY MZ-N510 (NetMD)"; + case MZ_NE410: + return "SONY MZ-NE410 / MZ-DN430 / MZ-NF520 (NetMD)"; + case MZ_NE810: + return "SONY MZ-NE810 / MZ-NE910 (NetMD)"; + case CMT_M333NT: + return "SONY CMT-M333NT / CMT_M373NT (NetMD)"; + case LAM_10: + return "SONY LAM-10 (NetMD)"; + case AIWA_AM_NX1: + return "AIWA AM-NX1 (NetMD)"; + case AIWA_AM_NX9: + return "AIWA AM-NX9 (NetMD)"; + case MZ_NH1: + return "SONY MZ-NH1 (NetMD)"; + case MZ_NH3D: + return "SONY MZ-NH3D (NetMD)"; + case MZ_NH900: + return "SONY MZ-NH900 (NetMD)"; + case MZ_NH700: + return "SONY MZ-NH700 / MZ-NH800 (NetMD)"; + case MZ_NH600: + return "SONY MZ-NH600 / MZ-NH600D (NetMD)"; + case MZ_N920: + return "SONY MZ-N920 (NetMD)"; + case LAM_3: + return "SONY LAM-3 (NetMD)"; + case MZ_DH10P: + return "SONY MZ-DH10P (NetMD)"; + case MZ_RH10: + return "SONY MZ-RH10 (NetMD)"; + case MZ_RH910: + return "SONY MZ-RH910 (NetMD)"; + case CMT_AH10_A: + return "SONY CMT-AH10 (NetMD)"; + case CMT_AH10_B: + return "SONY CMT-AH10 (NetMD)"; + case DS_HMD1: + return "SONY DS-HMD1 (NetMD)"; + case MZ_RH1: + return "SONY MZ-RH1 (NetMD)"; } return NULL; } diff --git a/qhimdtransfer/qhimddetection.h b/qhimdtransfer/qhimddetection.h index 5c30be9..9d53305 100644 --- a/qhimdtransfer/qhimddetection.h +++ b/qhimdtransfer/qhimddetection.h @@ -4,52 +4,98 @@ #include <QObject> #include <QList> #include <QString> +#include <qmddevice.h> -#define SONY 0x054c //known himd-mode product IDs -#define MZ_NH1 0x017f -#define MZ_NH3D 0x0181 -#define MZ_NH900 0x0183 -#define MZ_NH700 0x0185 -#define MZ_NH600 0x0187 -#define LAM_3 0x018a -#define MZ_DH10P 0x01ea -#define MZ_RH10 0x021a -#define MZ_RH910 0x021c -#define CMT_AH10 0x022d -#define DS_HMD1 0x023d -#define MZ_RH1 0x0287 - -struct himd_device { - bool is_busy; - QString path; - bool md_inserted; - QString recorder_name; - virtual ~himd_device(){} /* for polymorphic delete */ - }; +// known vendor IDs +#define SONY 0x054c +#define SHARP 0x4dd + +// known himd-mode product IDs +#define MZ_NH1_HIMD 0x017f +#define MZ_NH3D_HIMD 0x0181 +#define MZ_NH900_HIMD 0x0183 +#define MZ_NH700_HIMD 0x0185 +#define MZ_NH600_HIMD 0x0187 +#define LAM_3_HIMD 0x018b +#define MZ_DH10P_HIMD 0x01ea +#define MZ_RH10_HIMD 0x021a +#define MZ_RH910_HIMD 0x021c +#define CMT_AH10_HIMD 0x022d +#define DS_HMD1_HIMD 0x023d +#define MZ_RH1_HIMD 0x0287 + +// known Sony/Aiwa netmd-mode product IDs +#define PCLK_XX 0x34 +#define UNKNOWN_A 0x36 +#define MZ_N1 0x75 +#define UNKNOWN_B 0x7c +#define LAM_1 0x80 +#define MDS_JE780 0x81 // or MDS-JE980 +#define MZ_N505 0x84 +#define MZ_S1 0x85 +#define MZ_N707 0x86 +#define CMT_C7NT 0x8e +#define PCGA_MDN1 0x97 +#define CMT_L7HD 0xad +#define MZ_N10 0xc6 +#define MZ_N910 0xc7 +#define MZ_N710 0xc8 // or MZ-NE810/NF810 +#define MZ_N510 0xc9 // or MZ-NF610 +#define MZ_NE410 0xca // or MZ-DN430/NF520 +#define MZ_NE810 0xeb // or MZ-NE910 +#define CMT_M333NT 0xe7 // or CMT-M373NT +#define LAM_10 0x101 +#define AIWA_AM_NX1 0x113 +#define AIWA_AM_NX9 0x14c +#define MZ_NH1 0x17e +#define MZ_NH3D 0x180 +#define MZ_NH900 0x182 +#define MZ_NH700 0x184 // or MZ-NH800 +#define MZ_NH600 0x186 // or MZ-NH600D +#define MZ_N920 0x188 +#define LAM_3 0x18a +#define MZ_DH10P 0x1e9 +#define MZ_RH10 0x219 +#define MZ_RH910 0x21b +#define CMT_AH10_A 0x21d +#define CMT_AH10_B 0x22c +#define DS_HMD1 0x23c +#define MZ_RH1 0x286 + +// known Sharp netmd-mode product IDs +#define IM_MT880H 0x7202 // or IM-MT899H +#define IM_DR400 0x9013 // or IM-DR410 +#define IM_DR80 0x9014 // or IM-DR420/DR580 / Kenwood DMC-S9NET const char * identify_usb_device(int vid, int pid); +typedef QList<QMDDevice *> QMDDevicePtrList; + class QHiMDDetection : public QObject { Q_OBJECT Q_DISABLE_COPY(QHiMDDetection) protected: - QList<himd_device *> device_list; + QMDDevicePtrList dlist; + netmd_device * dev_list; public: explicit QHiMDDetection(QObject *parent = 0); - virtual ~QHiMDDetection() {} + virtual ~QHiMDDetection(); + virtual void clearDeviceList(); + virtual void cleanup_netmd_list(); + void rescan_netmd_devices(); + void scan_for_minidisc_devices(); virtual void scan_for_himd_devices(){} - himd_device *find_by_path(QString path); - -protected slots: - virtual void himd_busy(QString path); - virtual void himd_idle(QString path); + virtual void remove_himddevice(QString path); + void scan_for_netmd_devices(); + QMDDevice *find_by_path(QString path); + QMDDevice *find_by_name(QString name); signals: - void himd_found(QString path); - void himd_removed(QString path); + void deviceListChanged(QMDDevicePtrList list); }; QHiMDDetection * createDetection(QObject * parent = NULL); #endif // QHIMDDETECTION_H + diff --git a/qhimdtransfer/qhimdmainwindow.cpp b/qhimdtransfer/qhimdmainwindow.cpp index e2675d9..a406ea5 100644 --- a/qhimdtransfer/qhimdmainwindow.cpp +++ b/qhimdtransfer/qhimdmainwindow.cpp @@ -1,213 +1,11 @@ #include "qhimdmainwindow.h" #include "ui_qhimdmainwindow.h" #include "qhimdaboutdialog.h" -#include "qhimduploaddialog.h" #include <QtGui/QMessageBox> -#include <QtGui/QApplication> #include <QtCore/QDebug> -QString QHiMDMainWindow::dumpmp3(const QHiMDTrack & trk, QString file) -{ - QString errmsg; - struct himd_mp3stream str; - struct himderrinfo status; - unsigned int len; - const unsigned char * data; - QFile f(file); - - if(!f.open(QIODevice::ReadWrite)) - { - return tr("Error opening file for MP3 output"); - } - if(!(errmsg = trk.openMpegStream(&str)).isNull()) - { - f.remove(); - return tr("Error opening track: ") + errmsg; - } - while(himd_mp3stream_read_block(&str, &data, &len, NULL, &status) >= 0) - { - if(f.write((const char*)data,len) == -1) - { - errmsg = tr("Error writing audio data"); - goto clean; - } - uploadDialog->blockTransferred(); - QApplication::processEvents(); - if(uploadDialog->upload_canceled()) - { - errmsg = tr("upload aborted by the user"); - goto clean; - } - - } - if(status.status != HIMD_STATUS_AUDIO_EOF) - errmsg = tr("Error reading audio data: ") + status.statusmsg; - -clean: - f.close(); - himd_mp3stream_close(&str); - if(!errmsg.isNull()) - f.remove(); - return errmsg; -} - -static inline TagLib::String QStringToTagString(const QString & s) -{ - return TagLib::String(s.toUtf8().data(), TagLib::String::UTF8); -} - -static void addid3tag(QString title, QString artist, QString album, QString file) -{ -#ifdef Q_OS_WIN - TagLib::FileRef f(file.toStdWString().c_str()); -#else - TagLib::FileRef f(file.toUtf8().data()); -#endif - TagLib::Tag *t = f.tag(); - t->setTitle(QStringToTagString(title)); - t->setArtist(QStringToTagString(artist)); - t->setAlbum(QStringToTagString(album)); - t->setComment("*** imported from HiMD via QHiMDTransfer ***"); - f.file()->save(); -} - -QString QHiMDMainWindow::dumpoma(const QHiMDTrack & track, QString file) -{ - QString errmsg; - struct himd_nonmp3stream str; - struct himderrinfo status; - unsigned int len; - const unsigned char * data; - QFile f(file); - - if(!f.open(QIODevice::ReadWrite)) - return tr("Error opening file for ATRAC output"); - - if(!(errmsg = track.openNonMpegStream(&str)).isNull()) - { - f.remove(); - return tr("Error opening track: ") + status.statusmsg; - } - - if(f.write(track.makeEA3Header()) == -1) - { - errmsg = tr("Error writing header"); - goto clean; - } - while(himd_nonmp3stream_read_block(&str, &data, &len, NULL, &status) >= 0) - { - if(f.write((const char*)data,len) == -1) - { - errmsg = tr("Error writing audio data"); - goto clean; - } - uploadDialog->blockTransferred(); - QApplication::processEvents(); - if(uploadDialog->upload_canceled()) - { - errmsg = QString("upload aborted by the user"); - goto clean; - } - } - if(status.status != HIMD_STATUS_AUDIO_EOF) - errmsg = QString("Error reading audio data: ") + status.statusmsg; - -clean: - f.close(); - himd_nonmp3stream_close(&str); - - if(!errmsg.isNull()) - f.remove(); - return errmsg; -} - -QString QHiMDMainWindow::dumppcm(const QHiMDTrack & track, QString file) -{ - struct himd_nonmp3stream str; - struct himderrinfo status; - unsigned int len, i; - int left, right; - int clipcount; - QString errmsg; - QFile f(file); - const unsigned char * data; - sox_format_t * out; - sox_sample_t soxbuf [HIMD_MAX_PCMFRAME_SAMPLES * 2]; - sox_signalinfo_t signal_out; - - signal_out.channels = 2; - signal_out.length = 0; - signal_out.precision = 16; - signal_out.rate = 44100; - - if(!(out = sox_open_write(file.toUtf8(), &signal_out, NULL, NULL, NULL, NULL))) - return tr("Error opening file for WAV output"); - - if(!(errmsg = track.openNonMpegStream(&str)).isNull()) - { - f.remove(); - return tr("Error opening track: ") + status.statusmsg; - } - - while(himd_nonmp3stream_read_block(&str, &data, &len, NULL, &status) >= 0) - { - - for(i = 0; i < len/4; i++) { - - left = data[i*4]*256+data[i*4+1]; - right = data[i*4+2]*256+data[i*4+3]; - if (left > 0x8000) left -= 0x10000; - if (right > 0x8000) right -= 0x10000; - - soxbuf[i*2] = SOX_SIGNED_16BIT_TO_SAMPLE(left, clipcount); - soxbuf[i*2+1] = SOX_SIGNED_16BIT_TO_SAMPLE(right, clipcount); - (void)clipcount; /* suppess "is unused" warning */ - } - - if (sox_write(out, soxbuf, len/2) != len/2) - { - errmsg = tr("Error writing audio data"); - goto clean; - } - uploadDialog->blockTransferred(); - QApplication::processEvents(); - if(uploadDialog->upload_canceled()) - { - errmsg = QString("upload aborted by the user"); - goto clean; - } - } - if(status.status != HIMD_STATUS_AUDIO_EOF) - errmsg = QString("Error reading audio data: ") + status.statusmsg; - -clean: - sox_close(out); - himd_nonmp3stream_close(&str); - - if(!errmsg.isNull()) - f.remove(); - return errmsg; -} - -void QHiMDMainWindow::checkfile(QString UploadDirectory, QString &filename, QString extension) -{ - QFile f; - QString newname; - int i = 2; - - f.setFileName(UploadDirectory + "/" + filename + extension); - while(f.exists()) - { - newname = filename + " (" + QString::number(i) + ")"; - f.setFileName(UploadDirectory + "/" + newname + extension); - i++; - } - if(!newname.isEmpty()) - filename = newname; -} - void QHiMDMainWindow::set_buttons_enable(bool connect, bool download, bool upload, bool rename, bool del, bool format, bool quit) { ui->action_Connect->setEnabled(connect); @@ -221,13 +19,10 @@ void QHiMDMainWindow::set_buttons_enable(bool connect, bool download, bool uploa ui->download_button->setEnabled(download); } -void QHiMDMainWindow::init_himd_browser() +void QHiMDMainWindow::init_himd_browser(QAbstractListModel * model) { - int i = 0; + ui->TrackList->setModel(model); - ui->TrackList->setModel(&trackmodel); - for(;i < trackmodel.columnCount(); i++) - ui->TrackList->resizeColumnToContents(i); QObject::connect(ui->TrackList->selectionModel(), SIGNAL(selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT(handle_himd_selection_change(const QItemSelection&, const QItemSelection&))); } @@ -237,7 +32,6 @@ void QHiMDMainWindow::init_local_browser() QStringList DownloadFileList; localmodel.setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); localmodel.setNameFilters(QStringList() << "*.mp3" << "*.wav" << "*.oma"); - localmodel.setSelectableExtensions(trackmodel.downloadableFileExtensions()); localmodel.setNameFilterDisables(false); localmodel.setReadOnly(false); localmodel.setRootPath("/"); @@ -259,8 +53,9 @@ void QHiMDMainWindow::save_window_settings() settings.setValue("geometry", QMainWindow::saveGeometry()); settings.setValue("windowState", QMainWindow::saveState()); - for(;i < trackmodel.columnCount(); i++) + for(;i < htmodel.columnCount(); i++) settings.setValue("himd_browser" + QString::number(i), ui->TrackList->columnWidth(i)); + /* TODO: save and restore columnWidth for netmd model */ } void QHiMDMainWindow::read_window_settings() @@ -270,7 +65,7 @@ void QHiMDMainWindow::read_window_settings() QMainWindow::restoreGeometry(settings.value("geometry").toByteArray()); QMainWindow::restoreState(settings.value("windowState").toByteArray()); - for(; i < trackmodel.columnCount(); i++) + for(; i < htmodel.columnCount(); i++) { width = settings.value("himd_browser" + QString::number(i), 0).toInt(); if(width != 0) @@ -280,105 +75,73 @@ void QHiMDMainWindow::read_window_settings() bool QHiMDMainWindow::autodetect_init() { - int k; - - k = QObject::connect(detect, SIGNAL(himd_found(QString)), this, SLOT(himd_found(QString))); - k += QObject::connect(detect, SIGNAL(himd_removed(QString)), this, SLOT(himd_removed(QString))); - - if(!k) + if(!QObject::connect(detect, SIGNAL(deviceListChanged(QMDDevicePtrList)), this, SLOT(device_list_changed(QMDDevicePtrList)))) return false; - QObject::connect(this, SIGNAL(himd_busy(QString)), detect, SLOT(himd_busy(QString))); - QObject::connect(this, SIGNAL(himd_idle(QString)), detect, SLOT(himd_idle(QString))); - - detect->scan_for_himd_devices(); + detect->scan_for_minidisc_devices(); return true; } -void QHiMDMainWindow::open_himd_at(const QString & path) +void QHiMDMainWindow::setCurrentDevice(QMDDevice *dev) { - QMessageBox himdStatus; - QString error; + current_device = dev; + QObject::connect(current_device, SIGNAL(closed()), this, SLOT(current_device_closed())); +} - error = trackmodel.open(path.toAscii()); +void QHiMDMainWindow::open_device(QMDDevice * dev) +{ + QMessageBox mdStatus; + QString error; - if (!error.isNull()) { - himdStatus.setText(tr("Error opening HiMD data. Make sure you chose the proper root directory of your HiMD-Walkman.\n") + error); - himdStatus.exec(); + if(dev->deviceType() == HIMD_DEVICE && dev->path().isEmpty()) + { + mdStatus.setText(tr("Error opening himd device/disc image , no device path given\nPlease use connect button to set the path to the himd device/disc image")); + mdStatus.exec(); set_buttons_enable(1,0,0,0,0,0,1); return; } - ui->himdpath->setText(path); - settings.setValue("lastHiMDDirectory", path); + if(current_device) + current_device_closed(); - himd_device * dev = detect->find_by_path(path); - if(dev) - ui->statusBar->showMessage(dev->recorder_name); + setCurrentDevice(dev); + if(current_device->deviceType() == NETMD_DEVICE) { + error = ntmodel.open(current_device); + init_himd_browser(&ntmodel); + localmodel.setSelectableExtensions(ntmodel.downloadableFileExtensions()); + } + else if(current_device->deviceType() == HIMD_DEVICE) { + error = htmodel.open(current_device); + init_himd_browser(&htmodel); + localmodel.setSelectableExtensions(htmodel.downloadableFileExtensions()); + } else - ui->statusBar->clearMessage(); + { + mdStatus.setText(tr("Error opening minidisc device, unknown/invalid device type given\n")); + mdStatus.exec(); + set_buttons_enable(1,0,0,0,0,0,1); + return; + } + if (!error.isNull()) { + mdStatus.setText(tr("Error opening minidisc device:\n") + error); + mdStatus.exec(); + set_buttons_enable(1,0,0,0,0,0,1); + return; + } + + ui->DiscTitle->setText(current_device->discTitle()); set_buttons_enable(1,0,0,1,1,1,1); } void QHiMDMainWindow::upload_to(const QString & UploadDirectory) { - emit himd_busy(ui->himdpath->text()); + QMDTrackIndexList tlist; - QHiMDTrackList tracks = trackmodel.tracks(ui->TrackList->selectionModel()->selectedRows(0)); + foreach(QModelIndex index, ui->TrackList->selectionModel()->selectedRows(0)) + tlist.append(index.row()); - int allblocks = 0; - for(int i = 0;i < tracks.length(); i++) - allblocks += tracks[i].blockcount(); - - uploadDialog->init(tracks.length(), allblocks); - - for(int i = 0;i < tracks.length(); i++) - { - QString filename, errmsg; - QString title = tracks[i].title(); - if(title.isNull()) - filename = tr("Track %1").arg(tracks[i].tracknum()+1); - else - filename = tracks[i].artist() + " - " + title; - - uploadDialog->starttrack(tracks[i], filename); - if (!tracks[i].copyprotected()) - { - QString codec = tracks[i].codecname(); - if (codec == "MPEG") - { - checkfile(UploadDirectory, filename, ".mp3"); - errmsg = dumpmp3 (tracks[i], UploadDirectory + "/" + filename + ".mp3"); - if(errmsg.isNull()) - addid3tag (tracks[i].title(),tracks[i].artist(),tracks[i].album(), UploadDirectory+ "/" +filename + ".mp3"); - } - else if (codec == "LPCM") - { - checkfile(UploadDirectory, filename, ".wav"); - errmsg = dumppcm (tracks[i], UploadDirectory + "/" + filename + ".wav"); - } - else if (codec == "AT3+" || codec == "AT3 ") - { - checkfile(UploadDirectory, filename, ".oma"); - errmsg = dumpoma (tracks[i], UploadDirectory + "/" + filename + ".oma"); - } - } - else - errmsg = tr("upload disabled because of DRM encryption"); - - if(errmsg.isNull()) - uploadDialog->trackSucceeded(); - else - uploadDialog->trackFailed(errmsg); - - QApplication::processEvents(); - if(uploadDialog->upload_canceled()) - break; - } - uploadDialog->finished(); - - emit himd_idle(ui->himdpath->text()); + current_device->batchUpload(tlist, UploadDirectory); } QHiMDMainWindow::QHiMDMainWindow(QWidget *parent) @@ -386,22 +149,25 @@ QHiMDMainWindow::QHiMDMainWindow(QWidget *parent) { aboutDialog = new QHiMDAboutDialog; formatDialog = new QHiMDFormatDialog; - uploadDialog = new QHiMDUploadDialog; + current_device = NULL; detect = createDetection(this); ui->setupUi(this); ui->updir->setText(settings.value("lastUploadDirectory", QDir::homePath()).toString()); set_buttons_enable(1,0,0,0,0,0,1); - init_himd_browser(); + init_himd_browser(&htmodel); // use himd tracks model as default init_local_browser(); read_window_settings(); - ui->himd_devices->hide(); + ui->himdpath->hide(); if(!autodetect_init()) ui->statusBar->showMessage(" autodetection disabled", 10000); } QHiMDMainWindow::~QHiMDMainWindow() { + if(current_device && current_device->isOpen()) + current_device->close(); + save_window_settings(); delete ui; } @@ -454,8 +220,9 @@ void QHiMDMainWindow::on_action_Format_triggered() void QHiMDMainWindow::on_action_Connect_triggered() { int index; + QHiMDDevice *dev; QString HiMDDirectory; - HiMDDirectory = settings.value("lastHiMDDirectory", QDir::rootPath()).toString(); + HiMDDirectory = settings.value("lastImageDirectory", QDir::rootPath()).toString(); HiMDDirectory = QFileDialog::getExistingDirectory(this, tr("Select directory of HiMD Medium"), HiMDDirectory, @@ -464,21 +231,13 @@ void QHiMDMainWindow::on_action_Connect_triggered() if(HiMDDirectory.isEmpty()) return; - index = ui->himd_devices->findText(HiMDDirectory); - if(index == -1) - { - ui->himd_devices->addItem(HiMDDirectory); - index = ui->himd_devices->findText(HiMDDirectory); - } - ui->himd_devices->setCurrentIndex(index); + index = ui->himd_devices->findText("disc image"); + ui->himd_devices->setCurrentIndex(index); // index of disk image device + dev = (QHiMDDevice *)ui->himd_devices->itemData(index).value<void *>(); + dev->setPath(HiMDDirectory); + ui->himd_devices->setItemText(index, QString((dev->name() + " at " + dev->path() ))); - if(ui->himd_devices->isHidden()) - { - ui->himd_devices->show(); - ui->himdpath->hide(); - } - - open_himd_at(HiMDDirectory); + open_device(dev); } void QHiMDMainWindow::on_upload_button_clicked() @@ -506,67 +265,60 @@ void QHiMDMainWindow::handle_local_selection_change(const QItemSelection&, const } if(localmodel.fileInfo(index).isFile()) - download_possible = trackmodel.is_open(); + download_possible = current_device->isOpen(); ui->action_Download->setEnabled(download_possible); ui->download_button->setEnabled(download_possible); } -void QHiMDMainWindow::himd_found(QString HiMDPath) +void QHiMDMainWindow::device_list_changed(QMDDevicePtrList dplist) { - int index; + QString device; + QMDDevice * dev; - if(HiMDPath.isEmpty()) - return; + /* close current device if it is removed from device list */ + if(current_device != NULL && current_device->isOpen() && !dplist.contains(current_device)) { + current_device_closed(); + } - index = ui->himd_devices->findText(HiMDPath); - if(index == -1) - ui->himd_devices->addItem(HiMDPath); + ui->himd_devices->clear(); - if(ui->himd_devices->isHidden()) + foreach(dev, dplist) { - ui->himd_devices->show(); - ui->himdpath->hide(); + device = QString(dev->deviceType() == NETMD_DEVICE ? dev->name() : dev->name() + " at " + dev->path() ); + ui->himd_devices->addItem(device, qVariantFromValue((void *)dev)); } - if(!trackmodel.is_open()) - { - index = ui->himd_devices->findText(HiMDPath); - ui->himd_devices->setCurrentIndex(index); - open_himd_at(HiMDPath); + if(current_device) + ui->himd_devices->setCurrentIndex(dplist.indexOf(current_device)); + else { + if(dplist.count() > 1) { + ui->himd_devices->setCurrentIndex(1); + open_device(dplist.at(1)); // open first autodetected device + } } - } - -void QHiMDMainWindow::himd_removed(QString HiMDPath) +void QHiMDMainWindow::on_himd_devices_activated(QString device) { - int index; + QMDDevice * dev; + int index = ui->himd_devices->findText(device); - if(HiMDPath.isEmpty()) - return; - if (ui->himdpath->text() == HiMDPath) - { - ui->himdpath->setText(tr("(disconnected)")); - ui->statusBar->clearMessage(); - trackmodel.close(); - } - - index = ui->himd_devices->findText(HiMDPath); - if(index != -1) - { - ui->himd_devices->removeItem(index); - } + dev = (QMDDevice *)ui->himd_devices->itemData(index).value<void *>(); - if(ui->himd_devices->count() == 0) - { - ui->himd_devices->hide(); - ui->himdpath->show(); - } + open_device(dev); } -void QHiMDMainWindow::on_himd_devices_activated(QString device) +void QHiMDMainWindow::current_device_closed() { - open_himd_at(device); + QObject::disconnect(current_device, SIGNAL(closed()), this, SLOT(current_device_closed())); + + if(current_device->deviceType() == NETMD_DEVICE) + ntmodel.close(); + + else if(current_device->deviceType() == HIMD_DEVICE) + htmodel.close(); + + current_device = NULL; } void QHiMDMainWindow::on_download_button_clicked() diff --git a/qhimdtransfer/qhimdmainwindow.h b/qhimdtransfer/qhimdmainwindow.h index 85be8ae..c08d465 100644 --- a/qhimdtransfer/qhimdmainwindow.h +++ b/qhimdtransfer/qhimdmainwindow.h @@ -6,14 +6,9 @@ #include <QtCore/QSettings> #include "qhimdaboutdialog.h" #include "qhimdformatdialog.h" -#include "qhimduploaddialog.h" #include "qhimddetection.h" -#include "qhimdmodel.h" -#include "../libhimd/himd.h" -#include <tlist.h> -#include <fileref.h> -#include <tfile.h> -#include <tag.h> +#include "qmdmodel.h" +#include "qmddevice.h" extern "C" { #include <sox.h> @@ -36,22 +31,20 @@ private: Ui::QHiMDMainWindowClass *ui; QHiMDAboutDialog * aboutDialog; QHiMDFormatDialog * formatDialog; - QHiMDUploadDialog * uploadDialog; QHiMDDetection * detect; - QHiMDTracksModel trackmodel; + QNetMDTracksModel ntmodel; + QHiMDTracksModel htmodel; QHiMDFileSystemModel localmodel; QSettings settings; - QString dumpmp3(const QHiMDTrack & trk, QString file); - QString dumpoma(const QHiMDTrack & trk, QString file); - QString dumppcm(const QHiMDTrack & trk, QString file); - void checkfile(QString UploadDirectory, QString &filename, QString extension); + QMDDevice * current_device; void set_buttons_enable(bool connect, bool download, bool upload, bool rename, bool del, bool format, bool quit); - void init_himd_browser(); + void init_himd_browser(QAbstractListModel *model); void init_local_browser(); void save_window_settings(); void read_window_settings(); bool autodetect_init(); - void open_himd_at(const QString & path); + void setCurrentDevice(QMDDevice * dev); + void open_device(QMDDevice * dev); void upload_to(const QString & path); private slots: @@ -64,14 +57,11 @@ private slots: void on_upload_button_clicked(); void handle_himd_selection_change(const QItemSelection&, const QItemSelection&); void handle_local_selection_change(const QItemSelection&, const QItemSelection&); - void himd_found(QString path); - void himd_removed(QString path); + void device_list_changed(QMDDevicePtrList dplist); void on_himd_devices_activated(QString device); + void current_device_closed(); void on_download_button_clicked(); -signals: - void himd_busy(QString path); - void himd_idle(QString path); }; #endif // QHIMDMAINWINDOW_H diff --git a/qhimdtransfer/qhimdmainwindow.ui b/qhimdtransfer/qhimdmainwindow.ui index b55845f..f394d92 100644 --- a/qhimdtransfer/qhimdmainwindow.ui +++ b/qhimdtransfer/qhimdmainwindow.ui @@ -35,7 +35,7 @@ </font> </property> <property name="text"> - <string>HiMD path</string> + <string><html><head/><body><p>minidisc device (path):</p></body></html></string> </property> </widget> </item> @@ -52,6 +52,20 @@ </layout> </item> <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="disc_title_text"> + <property name="text"> + <string><html><head/><body><p><span style=" font-weight:600;">disc title:</span></p></body></html></string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="DiscTitle"/> + </item> + </layout> + </item> + <item> <widget class="QTreeView" name="TrackList"> <property name="editTriggers"> <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> @@ -174,7 +188,7 @@ <x>0</x> <y>0</y> <width>784</width> - <height>18</height> + <height>20</height> </rect> </property> <widget class="QMenu" name="menu_Action"> diff --git a/qhimdtransfer/qhimdmodel.cpp b/qhimdtransfer/qhimdmodel.cpp deleted file mode 100644 index ac4bde5..0000000 --- a/qhimdtransfer/qhimdmodel.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include <QtGui/QFont> -#include <QtGui/QFontMetrics> -#include "qhimdmodel.h" - -static QString get_himd_str(struct himd * himd, int idx) -{ - QString outstr; - char * str; - if(!idx) - return QString(); - str = himd_get_string_utf8(himd, idx, NULL, NULL); - if(!str) - return QString(); - - outstr = QString::fromUtf8(str); - himd_free(str); - return outstr; -} - -QHiMDTrack::QHiMDTrack(struct himd * himd, unsigned int trackindex) : himd(himd), trknum(trackindex) -{ - trackslot = himd_get_trackslot(himd, trackindex, NULL); - if(trackslot != 0) - if(himd_get_track_info(himd, trackslot, &ti, NULL) < 0) - trackslot = -1; -} - -unsigned int QHiMDTrack::tracknum() const -{ - return trknum; -} - -QString QHiMDTrack::title() const -{ - if(trackslot != 0) - return get_himd_str(himd, ti.title); - else - return QString(); -} - -QString QHiMDTrack::artist() const -{ - if(trackslot != 0) - return get_himd_str(himd, ti.artist); - else - return QString(); -} - -QString QHiMDTrack::album() const -{ - if(trackslot != 0) - return get_himd_str(himd, ti.album); - else - return QString(); -} - -QString QHiMDTrack::codecname() const -{ - if(trackslot != 0) - return himd_get_codec_name(&ti); - else - return QString(); -} - -QTime QHiMDTrack::duration() const -{ - QTime t; - if(trackslot != 0) - return t.addSecs(ti.seconds); - else - return t; -} - -bool QHiMDTrack::copyprotected() const -{ - if(trackslot != 0) - return !himd_track_uploadable(himd, &ti); - return true; -} - -int QHiMDTrack::blockcount() const -{ - if(trackslot != 0) - return himd_track_blocks(himd, &ti, NULL); - else - return 0; -} - -QString QHiMDTrack::openMpegStream(struct himd_mp3stream * str) const -{ - struct himderrinfo status; - if(himd_mp3stream_open(himd, trackslot, str, &status) < 0) - return QString::fromUtf8(status.statusmsg); - return QString(); -} - -QString QHiMDTrack::openNonMpegStream(struct himd_nonmp3stream * str) const -{ - struct himderrinfo status; - if(himd_nonmp3stream_open(himd, trackslot, str, &status) < 0) - return QString::fromUtf8(status.statusmsg); - return QString(); -} - -QByteArray QHiMDTrack::makeEA3Header() const -{ - char header[EA3_FORMAT_HEADER_SIZE]; - make_ea3_format_header(header, &ti.codec_info); - return QByteArray(header,EA3_FORMAT_HEADER_SIZE); -} - -enum columnum { - ColId, ColTitle, ColArtist, ColAlbum, ColLength, ColCodec, ColUploadable, - LAST_columnnum = ColUploadable -}; - -QVariant QHiMDTracksModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if(orientation != Qt::Horizontal) - return QVariant(); - - if(role == Qt::SizeHintRole) - { - static QFont f; - static QFontMetrics met(f); - switch((columnum)section) - { - case ColId: - return QSize(met.width("9999")+5, 0); - case ColTitle: - case ColArtist: - case ColAlbum: - return QSize(25*met.averageCharWidth(), 0); - case ColLength: - return QSize(met.width("9:99:99"), 0); - case ColCodec: - case ColUploadable: - /* Really use the header for the metric in these columns, - contents will be shorter */ - return QAbstractListModel::headerData(section,orientation,role); - } - } - - if(role == Qt::DisplayRole) - { - switch((columnum)section) - { - case ColId: - return tr("Nr."); - case ColTitle: - return tr("Title"); - case ColArtist: - return tr("Artist"); - case ColAlbum: - return tr("Album"); - case ColLength: - return tr("Length"); - case ColCodec: - return tr("Format"); - case ColUploadable: - return tr("Uploadable"); - } - } - return QVariant(); -} - -QVariant QHiMDTracksModel::data(const QModelIndex & index, int role) const -{ - if(role == Qt::TextAlignmentRole && - (index.column() == ColId || index.column() == ColLength)) - return Qt::AlignRight; - - if(index.row() >= rowCount()) - return QVariant(); - - QHiMDTrack track(himd, index.row()); - - if(role == Qt::CheckStateRole && index.column() == ColUploadable) - return track.copyprotected() ? Qt::Unchecked : Qt::Checked; - - if(role == Qt::DisplayRole) - { - switch((columnum)index.column()) - { - case ColId: - return index.row() + 1; - case ColTitle: - return track.title(); - case ColArtist: - return track.artist(); - case ColAlbum: - return track.album(); - case ColLength: - { - QTime t = track.duration(); - if(t < QTime(1,0,0)) - return t.toString("m:ss"); - else - return t.toString("h:mm:ss"); - } - case ColCodec: - return track.codecname(); - case ColUploadable: - return QVariant(); /* Displayed by checkbox */ - } - } - return QVariant(); -} - -int QHiMDTracksModel::rowCount(const QModelIndex &) const -{ - if(himd) - return himd_track_count(himd); - else - return 0; -} - -int QHiMDTracksModel::columnCount(const QModelIndex &) const -{ - return LAST_columnnum+1; -} - -QString QHiMDTracksModel::open(const QString & path) -{ - struct himd * newhimd; - struct himderrinfo status; - - newhimd = new struct himd; - if(himd_open(newhimd,path.toUtf8(), &status) < 0) - { - delete newhimd; - return QString::fromUtf8(status.statusmsg); - } - close(); - himd = newhimd; - reset(); /* inform views that the model contents changed */ - return QString(); -} - -bool QHiMDTracksModel::is_open() -{ - return himd != NULL; -} - -void QHiMDTracksModel::close() -{ - struct himd * oldhimd; - if(!himd) - return; - oldhimd = himd; - himd = NULL; - reset(); /* inform views that the model contents changed */ - himd_close(oldhimd); - delete oldhimd; -} - -QHiMDTrack QHiMDTracksModel::track(int trknum) const -{ - return QHiMDTrack(himd, trknum); -} - -QHiMDTrackList QHiMDTracksModel::tracks(const QModelIndexList & modelindices) const -{ - QHiMDTrackList tracks; - QModelIndex index; - foreach(index, modelindices) - tracks.append(track(index.row())); - return tracks; -} - -QStringList QHiMDTracksModel::downloadableFileExtensions() const -{ - return (QStringList() << "mp3"); -} - -/* QFileSystemModel stuff */ - -Qt::ItemFlags QHiMDFileSystemModel::flags(const QModelIndex &index) const -{ - if(!isDir(index) && !selectableExtensions.contains((fileInfo(index).suffix()), Qt::CaseInsensitive)) - return Qt::NoItemFlags; //not selectable, not enabled (grayed out in the browser) - - return QFileSystemModel::flags(index); -} - -void QHiMDFileSystemModel::setSelectableExtensions(QStringList extensions) -{ - selectableExtensions = extensions; -} diff --git a/qhimdtransfer/qhimdmodel.h b/qhimdtransfer/qhimdmodel.h deleted file mode 100644 index 6ccc2f8..0000000 --- a/qhimdtransfer/qhimdmodel.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef QHIMDMODEL_H -#define QHIMDMODEL_H - -#include <QtCore/QAbstractListModel> -#include <QtCore/QTime> -#include <QtCore/QList> -#include <QtCore/QStringList> -#include <QtGui/QFileSystemModel> -#include "himd.h" - -#include "sony_oma.h" - -class QHiMDTracksModel; - -class QHiMDTrack { - struct himd * himd; - unsigned int trknum; - unsigned int trackslot; - struct trackinfo ti; -public: - QHiMDTrack(struct himd * himd, unsigned int trackindex); - unsigned int tracknum() const; - QString title() const; - QString artist() const; - QString album() const; - QString codecname() const; - QTime duration() const; - bool copyprotected() const; - int blockcount() const; - - QString openMpegStream(struct himd_mp3stream * str) const; - QString openNonMpegStream(struct himd_nonmp3stream * str) const; - QByteArray makeEA3Header() const; -}; - -typedef QList<QHiMDTrack> QHiMDTrackList; - -class QHiMDTracksModel : public QAbstractListModel { - Q_OBJECT - - struct himd *himd; -public: - QHiMDTracksModel() : himd(NULL) {} - /* QAbstractListModel stuff */ - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex & parent = QModelIndex() ) const; - virtual int columnCount(const QModelIndex & parent = QModelIndex() ) const; - /* HiMD containter stuff */ - QString open(const QString & path); /* returns null if OK, error message otherwise */ - bool is_open(); - void close(); - QHiMDTrack track(int trackidx) const; - QHiMDTrackList tracks(const QModelIndexList & indices) const; - QStringList downloadableFileExtensions() const; -}; - -class QHiMDFileSystemModel : public QFileSystemModel { - Q_OBJECT - - QStringList selectableExtensions; -public: - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - void setSelectableExtensions(QStringList extensions); -}; - - -#endif diff --git a/qhimdtransfer/qhimdtransfer.pro b/qhimdtransfer/qhimdtransfer.pro index 236b5e9..ad6c396 100644 --- a/qhimdtransfer/qhimdtransfer.pro +++ b/qhimdtransfer/qhimdtransfer.pro @@ -49,8 +49,10 @@ HEADERS += qhimdaboutdialog.h \ qhimdformatdialog.h \ qhimduploaddialog.h \ qhimdmainwindow.h \ - qhimdmodel.h \ - qhimddetection.h + qhimddetection.h \ + qmdtrack.h \ + qmdmodel.h \ + qmddevice.h FORMS += qhimdaboutdialog.ui \ qhimdformatdialog.ui \ qhimduploaddialog.ui \ @@ -60,8 +62,10 @@ SOURCES += main.cpp \ qhimdformatdialog.cpp \ qhimduploaddialog.cpp \ qhimdmainwindow.cpp \ - qhimdmodel.cpp \ - qhimddetection.cpp + qhimddetection.cpp \ + qmdtrack.cpp \ + qmdmodel.cpp \ + qmddevice.cpp win32:SOURCES += qhimdwindetection.cpp else:SOURCES += qhimddummydetection.cpp RESOURCES += icons.qrc @@ -77,6 +81,7 @@ mac:ICON = qhimdtransfer.icns win32:TARGET = QHiMDTransfer mac:TARGET = QHiMDTransfer include(../libhimd/use_libhimd.pri) +include(../libnetmd/use_libnetmd.prl) # Installing stuff translations.files = $$bracketAll(LANGUAGES, qhimdtransfer_,.qm) diff --git a/qhimdtransfer/qhimduploaddialog.cpp b/qhimdtransfer/qhimduploaddialog.cpp index 4df44ca..13f327e 100644 --- a/qhimdtransfer/qhimduploaddialog.cpp +++ b/qhimdtransfer/qhimduploaddialog.cpp @@ -42,7 +42,7 @@ void QHiMDUploadDialog::finished() return; } -void QHiMDUploadDialog::starttrack(const QHiMDTrack & trk, const QString & title) +void QHiMDUploadDialog::starttrack(const QMDTrack &trk, const QString & title) { tracknum = trk.tracknum() + 1; m_ui->curtrack_label->setText(tr("current track: %1 - %2").arg(tracknum).arg(title)); @@ -64,6 +64,8 @@ void QHiMDUploadDialog::init(int trackcount, int totalblocks) allfinished = 0; m_ui->AllPBar->setRange(0, allblocks); m_ui->AllPBar->reset(); + // reset "canceled" variable, else if last upload was canceled next upload will be canceled automatically + canceled = false; scount = fcount = 0; m_ui->success_text->setText(""); diff --git a/qhimdtransfer/qhimduploaddialog.h b/qhimdtransfer/qhimduploaddialog.h index 86bcd5c..ec7a4d2 100644 --- a/qhimdtransfer/qhimduploaddialog.h +++ b/qhimdtransfer/qhimduploaddialog.h @@ -2,7 +2,7 @@ #define QHIMDUPLOADDIALOG_H #include <QtGui/QDialog> -#include "qhimdmodel.h" +#include "qmdtrack.h" namespace Ui { class QHiMDUploadDialog; @@ -17,7 +17,7 @@ public: bool upload_canceled() { return canceled; } void init(int trackcount, int totalblocks); - void starttrack(const QHiMDTrack & trk, const QString & title); + void starttrack(const QMDTrack & trk, const QString & title); void blockTransferred(); void trackFailed(const QString & errmsg); void trackSucceeded(); diff --git a/qhimdtransfer/qhimdwindetection.cpp b/qhimdtransfer/qhimdwindetection.cpp index 1dc437a..db723ef 100644 --- a/qhimdtransfer/qhimdwindetection.cpp +++ b/qhimdtransfer/qhimdwindetection.cpp @@ -3,8 +3,6 @@ #include <QtGui/QWidget> #include "qhimddetection.h" -#define WINVER 0x0500 - #include <windows.h> #include <dbt.h> #include <setupapi.h> @@ -12,10 +10,10 @@ #include <ddk/ntddstor.h> // needed for handling storage devices #include <ddk/cfgmgr32.h> // needed for CM_Get_Child function -struct win_himd_device : himd_device { - HANDLE devhandle; - HDEVNOTIFY himdChange; - }; +struct win_os_data : os_data { + HANDLE devhandle; + HDEVNOTIFY himdChange; + }; static const GUID my_GUID_IO_MEDIA_ARRIVAL = {0xd07433c0, 0xa98e, 0x11d2, {0x91, 0x7a, 0x00, 0xa0, 0xc9, 0x06, 0x8f, 0xf3} }; @@ -23,6 +21,9 @@ static const GUID my_GUID_IO_MEDIA_ARRIVAL = static const GUID my_GUID_IO_MEDIA_REMOVAL = {0xd07433c1, 0xa98e, 0x11d2, {0x91, 0x7a, 0x00, 0xa0, 0xc9, 0x06, 0x8f, 0xf3} }; +static const GUID my_GUID_DEVINTERFACE_USB_DEVICE = + {0xa5dcbf10, 0x6530, 0x11d2, {0x90, 0x1f, 0x00, 0xc0,0x4f, 0xb9, 0x51, 0xed} }; + static const int my_DBT_CUSTOMEVENT = 0x8006; @@ -37,18 +38,20 @@ public: void scan_for_himd_devices(); QHiMDWinDetection(QObject * parent = NULL); ~QHiMDWinDetection(); - win_himd_device *find_by_path(QString path); + //win_himd_device *find_by_path(QString path); private: HDEVNOTIFY hDevNotify; - win_himd_device *find_by_handle(HANDLE devhandle); - win_himd_device *win_dev_at(int idx); + HDEVNOTIFY listen_usbdev; + QMDDevice *find_by_handle(HANDLE devhandle); void add_himddevice(QString path, QString name); - void remove_himddevice(QString path); + virtual void remove_himddevice(QString path); void add_himd(HANDLE devhandle); void remove_himd(HANDLE devhandle); HDEVNOTIFY register_mediaChange(HANDLE devhandle); void unregister_mediaChange(HDEVNOTIFY himd_change); + HDEVNOTIFY register_usbDeviceNotification(); + void unregister_usbDeviceNotification(); bool winEvent(MSG * msg, long * result); }; @@ -58,17 +61,21 @@ QHiMDDetection * createDetection(QObject * parent) return new QHiMDWinDetection(parent); } -QHiMDWinDetection::QHiMDWinDetection(QObject * parent) +QHiMDWinDetection::QHiMDWinDetection(QObject * parent) : QHiMDDetection(parent), QWidget(0) { // ask for Window ID to have Qt create the window. (void)winId(); + // register for usb device notifications + if((listen_usbdev = register_usbDeviceNotification()) == NULL) + qDebug() << "cannot register usb device notifications" << endl; } QHiMDWinDetection::~QHiMDWinDetection() { - while (!device_list.isEmpty()) - remove_himddevice(device_list.at(0)->path); + unregister_usbDeviceNotification(); + clearDeviceList(); + cleanup_netmd_list(); } void QHiMDWinDetection::scan_for_himd_devices() @@ -96,21 +103,19 @@ void QHiMDWinDetection::scan_for_himd_devices() return; } -win_himd_device *QHiMDWinDetection::win_dev_at(int idx) -{ - return static_cast<win_himd_device*>(device_list.at(idx)); -} - -win_himd_device *QHiMDWinDetection::find_by_path(QString path) +QMDDevice *QHiMDWinDetection::find_by_handle(HANDLE devhandle) { - return static_cast<win_himd_device*>(QHiMDDetection::find_by_path(path)); -} + QMDDevice *mddev; + win_os_data *osd; -win_himd_device *QHiMDWinDetection::find_by_handle(HANDLE devhandle) -{ - for (int i = 0; i < device_list.size(); i++) - if(win_dev_at(i)->devhandle == devhandle) - return win_dev_at(i); + foreach(mddev, dlist) + { + if(mddev->deviceType() != HIMD_DEVICE) + continue; + osd = static_cast<win_os_data *>(mddev->pdata()); + if(osd->devhandle == devhandle) + return mddev; + } return NULL; } @@ -178,6 +183,7 @@ static bool identified(QString devpath, QString & name) int vid = devpath.mid(devpath.indexOf("VID") + 4, 4).toInt(NULL,16); int pid = devpath.mid(devpath.indexOf("PID") + 4, 4).toInt(NULL,16); const char * devname = identify_usb_device(vid, pid); + if (devname) { name = devname; @@ -191,7 +197,8 @@ void QHiMDWinDetection::add_himddevice(QString path, QString name) if (find_by_path(path)) return; - win_himd_device * new_device = new win_himd_device; + QHiMDDevice * new_device = new QHiMDDevice(); + struct win_os_data pdata; int k; char drv[] = "\\\\.\\X:"; QByteArray device = "\\\\.\\PHYSICALDRIVE"; @@ -213,80 +220,93 @@ void QHiMDWinDetection::add_himddevice(QString path, QString name) if(k != 0) device.append(QString::number(sdn.DeviceNumber)); - new_device->devhandle = CreateFileA(device.data(), NULL , FILE_SHARE_READ, NULL, + + pdata.devhandle = CreateFileA(device.data(), NULL , FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if(new_device->devhandle == INVALID_HANDLE_VALUE) + if(pdata.devhandle == INVALID_HANDLE_VALUE) return; - new_device->himdChange = register_mediaChange(new_device->devhandle); - new_device->is_busy = false; - new_device->path = path; - new_device->recorder_name = name; + pdata.himdChange = register_mediaChange(pdata.devhandle); + new_device->setBusy(false); + new_device->setPath(path); + new_device->setName(name); file[0] = path.at(0).toAscii(); if(OpenFile(file, &OFfile, OF_EXIST) != HFILE_ERROR) - { - new_device->md_inserted = true; - emit himd_found(new_device->path); - qDebug() << "himd device at " + new_device->path + " added (" + new_device->recorder_name + ")"; - } + pdata.md_inserted = true; else - { - qDebug() << "himd device at " + new_device->path + " added (" + new_device->recorder_name + ")" + " ; without MD"; - new_device->md_inserted = false; - } + pdata.md_inserted = false; - device_list.append(new_device); + new_device->setPlatformData(pdata); + dlist.append(new_device); + emit deviceListChanged(dlist); return; - } void QHiMDWinDetection::remove_himddevice(QString path) { - win_himd_device * dev = find_by_path(path); + int index = -1; + win_os_data * data; + QHiMDDevice * dev = static_cast<QHiMDDevice *>(find_by_path(path)); + if (!dev) return; - unregister_mediaChange(dev->himdChange); + index = dlist.indexOf(dev); - if (dev->devhandle != NULL) - CloseHandle(dev->devhandle); + if(dev->isOpen()) + dev->close(); - emit himd_removed(dev->path); + if(dev->name() != "disc image") { + data = static_cast<win_os_data *>(dev->pdata()); + if(data->himdChange != NULL) + unregister_mediaChange(data->himdChange); - qDebug() << "himd device at " + dev->path + " removed (" + dev->recorder_name + ")"; + if(data->devhandle != NULL) + CloseHandle(data->devhandle); + } - device_list.removeAll(dev); delete dev; + dev = NULL; + + dlist.removeAt(index); + + emit deviceListChanged(dlist); } void QHiMDWinDetection::add_himd(HANDLE devhandle) { - win_himd_device * dev = find_by_handle(devhandle); + win_os_data * data; + QMDDevice * dev = find_by_handle(devhandle); if (!dev) return; - if(!dev->md_inserted) + data = static_cast<win_os_data *>(dev->pdata()); + + if(!data->md_inserted) { - dev->md_inserted = true; - emit himd_found(dev->path); - qDebug() << "himd device at " + dev->path + " : md inserted"; + data->md_inserted = true; } return; } void QHiMDWinDetection::remove_himd(HANDLE devhandle) { - win_himd_device * dev = find_by_handle(devhandle); + win_os_data * data; + QMDDevice * dev = find_by_handle(devhandle); + if (!dev) return; - if(dev->md_inserted) + data = static_cast<win_os_data *>(dev->pdata()); + + if(dev->isOpen()) + dev->close(); + + if(data->md_inserted) { - dev->md_inserted = false; - emit himd_removed(dev->path); - qDebug() << "himd device at " + dev->path + " : md removed"; + data->md_inserted = false; } return; } @@ -311,9 +331,30 @@ void QHiMDWinDetection::unregister_mediaChange(HDEVNOTIFY himd_change) UnregisterDeviceNotification(himd_change); } +HDEVNOTIFY QHiMDWinDetection::register_usbDeviceNotification() +{ + DEV_BROADCAST_DEVICEINTERFACE filter; + + ZeroMemory(&filter, sizeof(filter)); + filter.dbcc_size = sizeof(filter); + filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + filter.dbcc_reserved = 0; + filter.dbcc_classguid = my_GUID_DEVINTERFACE_USB_DEVICE; + + return RegisterDeviceNotification( this->winId(), &filter, DEVICE_NOTIFY_WINDOW_HANDLE); + +} + +void QHiMDWinDetection::unregister_usbDeviceNotification() +{ + if(listen_usbdev != NULL) + UnregisterDeviceNotification(listen_usbdev); +} + bool QHiMDWinDetection::winEvent(MSG * msg, long * result) { QString name, devID, path ; + if(msg->message == WM_DEVICECHANGE) { PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR )msg->lParam; @@ -335,6 +376,16 @@ bool QHiMDWinDetection::winEvent(MSG * msg, long * result) } } } + else if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { + PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; + devID = QString::fromWCharArray(pDevInf->dbcc_name).toUpper(); + /* only handle netmd devices, himd devices will be handled by DBT_DEVTYP_VOLUME */ + if(identified(devID, name) && name.contains("NetMD)")) { + qDebug() << name << " detected, rescanning netmd devices" << endl; + rescan_netmd_devices(); + } + } break; } case DBT_DEVICEREMOVECOMPLETE : @@ -346,6 +397,15 @@ bool QHiMDWinDetection::winEvent(MSG * msg, long * result) qDebug() << "Message:DBT_DEVICEREMOVECOMPLETE for drive " + path; remove_himddevice(path); } + else if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { + PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; + devID = QString::fromWCharArray(pDevInf->dbcc_name).toUpper(); + if(identified(devID, name) && name.contains("NetMD)")) { + qDebug() << name << " removed, rescanning netmd devices" << endl; + rescan_netmd_devices(); + } + } break; } case DBT_DEVICEQUERYREMOVE : @@ -353,13 +413,13 @@ bool QHiMDWinDetection::winEvent(MSG * msg, long * result) if(pHdr->dbch_devicetype & DBT_DEVTYP_HANDLE) { PDEV_BROADCAST_HANDLE pHdrh = (PDEV_BROADCAST_HANDLE)pHdr; - win_himd_device *dev = find_by_handle(pHdrh->dbch_handle); + QMDDevice *dev = find_by_handle(pHdrh->dbch_handle); if(!dev) { qDebug() << "Message:DBT_DEVICEQUERYREMOVE for unknown device " << pHdrh->dbch_handle; break; } - if(dev->is_busy) + if(dev->isBusy()) { *result = BROADCAST_QUERY_DENY; qDebug() << "Message:DBT_DEVICEQUERYREMOVE for drive " + path + " denied: transfer in progress"; @@ -368,9 +428,24 @@ bool QHiMDWinDetection::winEvent(MSG * msg, long * result) else { qDebug() << "Message:DBT_DEVICEQUERYREMOVE requested"; - remove_himddevice(dev->path); + remove_himddevice(dev->path()); } } + else if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { + PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; + devID = QString::fromWCharArray(pDevInf->dbcc_name).toUpper(); + if(identified(devID, name) && name.contains("NetMD)")) { + QMDDevice * dev = find_by_name(name); + if(!dev) + break; + if(dev->isBusy()) { + *result = BROADCAST_QUERY_DENY; + return true; + } + dev->close(); + } + } break; } case my_DBT_CUSTOMEVENT : diff --git a/qhimdtransfer/qmddevice.cpp b/qhimdtransfer/qmddevice.cpp new file mode 100644 index 0000000..eeb7e78 --- /dev/null +++ b/qhimdtransfer/qmddevice.cpp @@ -0,0 +1,480 @@ +#include <qmddevice.h> +#include <QtGui/QMessageBox> +#include <QtGui/QApplication> +#include <QFile> +#include <tlist.h> +#include <fileref.h> +#include <tfile.h> +#include <tag.h> + +extern "C" { +#include <sox.h> +} + +/* common device members */ +QMDDevice::QMDDevice() : dev_type(NO_DEVICE) +{ +} + +QMDDevice::~QMDDevice() +{ + close(); +} + +enum device_type QMDDevice::deviceType() +{ + return dev_type; +} + +void QMDDevice::setPath(QString path) +{ + device_path = path; +} + +QString QMDDevice::path() +{ + return device_path; +} + +void QMDDevice::setName(QString name) +{ + recorder_name = name; +} + +QString QMDDevice::name() +{ + return recorder_name; +} + +void QMDDevice::setBusy(bool busy) +{ + is_busy = busy; +} + +bool QMDDevice::isBusy() +{ + return is_busy; +} + +void QMDDevice::setPlatformData(os_data pdata) +{ + osdata = pdata; +} + +os_data * QMDDevice::pdata() +{ + os_data * data = &osdata; + return data; +} + +QStringList QMDDevice::downloadableFileExtensions() const +{ + if(dev_type == NETMD_DEVICE) + return QStringList() << "wav"; + + if(dev_type == HIMD_DEVICE) + return QStringList() << "mp3"; + + return QStringList(); +} + +void QMDDevice::checkfile(QString UploadDirectory, QString &filename, QString extension) +{ + QFile f; + QString newname; + int i = 2; + + f.setFileName(UploadDirectory + "/" + filename + extension); + while(f.exists()) + { + newname = filename + " (" + QString::number(i) + ")"; + f.setFileName(UploadDirectory + "/" + newname + extension); + i++; + } + if(!newname.isEmpty()) + filename = newname; +} + + +/* netmd device members */ +QNetMDDevice::QNetMDDevice() +{ + dev_type = NETMD_DEVICE; + devh = NULL; + netmd = NULL; + is_open = false; +} + +QNetMDDevice::~QNetMDDevice() +{ + close(); +} + +void QNetMDDevice::setUsbDevice(netmd_device * dev) +{ + netmd = dev; +} + +QString QNetMDDevice::open() +{ + uint8_t i = 0; + netmd_error error; + char buffer[256]; + + if(!netmd) + return tr("netmd_device not set, use setUsbDevice(...) function first"); + + if((error = netmd_open(netmd, &devh)) != NETMD_NO_ERROR) + return tr("Error opening netmd: %1").arg(netmd_strerror(error)); + + netmd_initialize_disc_info(devh, ¤t_md); + + /* generate track count first, needed by QNetMDTracksModel */ + while(netmd_request_title(devh, i, buffer, sizeof(buffer)) >= 0) + i++; + + trk_count = i; + + is_open = true; + emit opened(); + return QString(); +} + +void QNetMDDevice::close() +{ + if(!devh) + return; + + netmd_clean_disc_info(¤t_md); + netmd_close(devh); + devh = NULL; + + is_open = false; + trk_count = 0; + emit closed(); +} + +QString QNetMDDevice::discTitle() +{ + return QString(current_md.groups[0].name); +} + +QNetMDTrack QNetMDDevice::netmdTrack(unsigned int trkindex) +{ + minidisc * disc = ¤t_md; + + return QNetMDTrack(devh, disc, trkindex); +} + +void QNetMDDevice::batchUpload(QMDTrackIndexList tlist, QString path) +{ + QMessageBox mdStatus; + + mdStatus.setText(tr("netmd uploads are not implemented, yet\n will be comming soon")); + mdStatus.exec(); +} + +/* himd device members */ + +QHiMDDevice::QHiMDDevice() +{ + dev_type = HIMD_DEVICE; + himd = NULL; + is_open = false; +} + +QHiMDDevice::~QHiMDDevice() +{ + close(); +} + +QString QHiMDDevice::open() +{ + struct himderrinfo status; + + if(!pdata()->md_inserted) + return tr("cannot open device, no disc"); + + if(himd) + return QString(); // already opened + + himd = new struct himd; + if(himd_open(himd, device_path.toUtf8(), &status) < 0) + { + delete himd; + himd = NULL; + return QString::fromUtf8(status.statusmsg); + } + + trk_count = himd_track_count(himd); + is_open = true; + emit opened(); + return QString(); +} + +void QHiMDDevice::close() +{ + if(!himd) + return; + + himd_close(himd); + delete himd; + himd = NULL; + + is_open = false; + trk_count = 0; + emit closed(); +} + +QHiMDTrack QHiMDDevice::himdTrack(unsigned int trkindex) +{ + return QHiMDTrack(himd, trkindex); +} + +QString QHiMDDevice::dumpmp3(const QHiMDTrack &trk, QString file) +{ + QString errmsg; + struct himd_mp3stream str; + struct himderrinfo status; + unsigned int len; + const unsigned char * data; + QFile f(file); + + if(!f.open(QIODevice::ReadWrite)) + { + return tr("Error opening file for MP3 output"); + } + if(!(errmsg = trk.openMpegStream(&str)).isNull()) + { + f.remove(); + return tr("Error opening track: ") + errmsg; + } + while(himd_mp3stream_read_block(&str, &data, &len, NULL, &status) >= 0) + { + if(f.write((const char*)data,len) == -1) + { + errmsg = tr("Error writing audio data"); + goto clean; + } + uploadDialog.blockTransferred(); + QApplication::processEvents(); + if(uploadDialog.upload_canceled()) + { + errmsg = tr("upload aborted by the user"); + goto clean; + } + + } + if(status.status != HIMD_STATUS_AUDIO_EOF) + errmsg = tr("Error reading audio data: ") + status.statusmsg; + +clean: + f.close(); + himd_mp3stream_close(&str); + if(!errmsg.isNull()) + f.remove(); + return errmsg; +} + +static inline TagLib::String QStringToTagString(const QString & s) +{ + return TagLib::String(s.toUtf8().data(), TagLib::String::UTF8); +} + +static void addid3tag(QString title, QString artist, QString album, QString file) +{ +#ifdef Q_OS_WIN + TagLib::FileRef f(file.toStdWString().c_str()); +#else + TagLib::FileRef f(file.toUtf8().data()); +#endif + TagLib::Tag *t = f.tag(); + t->setTitle(QStringToTagString(title)); + t->setArtist(QStringToTagString(artist)); + t->setAlbum(QStringToTagString(album)); + t->setComment("*** imported from HiMD via QHiMDTransfer ***"); + f.file()->save(); +} + +QString QHiMDDevice::dumpoma(const QHiMDTrack &track, QString file) +{ + QString errmsg; + struct himd_nonmp3stream str; + struct himderrinfo status; + unsigned int len; + const unsigned char * data; + QFile f(file); + + if(!f.open(QIODevice::ReadWrite)) + return tr("Error opening file for ATRAC output"); + + if(!(errmsg = track.openNonMpegStream(&str)).isNull()) + { + f.remove(); + return tr("Error opening track: ") + status.statusmsg; + } + + if(f.write(track.makeEA3Header()) == -1) + { + errmsg = tr("Error writing header"); + goto clean; + } + while(himd_nonmp3stream_read_block(&str, &data, &len, NULL, &status) >= 0) + { + if(f.write((const char*)data,len) == -1) + { + errmsg = tr("Error writing audio data"); + goto clean; + } + uploadDialog.blockTransferred(); + QApplication::processEvents(); + if(uploadDialog.upload_canceled()) + { + errmsg = QString("upload aborted by the user"); + goto clean; + } + } + if(status.status != HIMD_STATUS_AUDIO_EOF) + errmsg = QString("Error reading audio data: ") + status.statusmsg; + +clean: + f.close(); + himd_nonmp3stream_close(&str); + + if(!errmsg.isNull()) + f.remove(); + return errmsg; +} + +QString QHiMDDevice::dumppcm(const QHiMDTrack &track, QString file) +{ + struct himd_nonmp3stream str; + struct himderrinfo status; + unsigned int len, i; + int left, right; + int clipcount; + QString errmsg; + QFile f(file); + const unsigned char * data; + sox_format_t * out; + sox_sample_t soxbuf [HIMD_MAX_PCMFRAME_SAMPLES * 2]; + sox_signalinfo_t signal_out; + + signal_out.channels = 2; + signal_out.length = 0; + signal_out.precision = 16; + signal_out.rate = 44100; + + if(!(out = sox_open_write(file.toUtf8(), &signal_out, NULL, NULL, NULL, NULL))) + return tr("Error opening file for WAV output"); + + if(!(errmsg = track.openNonMpegStream(&str)).isNull()) + { + f.remove(); + return tr("Error opening track: ") + status.statusmsg; + } + + while(himd_nonmp3stream_read_block(&str, &data, &len, NULL, &status) >= 0) + { + + for(i = 0; i < len/4; i++) { + + left = data[i*4]*256+data[i*4+1]; + right = data[i*4+2]*256+data[i*4+3]; + if (left > 0x8000) left -= 0x10000; + if (right > 0x8000) right -= 0x10000; + + soxbuf[i*2] = SOX_SIGNED_16BIT_TO_SAMPLE(left, clipcount); + soxbuf[i*2+1] = SOX_SIGNED_16BIT_TO_SAMPLE(right, clipcount); + (void)clipcount; /* suppess "is unused" warning */ + } + + if (sox_write(out, soxbuf, len/2) != len/2) + { + errmsg = tr("Error writing audio data"); + goto clean; + } + uploadDialog.blockTransferred(); + QApplication::processEvents(); + if(uploadDialog.upload_canceled()) + { + errmsg = QString("upload aborted by the user"); + goto clean; + } + } + if(status.status != HIMD_STATUS_AUDIO_EOF) + errmsg = QString("Error reading audio data: ") + status.statusmsg; + +clean: + sox_close(out); + himd_nonmp3stream_close(&str); + + if(!errmsg.isNull()) + f.remove(); + return errmsg; +} + +void QHiMDDevice::upload(unsigned int trackidx, QString path) +{ + QString filename, errmsg; + QHiMDTrack track = himdTrack(trackidx); + QString title = track.title(); + + if(title.isNull()) + filename = tr("Track %1").arg(track.tracknum()+1); + else + filename = track.artist() + " - " + title; + + uploadDialog.starttrack(track, filename); + if (!track.copyprotected()) + { + QString codec = track.codecname(); + if (codec == "MPEG") + { + checkfile(path, filename, ".mp3"); + errmsg = dumpmp3 (track, path + "/" + filename + ".mp3"); + if(errmsg.isNull()) + addid3tag (track.title(),track.artist(),track.album(), path + "/" +filename + ".mp3"); + } + else if (codec == "LPCM") + { + checkfile(path, filename, ".wav"); + errmsg = dumppcm (track, path + "/" + filename + ".wav"); + } + else if (codec == "AT3+" || codec == "AT3 ") + { + checkfile(path, filename, ".oma"); + errmsg = dumpoma (track, path + "/" + filename + ".oma"); + } + } + else + errmsg = tr("upload disabled because of DRM encryption"); + + if(errmsg.isNull()) + uploadDialog.trackSucceeded(); + else + uploadDialog.trackFailed(errmsg); + +} + +void QHiMDDevice::batchUpload(QMDTrackIndexList tlist, QString path) +{ + int allblocks = 0; + + setBusy(true); + + for(int i = 0;i < tlist.length(); i++) + allblocks += himdTrack(tlist.at(i)).blockcount(); + + uploadDialog.init(tlist.length(), allblocks); + + for(int i = 0; i < tlist.length(); i++) { + upload(tlist[i], path); + QApplication::processEvents(); + if(uploadDialog.upload_canceled()) + break; + } + + uploadDialog.finished(); + setBusy(false); +} diff --git a/qhimdtransfer/qmddevice.h b/qhimdtransfer/qmddevice.h new file mode 100644 index 0000000..2e92102 --- /dev/null +++ b/qhimdtransfer/qmddevice.h @@ -0,0 +1,98 @@ +#ifndef QMDDEVICE_H +#define QMDDEVICE_H + +#include <QString> +#include <QStringList> +#include <QMetaType> + +#include <qmdtrack.h> +#include "qhimduploaddialog.h" + +enum device_type { + NO_DEVICE, + NETMD_DEVICE, + HIMD_DEVICE +}; + +struct os_data { + bool md_inserted; + virtual ~os_data(){} +}; + +class QMDDevice : public QObject { + Q_OBJECT + Q_DISABLE_COPY(QMDDevice) + + QString recorder_name; + bool is_busy; + struct os_data osdata; +protected: + QString device_path; + enum device_type dev_type; + bool is_open; + unsigned int trk_count; + QHiMDUploadDialog uploadDialog; +public: + explicit QMDDevice(); // + virtual ~QMDDevice(); + virtual enum device_type deviceType(); // + virtual void setPath(QString path); // + virtual QString path(); // + virtual void setName(QString name); // + virtual QString name(); // + virtual void setBusy(bool busy); // + virtual bool isBusy(); // + virtual QString open() {return QString();} + virtual void close() {} + virtual bool isOpen() {return is_open;} + virtual QString discTitle() {return QString();} + virtual void setPlatformData(struct os_data pdata); // + virtual struct os_data * pdata(); // + virtual QMDTrack track(unsigned int trkindex) {return QMDTrack();} + virtual int trackCount() {return trk_count;} + virtual QStringList downloadableFileExtensions() const; // + virtual void checkfile(QString UploadDirectory, QString &filename, QString extension); + virtual void batchUpload(QMDTrackIndexList tlist, QString path) {} + virtual void upload(unsigned int trackidx, QString path) {} + +signals: + void opened(); + void closed(); +}; + +class QNetMDDevice : public QMDDevice { + + netmd_device * netmd; + netmd_dev_handle * devh; + minidisc current_md; +public: + explicit QNetMDDevice(); + virtual ~QNetMDDevice(); + virtual void setUsbDevice(netmd_device * dev); // + virtual QString open(); // + virtual void close(); // + virtual QString discTitle(); // + virtual QNetMDTrack netmdTrack(unsigned int trkindex); + virtual void batchUpload(QMDTrackIndexList tlist, QString path); + virtual void upload(unsigned int trackidx, QString path) {} + +}; + +class QHiMDDevice : public QMDDevice { + + struct himd * himd; +public: + explicit QHiMDDevice(); + virtual ~QHiMDDevice(); + virtual QString open(); // + virtual void close(); // + virtual QHiMDTrack himdTrack(unsigned int trkindex); // + QString dumpmp3(const QHiMDTrack &trk, QString file); + QString dumpoma(const QHiMDTrack & track, QString file); + QString dumppcm(const QHiMDTrack &track, QString file); + virtual void upload(unsigned int trackidx, QString path); + virtual void batchUpload(QMDTrackIndexList tlist, QString path); + +}; + +#endif // QMDDEVICE_H diff --git a/qhimdtransfer/qmdmodel.cpp b/qhimdtransfer/qmdmodel.cpp new file mode 100644 index 0000000..abbdddc --- /dev/null +++ b/qhimdtransfer/qmdmodel.cpp @@ -0,0 +1,358 @@ +#include <QtGui/QFont> +#include <QtGui/QFontMetrics> +#include <qmdmodel.h> + + +enum hcolumnum { + ColId, ColTitle, ColArtist, ColAlbum, ColLength, ColCodec, ColUploadable, + LAST_hcolumnnum = ColUploadable +}; + +enum ncolumnum { + CoId, CoGroup, CoTitle, CoLength, CoCodec, CoUploadable, + LAST_ncolumnnum = CoUploadable +}; + + +/* netmd tracks model */ +QVariant QNetMDTracksModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation != Qt::Horizontal) + return QVariant(); + + if(role == Qt::SizeHintRole) + { + static QFont f; + static QFontMetrics met(f); + switch((ncolumnum)section) + { + case CoId: + return QSize(met.width("9999")+5, 0); + case CoGroup: + case CoTitle: + case CoLength: + return QSize(met.width("9:99:99"), 0); + case CoCodec: + case CoUploadable: + /* Really use the header for the metric in these columns, + contents will be shorter */ + return QAbstractListModel::headerData(section,orientation,role); + } + } + + if(role == Qt::DisplayRole) + { + switch((ncolumnum)section) + { + case CoId: + return tr("Nr."); + case CoGroup: + return tr("Group"); + case CoTitle: + return tr("Title"); + case CoLength: + return tr("Length"); + case CoCodec: + return tr("Format"); + case CoUploadable: + return tr("Uploadable"); + } + } + return QVariant(); +} + +QVariant QNetMDTracksModel::data(const QModelIndex & index, int role) const +{ + if(role == Qt::TextAlignmentRole && + (index.column() == CoId || index.column() == CoLength)) + return Qt::AlignRight; + + if(index.row() >= rowCount()) + return QVariant(); + + QNetMDTrack track = allTracks[index.row()]; + + if(role == Qt::CheckStateRole && index.column() == CoUploadable) + return track.copyprotected() ? Qt::Unchecked : Qt::Checked; + + if(role == Qt::DisplayRole) + { + switch((ncolumnum)index.column()) + { + case CoId: + return index.row() + 1; + case CoGroup: + return track.group(); + case CoTitle: + return track.title(); + case CoLength: + { + QTime t = track.duration(); + if(t < QTime(1,0,0)) + return t.toString("m:ss"); + else + return t.toString("h:mm:ss"); + } + case CoCodec: + return track.codecname(); + case CoUploadable: + return QVariant(); /* Displayed by checkbox */ + } + } + return QVariant(); +} + +int QNetMDTracksModel::rowCount(const QModelIndex &) const +{ + if(ndev == NULL) + return 0; + + return ndev->trackCount(); +} + +int QNetMDTracksModel::columnCount(const QModelIndex &) const +{ + return LAST_ncolumnnum+1; +} + +QString QNetMDTracksModel::open(QMDDevice * device) +{ + int i = 0; + QString ret = "error opening net device"; + + if(ndev != NULL) + close(); + + if(device->deviceType() == NETMD_DEVICE) + { + ndev = static_cast<QNetMDDevice *>(device); + ret = ndev->open(); + } + + if(!ret.isEmpty()) + close(); + + /* fetch track info for all tracks first, getting track info inside data() function is very slow */ + for(; i < ndev->trackCount(); i++) + allTracks.append(ndev->netmdTrack(i)); + + reset(); /* inform views that the model contents changed */ + return ret; +} + +bool QNetMDTracksModel::is_open() +{ + return ndev->isOpen(); +} + +void QNetMDTracksModel::close() +{ + if(ndev != NULL && ndev->isOpen()) + ndev->close(); + + ndev = NULL; + + allTracks.clear(); + reset(); /* inform views that the model contents changed */ +} + +QNetMDTrack QNetMDTracksModel::track(int trkidx) const +{ + return ndev->netmdTrack(trkidx); +} + +QNetMDTrackList QNetMDTracksModel::tracks(const QModelIndexList & modelindices) const +{ + QNetMDTrackList tracks; + QModelIndex index; + + foreach(index, modelindices) + tracks.append(ndev->netmdTrack(index.row())); + return tracks; +} + +QStringList QNetMDTracksModel::downloadableFileExtensions() const +{ + return ndev->downloadableFileExtensions(); +} + + +/* himd tracks model */ + +QVariant QHiMDTracksModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation != Qt::Horizontal) + return QVariant(); + + if(role == Qt::SizeHintRole) + { + static QFont f; + static QFontMetrics met(f); + switch((hcolumnum)section) + { + case ColId: + return QSize(met.width("9999")+5, 0); + case ColTitle: + case ColArtist: + case ColAlbum: + return QSize(25*met.averageCharWidth(), 0); + case ColLength: + return QSize(met.width("9:99:99"), 0); + case ColCodec: + case ColUploadable: + /* Really use the header for the metric in these columns, + contents will be shorter */ + return QAbstractListModel::headerData(section,orientation,role); + } + } + + if(role == Qt::DisplayRole) + { + switch((hcolumnum)section) + { + case ColId: + return tr("Nr."); + case ColTitle: + return tr("Title"); + case ColArtist: + return tr("Artist"); + case ColAlbum: + return tr("Album"); + case ColLength: + return tr("Length"); + case ColCodec: + return tr("Format"); + case ColUploadable: + return tr("Uploadable"); + } + } + return QVariant(); +} + +QVariant QHiMDTracksModel::data(const QModelIndex & index, int role) const +{ + if(role == Qt::TextAlignmentRole && + (index.column() == ColId || index.column() == ColLength)) + return Qt::AlignRight; + + if(index.row() >= rowCount()) + return QVariant(); + + QHiMDTrack track = hdev->himdTrack(index.row()); + + if(role == Qt::CheckStateRole && index.column() == ColUploadable) + return track.copyprotected() ? Qt::Unchecked : Qt::Checked; + + if(role == Qt::DisplayRole) + { + switch((hcolumnum)index.column()) + { + case ColId: + return index.row() + 1; + case ColTitle: + return track.title(); + case ColArtist: + return track.artist(); + case ColAlbum: + return track.album(); + case ColLength: + { + QTime t = track.duration(); + if(t < QTime(1,0,0)) + return t.toString("m:ss"); + else + return t.toString("h:mm:ss"); + } + case ColCodec: + return track.codecname(); + case ColUploadable: + return QVariant(); /* Displayed by checkbox */ + } + } + return QVariant(); +} + +int QHiMDTracksModel::rowCount(const QModelIndex &) const +{ + if(hdev == NULL) + return 0; + + return hdev->trackCount(); +} + +int QHiMDTracksModel::columnCount(const QModelIndex &) const +{ + return LAST_hcolumnnum+1; +} + +QString QHiMDTracksModel::open(QMDDevice * device) +{ + QString ret = "error opening himd device"; + + if(hdev != NULL) + close(); + + if(device->deviceType() == HIMD_DEVICE) + { + hdev = static_cast<QHiMDDevice *>(device); + ret = hdev->open(); + } + + if(!ret.isEmpty()) + close(); + + reset(); /* inform views that the model contents changed */ + return ret; +} + +bool QHiMDTracksModel::is_open() +{ + return hdev->isOpen(); +} + +void QHiMDTracksModel::close() +{ + if(hdev != NULL && hdev->isOpen()) + hdev->close(); + + hdev = NULL; + + reset(); /* inform views that the model contents changed */ +} + +QHiMDTrack QHiMDTracksModel::track(int trknum) const +{ + return hdev->himdTrack(trknum); +} + +QHiMDTrackList QHiMDTracksModel::tracks(const QModelIndexList & modelindices) const +{ + QHiMDTrackList tracks; + QModelIndex index; + + foreach(index, modelindices) + tracks.append(hdev->himdTrack(index.row())); + return tracks; +} + +QStringList QHiMDTracksModel::downloadableFileExtensions() const +{ + return hdev->downloadableFileExtensions(); +} + + +/* QFileSystemModel stuff */ + +Qt::ItemFlags QHiMDFileSystemModel::flags(const QModelIndex &index) const +{ + if(!isDir(index) && !selectableExtensions.contains((fileInfo(index).suffix()), Qt::CaseInsensitive)) + return Qt::NoItemFlags; //not selectable, not enabled (grayed out in the browser) + + return QFileSystemModel::flags(index); +} + +void QHiMDFileSystemModel::setSelectableExtensions(QStringList extensions) +{ + selectableExtensions = extensions; + reset(); +} diff --git a/qhimdtransfer/qmdmodel.h b/qhimdtransfer/qmdmodel.h new file mode 100644 index 0000000..9dd644e --- /dev/null +++ b/qhimdtransfer/qmdmodel.h @@ -0,0 +1,80 @@ +#ifndef QHIMDMODEL_H +#define QHIMDMODEL_H + +#include <QtCore/QAbstractListModel> + +#include <QtCore/QList> +#include <QtCore/QStringList> +#include <QtGui/QFileSystemModel> +#include <qmdtrack.h> +#include <qmddevice.h> + +class QMDTracksModel : public QAbstractListModel { + Q_OBJECT + + QMDDevice * dev; +public: + QMDTracksModel() : dev(NULL) {} + /* QAbstractListModel stuff */ + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const {return QVariant();} + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const {return QVariant();} + virtual int rowCount(const QModelIndex & parent = QModelIndex() ) const {return 0;} + virtual int columnCount(const QModelIndex & parent = QModelIndex() ) const {return 0;} + /* dummy data for unknown devices */ + virtual QString open(QMDDevice *device = NULL) {return tr("no known device type specified");} + virtual bool is_open() {return false;} + virtual void close() {} + QStringList downloadableFileExtensions() const {return QStringList();} +}; + +class QNetMDTracksModel : public QMDTracksModel { + Q_OBJECT + + QNetMDDevice * ndev; + QNetMDTrackList allTracks; +public: + QNetMDTracksModel() {ndev = NULL;} + /* QAbstractListModel stuff */ + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex & parent = QModelIndex() ) const; + virtual int columnCount(const QModelIndex & parent = QModelIndex() ) const; + /* NetMD device stuff */ + QString open(QMDDevice *device); /* returns null if OK, error message otherwise */ + virtual bool is_open(); + void close(); + QNetMDTrack track(int trkidx) const; + virtual QNetMDTrackList tracks(const QModelIndexList & indices) const; // should be QMDTrackList later + QStringList downloadableFileExtensions() const; +}; + +class QHiMDTracksModel : public QMDTracksModel { + Q_OBJECT + + QHiMDDevice * hdev; +public: + QHiMDTracksModel() {hdev = NULL;} + /* QAbstractListModel stuff */ + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex & parent = QModelIndex() ) const; + virtual int columnCount(const QModelIndex & parent = QModelIndex() ) const; + /* HiMD containter stuff */ + virtual QString open(QMDDevice *device); /* returns null if OK, error message otherwise */ + virtual bool is_open(); + virtual void close(); + virtual QHiMDTrack track(int trackidx) const; + virtual QHiMDTrackList tracks(const QModelIndexList & indices) const; // should be QMDTrackList later + QStringList downloadableFileExtensions() const; +}; + +class QHiMDFileSystemModel : public QFileSystemModel { + Q_OBJECT + + QStringList selectableExtensions; +public: + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + void setSelectableExtensions(QStringList extensions); +}; + +#endif diff --git a/qhimdtransfer/qmdtrack.cpp b/qhimdtransfer/qmdtrack.cpp new file mode 100644 index 0000000..84643f8 --- /dev/null +++ b/qhimdtransfer/qmdtrack.cpp @@ -0,0 +1,204 @@ +#include "qmdtrack.h" + +static QString get_himd_str(struct himd * himd, int idx) +{ + QString outstr; + char * str; + if(!idx) + return QString(); + str = himd_get_string_utf8(himd, idx, NULL, NULL); + if(!str) + return QString(); + + outstr = QString::fromUtf8(str); + himd_free(str); + return outstr; +} + +QHiMDTrack::QHiMDTrack(struct himd * himd, unsigned int trackindex) : himd(himd), trknum(trackindex) +{ + trackslot = himd_get_trackslot(himd, trackindex, NULL); + if(trackslot != 0) + if(himd_get_track_info(himd, trackslot, &ti, NULL) < 0) + trackslot = -1; +} + +unsigned int QHiMDTrack::tracknum() const +{ + return trknum; +} + +QString QHiMDTrack::title() const +{ + if(trackslot != 0) + return get_himd_str(himd, ti.title); + else + return QString(); +} + +QString QHiMDTrack::artist() const +{ + if(trackslot != 0) + return get_himd_str(himd, ti.artist); + else + return QString(); +} + +QString QHiMDTrack::album() const +{ + if(trackslot != 0) + return get_himd_str(himd, ti.album); + else + return QString(); +} + +QString QHiMDTrack::codecname() const +{ + if(trackslot != 0) + return himd_get_codec_name(&ti); + else + return QString(); +} + +QTime QHiMDTrack::duration() const +{ + QTime t; + if(trackslot != 0) + return t.addSecs(ti.seconds); + else + return t; +} + +bool QHiMDTrack::copyprotected() const +{ + if(trackslot != 0) + return !himd_track_uploadable(himd, &ti); + return true; +} + +int QHiMDTrack::blockcount() const +{ + if(trackslot != 0) + return himd_track_blocks(himd, &ti, NULL); + else + return 0; +} + +QString QHiMDTrack::openMpegStream(struct himd_mp3stream * str) const +{ + struct himderrinfo status; + if(himd_mp3stream_open(himd, trackslot, str, &status) < 0) + return QString::fromUtf8(status.statusmsg); + return QString(); +} + +QString QHiMDTrack::openNonMpegStream(struct himd_nonmp3stream * str) const +{ + struct himderrinfo status; + if(himd_nonmp3stream_open(himd, trackslot, str, &status) < 0) + return QString::fromUtf8(status.statusmsg); + return QString(); +} + +QByteArray QHiMDTrack::makeEA3Header() const +{ + char header[EA3_FORMAT_HEADER_SIZE]; + make_ea3_format_header(header, &ti.codec_info); + return QByteArray(header,EA3_FORMAT_HEADER_SIZE); +} + + +QNetMDTrack::QNetMDTrack(netmd_dev_handle * deviceh, minidisc * my_md, unsigned int trackindex) +{ + uint8_t g; + struct netmd_pair const *bitrate; + unsigned char bitrate_id; + unsigned char channel; + char *name, buffer[256]; + + devh = deviceh; + md = my_md; + trkindex = trackindex; + + if(netmd_request_title(devh, trkindex, buffer, sizeof(buffer)) < 0) + { + trkindex = -1; + return; // no track with this trackindex + } + + /* Figure out which group this track is in */ + for( g = 1; g < md->group_count; g++ ) + { + if( (md->groups[g].start <= trkindex+1U) && (md->groups[g].finish >= trkindex+1U )) + { + groupstring = QString(md->groups[g].name); + break; + } + } + + netmd_request_track_time(devh, trkindex, &time); + netmd_request_track_flags(devh, trkindex, &flags); + netmd_request_track_bitrate(devh, trkindex, &bitrate_id, &channel); + + bitrate = find_pair(bitrate_id, bitrates); + + /* Skip 'LP:' prefix... the codec type shows up in the list anyway*/ + name = strncmp( buffer, "LP:", 3 ) ? buffer : buffer+3 ; + + titlestring = QString(name); + codecstring = QString(bitrate->name); +} + +unsigned int QNetMDTrack::tracknum() const +{ + return (trkindex < 0 ? trkindex : trkindex + 1); +} + +QString QNetMDTrack::group() const +{ + if(trkindex < 0) + return QString(); + + return groupstring; +} + +QString QNetMDTrack::title() const +{ + if(trkindex < 0) + return QString(); + + return titlestring; +} + +QString QNetMDTrack::codecname() const +{ + if(trkindex < 0) + return QString(); + + return codecstring; +} + +QTime QNetMDTrack::duration() const +{ + QTime t; + + if(trkindex < 0) + return t; + + return t.addSecs( time.minute * 60 + time.second); +} + +bool QNetMDTrack::copyprotected() const +{ + switch(flags) + { + case 0x00 : return false; + case 0x03 : return true; + default : return true; // return true if unknown + } +} + +int QNetMDTrack::blockcount() const +{ + return 0; // not implemented, yet +} diff --git a/qhimdtransfer/qmdtrack.h b/qhimdtransfer/qmdtrack.h new file mode 100644 index 0000000..c7f30ad --- /dev/null +++ b/qhimdtransfer/qmdtrack.h @@ -0,0 +1,81 @@ +#ifndef QMDTRACK_H +#define QMDTRACK_H + +#include <QtCore/QTime> +#include "himd.h" +#include "sony_oma.h" + +#ifdef Q_OS_WIN + #ifdef WINVER // WINVER needs to be 0x500 or later to make the windows autodetection mechanism work and it + #undef WINVER // must be defined correctly before including libusb.h (included from libnetmd.h), else it will be defined + #endif // in windef.h to 0x400 + #define WINVER 0x500 +#endif + +extern "C" { +#include <libnetmd.h> +} + +class QMDTrack +{ +public: + QMDTrack() {} // returns dummy data, implemented to have a common class name with common members + virtual unsigned int tracknum() const {return -1;} + virtual QString group() const {return QString();} + virtual QString title() const {return QString();} + virtual QString artist() const {return QString();} + virtual QString album() const {return QString();} + virtual QString codecname() const {return QString();} + virtual QTime duration() const {return QTime();} + virtual bool copyprotected() const {return true;} + virtual int blockcount() const {return 0;} +}; + +class QHiMDTrack : public QMDTrack{ + struct himd * himd; + unsigned int trknum; + unsigned int trackslot; + struct trackinfo ti; +public: + QHiMDTrack(struct himd * himd, unsigned int trackindex); + virtual unsigned int tracknum() const; + virtual QString title() const; + virtual QString artist() const; + virtual QString album() const; + virtual QString codecname() const; + virtual QTime duration() const; + virtual bool copyprotected() const; + virtual int blockcount() const; + + QString openMpegStream(struct himd_mp3stream * str) const; + QString openNonMpegStream(struct himd_nonmp3stream * str) const; + QByteArray makeEA3Header() const; +}; + +class QNetMDTrack : public QMDTrack { + netmd_dev_handle * devh; + minidisc * md; + uint8_t trkindex; + struct netmd_track time; + unsigned char flags; + QString groupstring; + QString titlestring; + QString codecstring; + +public: + QNetMDTrack(netmd_dev_handle *deviceh, minidisc * my_md, unsigned int trackindex); + virtual unsigned int tracknum() const; + virtual QString group() const; + virtual QString title() const; + virtual QString codecname() const; + virtual QTime duration() const; + virtual bool copyprotected() const; + virtual int blockcount() const; +}; + +typedef QList<QMDTrack> QMDTrackList; +typedef QList<QHiMDTrack> QHiMDTrackList; +typedef QList<QNetMDTrack> QNetMDTrackList; +typedef QList<unsigned int> QMDTrackIndexList; + +#endif // QMDTRACK_H -- 1.8.0.msysgit.0