FU Logo
  • Startseite
  • Kontakt
  • Impressum
  • Home
  • Listenauswahl
  • Anleitungen

Re: [linux-minidisc] netmd support for qhimdtransfer gui application

<-- thread -->
<-- date -->
  • From: Thomas Arp <manner.moe@gmx.de>
  • To: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>, "linux-minidisc@lists.fu-berlin.de" <linux-minidisc@lists.fu-berlin.de>
  • Date: Sat, 21 Dec 2013 17:35:29 +0100
  • Subject: Re: [linux-minidisc] netmd support for qhimdtransfer gui application

Am 20.12.2013 21:54, schrieb John Paul Adrian Glaubitz:
In any case, we need to re-base your patches against the current version
in github such that Howard's patch comes first, then your NetMD support
batch as we'd otherwise mess up the commit and version history which
is already part of the uploaded Debian package.

Again, sorry for the mess.

Adrian

O.K., here is my updated patch rebased against the current repo on github.

Thomas
>From 195e2642885594514acdfcf30b201377d9615e7e Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Sat, 21 Dec 2013 17:15:19 +0100
Subject: [PATCH 1/6] netmd support:implemented new class QNetMDTrack analogous
 to QHiMDTrack and put a base class QMDTrack over it holding common members

---
 qhimdtransfer/qmdtrack.cpp | 239 +++++++++++++++++++++++++++++++++++++++++++++
 qhimdtransfer/qmdtrack.h   |  92 +++++++++++++++++
 2 files changed, 331 insertions(+)
 create mode 100644 qhimdtransfer/qmdtrack.cpp
 create mode 100644 qhimdtransfer/qmdtrack.h

diff --git a/qhimdtransfer/qmdtrack.cpp b/qhimdtransfer/qmdtrack.cpp
new file mode 100644
index 0000000..3d6bf1f
--- /dev/null
+++ b/qhimdtransfer/qmdtrack.cpp
@@ -0,0 +1,239 @@
+#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;
+}
+
+QHiMDTrack::~QHiMDTrack()
+{
+    himd = NULL;
+}
+
+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(0,0,0);
+    if(trackslot != 0)
+        return t.addSecs(ti.seconds);
+    else
+        return QTime();
+}
+
+QDateTime QHiMDTrack::recdate() const
+{
+    QDate d(0,0,0);
+    QTime t(0,0,0);
+    if (trackslot != 0)
+    {
+        t.setHMS(ti.recordingtime.tm_hour,
+          ti.recordingtime.tm_min,
+          ti.recordingtime.tm_sec);
+        d.setDate(ti.recordingtime.tm_year+1900,
+          ti.recordingtime.tm_mon+1,
+          ti.recordingtime.tm_mday);
+        return QDateTime(d,t);
+    }
+    return QDateTime();
+}
+
+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, int trackindex)
+{
+    uint8_t g;
+    struct netmd_pair const *bitrate;
+    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);
+    blocks = 0;
+}
+
+QNetMDTrack::~QNetMDTrack()
+{
+    devh = NULL;
+    md = NULL;
+}
+
+unsigned int QNetMDTrack::tracknum() const
+{
+    /* returns zero based track number, maybe this function should return a one based track number as shown in the treeview,
+     * trackindex -> zero based; tracknumber -> one based
+     */
+    return trkindex;
+}
+
+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(0,0,0);
+
+    if(trkindex < 0)
+        return QTime();
+
+    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
+    }
+}
+
+void QNetMDTrack::setBlocks(int cnt)
+{
+    blocks = cnt;
+}
+
+int QNetMDTrack::blockcount() const
+{
+    return blocks;
+}
diff --git a/qhimdtransfer/qmdtrack.h b/qhimdtransfer/qmdtrack.h
new file mode 100644
index 0000000..0ac7e43
--- /dev/null
+++ b/qhimdtransfer/qmdtrack.h
@@ -0,0 +1,92 @@
+#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_extended.h>
+}
+
+/* define buffer size for netmd uploads */
+#define NETMD_RECV_BUF_SIZE 0x10000
+
+class QMDTrack
+{
+public:
+    QMDTrack() {}   // returns dummy data, implemented to have a common class name with common members
+    virtual ~QMDTrack() {}
+    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 ~QHiMDTrack();
+    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;
+    QDateTime recdate() 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;
+    int8_t trkindex;
+    struct netmd_track time;
+    unsigned char flags;
+    QString groupstring;
+    QString titlestring;
+    QString codecstring;
+private:
+    int blocks;
+public:
+    unsigned char bitrate_id;
+    unsigned char channel;
+    QNetMDTrack(netmd_dev_handle *deviceh, minidisc * my_md, int trackindex);
+    virtual ~QNetMDTrack();
+    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 void setBlocks(int cnt);
+    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.4.msysgit.0

>From 2beffbe7aabe137b23848d19521ec2103e834183 Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Sat, 21 Dec 2013 17:19:50 +0100
Subject: [PATCH 2/6] netmd support: implemented new class QMDDevice with
 subclasses QNetMDDevice and QHiMDDevice holding information for each device
 and containing upload routines, also created additional libnetmd header file
 to get access to additional libnetmd functions

---
 libnetmd/libnetmd.pro        |   3 +-
 libnetmd/libnetmd_extended.h |  37 +++
 qhimdtransfer/qmddevice.cpp  | 660 +++++++++++++++++++++++++++++++++++++++++++
 qhimdtransfer/qmddevice.h    | 101 +++++++
 4 files changed, 800 insertions(+), 1 deletion(-)
 create mode 100644 libnetmd/libnetmd_extended.h
 create mode 100644 qhimdtransfer/qmddevice.cpp
 create mode 100644 qhimdtransfer/qmddevice.h

diff --git a/libnetmd/libnetmd.pro b/libnetmd/libnetmd.pro
index eb6a402..3319d85 100644
--- a/libnetmd/libnetmd.pro
+++ b/libnetmd/libnetmd.pro
@@ -5,7 +5,8 @@ CONFIG += staticlib link_pkgconfig create_prl console debug_and_release_target
 DEFINES += G_DISABLE_DEPRECATED=1
 
 PKGCONFIG += libusb-1.0
-HEADERS += common.h const.h error.h libnetmd.h log.h netmd_dev.h playercontrol.h secure.h trackinformation.h utils.h
+HEADERS += common.h const.h error.h libnetmd.h log.h netmd_dev.h playercontrol.h secure.h trackinformation.h utils.h \
+    libnetmd_extended.h
 SOURCES += common.c error.c libnetmd.c log.c netmd_dev.c playercontrol.c secure.c trackinformation.c utils.c
 LIBS    += -lgcrypt
 
diff --git a/libnetmd/libnetmd_extended.h b/libnetmd/libnetmd_extended.h
new file mode 100644
index 0000000..1eb74db
--- /dev/null
+++ b/libnetmd/libnetmd_extended.h
@@ -0,0 +1,37 @@
+/*
+ * include this header file to get access to additional libnetmd members
+ */
+
+#include "libnetmd.h"
+
+typedef struct {
+        unsigned char content[255];
+        size_t length;
+        size_t position;
+} netmd_response;
+
+/*
+ * additional members from secure.c
+ */
+
+void netmd_send_secure_msg(netmd_dev_handle *dev, unsigned char cmd, unsigned char *data, size_t data_size);
+netmd_error netmd_recv_secure_msg(netmd_dev_handle *dev, unsigned char cmd, netmd_response *response,
+                                  unsigned char expected_response_code);
+netmd_error netmd_secure_real_recv_track(netmd_dev_handle *dev, uint32_t length, FILE *file, size_t chunksize);
+void netmd_write_aea_header(char *name, uint32_t frames, unsigned char channel, FILE* f);
+void netmd_write_wav_header(unsigned char format, uint32_t bytes, FILE *f);
+
+/*
+ * additional members from utils.c
+ * XXX: do not include utils.h when using taglib, definition of min(a,b) is incomatible with definition of min(...) in taglib
+ */
+void netmd_check_response_bulk(netmd_response *response, const unsigned char* const expected,
+                               const size_t expected_length, netmd_error *error);
+void netmd_check_response_word(netmd_response *response, const uint16_t expected,
+                               netmd_error *error);
+void netmd_read_response_bulk(netmd_response *response, unsigned char* target,
+                              const size_t length, netmd_error *error);
+unsigned char *netmd_copy_word_to_buffer(unsigned char **buf, uint16_t value, int little_endian);
+unsigned char netmd_read(netmd_response *response);
+uint16_t netmd_read_word(netmd_response *response);
+uint32_t netmd_read_doubleword(netmd_response *response);
diff --git a/qhimdtransfer/qmddevice.cpp b/qhimdtransfer/qmddevice.cpp
new file mode 100644
index 0000000..35e01a8
--- /dev/null
+++ b/qhimdtransfer/qmddevice.cpp
@@ -0,0 +1,660 @@
+#include <qmddevice.h>
+#include <QMessageBox>
+#include <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::setMdInserted(bool inserted)
+{
+    md_inserted = inserted;
+}
+
+bool QMDDevice::mdInserted()
+{
+    return md_inserted;
+}
+
+void QMDDevice::setDeviceHandle(void * devicehandle)
+{
+    devhandle = devicehandle;
+}
+
+void * QMDDevice::deviceHandle()
+{
+    return devhandle;
+}
+
+void QMDDevice::registerMdChange(void * regMdChange)
+{
+    mdChange = regMdChange;
+}
+
+void * QMDDevice::MdChange()
+{
+    return mdChange;
+}
+
+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, &current_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;
+    md_inserted = true;
+    emit opened();
+    return QString();
+}
+
+void QNetMDDevice::close()
+{
+    if(!devh)
+        return;
+
+    netmd_clean_disc_info(&current_md);
+    netmd_close(devh);
+    devh = NULL;
+
+    is_open = false;
+    trk_count = 0;
+    md_inserted = false;
+    emit closed();
+}
+
+QString QNetMDDevice::discTitle()
+{
+    return QString(current_md.groups[0].name);
+}
+
+QNetMDTrack QNetMDDevice::netmdTrack(unsigned int trkindex)
+{
+    minidisc * disc = &current_md;
+
+    return QNetMDTrack(devh, disc, trkindex);
+}
+
+QString QNetMDDevice::upload_track_blocks(uint32_t length, FILE *file, size_t chunksize)
+{
+    /* this is a copy of netmd_secure_real_recv_track(...) function, but updates upload dialog progress bar */
+    uint32_t done = 0;
+    unsigned char *data;
+    int status;
+    netmd_error error = NETMD_NO_ERROR;
+    int transferred = 0;
+
+    data = (unsigned char *)malloc(chunksize);
+    while (done < length) {
+        if ((length - done) < chunksize) {
+            chunksize = length - done;
+        }
+
+        status = libusb_bulk_transfer((libusb_device_handle*)devh, 0x81, data, (int)chunksize, &transferred, 10000);
+
+        if (status >= 0) {
+            done += transferred;
+            fwrite(data, transferred, 1, file);
+            netmd_log(NETMD_LOG_DEBUG, "%.1f%%\n", (double)done/(double)length * 100);
+
+            uploadDialog.blockTransferred();
+            QApplication::processEvents();
+            /* do not check for uploadDialog.upload_canceled() here, netmd device will remain busy if track upload hasn´t finished */
+        }
+        else if (status != -LIBUSB_ERROR_TIMEOUT) {
+            error = NETMD_USB_ERROR;
+        }
+    }
+    free(data);
+
+    return (error != NETMD_NO_ERROR) ? netmd_strerror(error) : QString();
+}
+
+void QNetMDDevice::upload(unsigned int trackidx, QString path)
+{
+    /* this is a copy of netmd_secure_recv_track(...) function, we need single block transfer function to make use of a progress bar,
+     * maybe we can add/change something inside libnetmd for this
+     */
+    QNetMDTrack track = netmdTrack(trackidx);
+    uint16_t track_id = trackidx;
+    unsigned char cmdhdr[] = {0x00, 0x10, 0x01};
+    unsigned char cmd[sizeof(cmdhdr) + sizeof(track_id)] = { 0 };
+    unsigned char *buf;
+    unsigned char codec;
+    uint32_t length;
+    netmd_response response;
+    netmd_error error;
+    QString filename, errmsg, filepath;
+    FILE * file = NULL;
+
+    if(name() != "SONY MZ-RH1 (NetMD)")
+    {
+        errmsg = tr("upload disabled, %1 does not support netmd track uploads").arg(name());
+        goto clean;
+    }
+
+    if(track.copyprotected())
+    {
+        errmsg = tr("upload disabled, Track is copy protected");
+        goto clean;
+    }
+
+    // create filename first
+    if(track.title().isEmpty())
+        filename = tr("Track %1").arg(track.tracknum() + 1);
+    else
+        filename = track.title();
+
+    if(track.bitrate_id == NETMD_ENCODING_SP) {
+        checkfile(path, filename, ".aea");
+        filepath = path + "/" + filename + ".aea";
+    }
+    else {
+        checkfile(path, filename, ".wav");
+        filepath = path + "/" + filename + ".wav";
+    }
+
+    if(!(file = fopen(filepath.toUtf8().data(), "wb"))) {
+            errmsg = tr("cannot open file %1 for writing").arg(filepath);
+            goto clean;
+    }
+
+    buf = cmd;
+    memcpy(buf, cmdhdr, sizeof(cmdhdr));
+    buf += sizeof(cmdhdr);
+    netmd_copy_word_to_buffer(&buf, trackidx + 1U, 0);
+
+    netmd_send_secure_msg(devh, 0x30, cmd, sizeof(cmd));
+    error = netmd_recv_secure_msg(devh, 0x30, &response, NETMD_STATUS_INTERIM);
+    netmd_check_response_bulk(&response, cmdhdr, sizeof(cmdhdr), &error);
+    netmd_check_response_word(&response, track_id + 1U, &error);
+    codec = netmd_read(&response);
+    length = netmd_read_doubleword(&response);
+
+    /* initialize track.blockcount() here, needed by progress bar in the uploadDialog */
+    track.setBlocks(length%NETMD_RECV_BUF_SIZE ? length / NETMD_RECV_BUF_SIZE + 1 : length / NETMD_RECV_BUF_SIZE);
+    uploadDialog.starttrack(track, filename);
+    if (track.bitrate_id == NETMD_ENCODING_SP) {
+        netmd_write_aea_header(track.title().toUtf8().data(), codec, track.channel, file);
+    }
+    else {
+        netmd_write_wav_header(codec, length, file);
+    }
+
+    errmsg = upload_track_blocks(length, file, NETMD_RECV_BUF_SIZE);
+    if(!errmsg.isNull()) {
+        goto clean;
+    }
+
+    error = netmd_recv_secure_msg(devh, 0x30, &response, NETMD_STATUS_ACCEPTED);
+    netmd_check_response_bulk(&response, cmdhdr, sizeof(cmdhdr), &error);
+    netmd_read_response_bulk(&response, NULL, 2, &error);
+    netmd_check_response_word(&response, 0, &error);
+
+    if(error != NETMD_NO_ERROR)
+        errmsg = QString(netmd_strerror(error));
+
+clean:
+    if(errmsg.isNull())
+        uploadDialog.trackSucceeded();
+    else
+        uploadDialog.trackFailed(errmsg);
+
+    if(file)
+        fclose(file);
+    if(!errmsg.isNull())
+    {
+        QFile f(filepath);
+        if(f.exists())
+            f.remove();
+    }
+}
+
+void QNetMDDevice::batchUpload(QMDTrackIndexList tlist, QString path)
+{
+    int allblocks = 0;
+
+    setBusy(true);
+
+    /* progress bar for all tracks does not work yet, is there any way to get track length without recieving a complete track ?
+     * as far as i´ve tested device remains busy if download procedure hasn´t finished.
+     * progressbar for all tracks shows idle mode if maximum value is set to 0
+     */
+    for(int i = 0;i < tlist.length(); i++) {
+        allblocks += netmdTrack(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);
+}
+
+/* himd device members */
+
+QHiMDDevice::QHiMDDevice()
+{
+    dev_type = HIMD_DEVICE;
+    himd = NULL;
+    is_open = false;
+}
+
+QHiMDDevice::~QHiMDDevice()
+{
+    close();
+}
+
+QString QHiMDDevice::open()
+{
+    struct himderrinfo status;
+
+    if(!mdInserted())
+        return tr("cannot open device, no disc");
+
+    if(himd)  // first close himd if opened
+    {
+        himd_close(himd);
+        delete himd;
+        himd = NULL;
+    }
+
+    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;
+    md_inserted = 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..c413f9c
--- /dev/null
+++ b/qhimdtransfer/qmddevice.h
@@ -0,0 +1,101 @@
+#ifndef QMDDEVICE_H
+#define QMDDEVICE_H
+
+#include <QString>
+#include <QStringList>
+
+#include <qmdtrack.h>
+#include "qhimduploaddialog.h"
+
+enum device_type {
+    NO_DEVICE,
+    NETMD_DEVICE,
+    HIMD_DEVICE
+};
+
+class QMDDevice : public QObject {
+    Q_OBJECT
+    Q_DISABLE_COPY(QMDDevice)
+
+    QString recorder_name;
+    bool is_busy;
+protected:
+    QString device_path;
+    enum device_type dev_type;
+    bool is_open;
+    unsigned int trk_count;
+    bool md_inserted;
+    void * devhandle;
+    void * mdChange;
+    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 setMdInserted(bool inserted);
+    virtual bool mdInserted();
+    virtual void setDeviceHandle(void * devicehandle);
+    virtual void * deviceHandle();
+    virtual void registerMdChange(void * regMdChange);
+    virtual void * MdChange();
+    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;
+private:
+    QString upload_track_blocks(uint32_t length, FILE *file, size_t chunksize);
+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;
+private:
+    QString dumpmp3(const QHiMDTrack &trk, QString file);
+    QString dumpoma(const QHiMDTrack & track, QString file);
+    QString dumppcm(const QHiMDTrack &track, QString file);
+public:
+    explicit QHiMDDevice();
+    virtual ~QHiMDDevice();
+    virtual QString open();
+    virtual void close();
+    virtual QHiMDTrack himdTrack(unsigned int trkindex);
+    virtual void upload(unsigned int trackidx, QString path);
+    virtual void batchUpload(QMDTrackIndexList tlist, QString path);
+
+};
+
+#endif // QMDDEVICE_H
-- 
1.8.4.msysgit.0

>From 28f0773211065848aff6e36c5473b8d1c0ed024a Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Sat, 21 Dec 2013 17:23:11 +0100
Subject: [PATCH 3/6] netmd support: implemented new class QNetMDTracksModel
 analogous to QHiMDTracksModel, renamed qhimdmodel.h/.cpp to qmdmodel.h/.cpp,
 QHiMDTrack class is already moved to qmdtrack.h/.cpp

---
 qhimdtransfer/qmdmodel.cpp | 373 +++++++++++++++++++++++++++++++++++++++++++++
 qhimdtransfer/qmdmodel.h   |  79 ++++++++++
 2 files changed, 452 insertions(+)
 create mode 100644 qhimdtransfer/qmdmodel.cpp
 create mode 100644 qhimdtransfer/qmdmodel.h

diff --git a/qhimdtransfer/qmdmodel.cpp b/qhimdtransfer/qmdmodel.cpp
new file mode 100644
index 0000000..2a3c4e7
--- /dev/null
+++ b/qhimdtransfer/qmdmodel.cpp
@@ -0,0 +1,373 @@
+#include <QFont>
+#include <QFontMetrics>
+#include <qmdmodel.h>
+
+enum hcolumnum {
+  ColId, ColTitle, ColArtist, ColAlbum, ColLength, ColCodec, ColUploadable, ColRecDate,
+  LAST_hcolumnnum = ColRecDate
+};
+
+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 ((ndev->name() != "SONY MZ-RH1 (NetMD)") || track.copyprotected()) ? Qt::Unchecked : Qt::Checked;
+
+    if(role == Qt::DisplayRole)
+    {
+        switch((ncolumnum)index.column())
+        {
+            case CoId:
+                return track.tracknum() + 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";
+
+    beginResetModel();
+    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));
+
+    endResetModel();	/* inform views that the model contents changed */
+    return ret;
+}
+
+bool QNetMDTracksModel::is_open()
+{
+    return ndev->isOpen();
+}
+
+void QNetMDTracksModel::close()
+{
+    beginResetModel();
+
+    if(ndev != NULL && ndev->isOpen())
+        ndev->close();
+
+    ndev = NULL;
+
+    allTracks.clear();
+    endResetModel();	/* 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);
+            case ColRecDate:
+                return QSize(met.width("yyyy.MM.dd hh:mm:ss"), 0);
+        }
+    }
+
+    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");
+            case ColRecDate:
+                return tr("Recorded At");
+        }
+    }
+    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 */
+            case ColRecDate:
+            {
+                QDateTime dt = track.recdate();
+                return dt.toString("yyyy.MM.dd hh:mm:ss");
+            }
+        }
+    }
+    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";
+
+    beginResetModel();
+    if(hdev != NULL)
+        close();
+
+    if(device->deviceType() == HIMD_DEVICE)
+    {
+        hdev = static_cast<QHiMDDevice *>(device);
+        ret = hdev->open();
+    }
+
+    if(!ret.isEmpty())
+        close();
+
+    endResetModel();	/* inform views that the model contents changed */
+    return ret;
+}
+
+bool QHiMDTracksModel::is_open()
+{
+    return hdev->isOpen();
+}
+
+void QHiMDTracksModel::close()
+{
+    beginResetModel();
+
+    if(hdev != NULL && hdev->isOpen())
+        hdev->close();
+
+    hdev = NULL;
+
+    endResetModel();	/* 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)
+{
+    beginResetModel();
+    selectableExtensions = extensions;
+    endResetModel();
+}
diff --git a/qhimdtransfer/qmdmodel.h b/qhimdtransfer/qmdmodel.h
new file mode 100644
index 0000000..bb8b0b8
--- /dev/null
+++ b/qhimdtransfer/qmdmodel.h
@@ -0,0 +1,79 @@
+#ifndef QMDMODEL_H
+#define QMDMODEL_H
+
+#include <QtCore/QAbstractListModel>
+
+#include <QtCore/QList>
+#include <QtCore/QStringList>
+#include <QFileSystemModel>
+#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 // QMDMODEL_H
-- 
1.8.4.msysgit.0

>From ae8c5310f5a61655eca134df44d803f6df940765 Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Sat, 21 Dec 2013 17:25:51 +0100
Subject: [PATCH 4/6] netmd support: implemented netmd autodetection at
 application start, also implemented netmd autodetection during application
 runtime using the new QMDDevice classes

---
 libnetmd/use_libnetmd.prl           |   2 +-
 qhimdtransfer/qhimddetection.cpp    | 258 ++++++++++++++++++++++++++++++++----
 qhimdtransfer/qhimddetection.h      | 105 ++++++++++-----
 qhimdtransfer/qhimdwindetection.cpp | 198 +++++++++++++++++----------
 4 files changed, 434 insertions(+), 129 deletions(-)

diff --git a/libnetmd/use_libnetmd.prl b/libnetmd/use_libnetmd.prl
index d9019f2..a3c72e0 100644
--- a/libnetmd/use_libnetmd.prl
+++ b/libnetmd/use_libnetmd.prl
@@ -11,7 +11,7 @@ build_pass:CONFIG(release,debug|release) {
  LIBS += -L../libnetmd/release
 }
 
-# fallback if libhimd was not compiled with
+# fallback if libnetmd was not compiled with
 # CONFIG += debug_and_release debug_and_release_target
 # while I force debug_and_release_target, it is ignored in a
 # just-one-kind build without debug_and_release
diff --git a/qhimdtransfer/qhimddetection.cpp b/qhimdtransfer/qhimddetection.cpp
index d563587..e73dc03 100644
--- a/qhimdtransfer/qhimddetection.cpp
+++ b/qhimdtransfer/qhimddetection.cpp
@@ -1,73 +1,275 @@
 #include <QtCore/QDebug>
 #include "qhimddetection.h"
 
+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);
 }
 
-// slots
+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;
 
-void QHiMDDetection::himd_busy(QString path)
+    emit deviceListChanged(dlist);
+    scan_for_netmd_devices();
+}
+
+void QHiMDDetection::scan_for_minidisc_devices()
+{
+    /* create device entry for disc images first */
+    QHiMDDevice * mddev = new QHiMDDevice();
+    mddev->setMdInserted(true);
+    mddev->setName("disc image");
+    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..4a5915a 100644
--- a/qhimdtransfer/qhimddetection.h
+++ b/qhimdtransfer/qhimddetection.h
@@ -4,50 +4,95 @@
 #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);
diff --git a/qhimdtransfer/qhimdwindetection.cpp b/qhimdtransfer/qhimdwindetection.cpp
index 3e79038..f5a9d0c 100644
--- a/qhimdtransfer/qhimdwindetection.cpp
+++ b/qhimdtransfer/qhimdwindetection.cpp
@@ -3,8 +3,6 @@
 #include <QWidget>
 #include "qhimddetection.h"
 
-#define WINVER 0x0500
-
 #include <windows.h>
 #include <dbt.h>
 #include <setupapi.h>
@@ -12,17 +10,15 @@
 #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;
-                    };
-
 static const GUID my_GUID_IO_MEDIA_ARRIVAL =
     {0xd07433c0, 0xa98e, 0x11d2, {0x91, 0x7a, 0x00, 0xa0, 0xc9, 0x06, 0x8f, 0xf3} };
 
 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 +33,19 @@ public:
     void scan_for_himd_devices();
     QHiMDWinDetection(QObject * parent = NULL);
     ~QHiMDWinDetection();
-    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 nativeEvent(const QByteArray & eventType, void * message, long *result);
     bool winEvent(MSG * msg, long * result);
 };
@@ -64,12 +61,16 @@ QHiMDWinDetection::QHiMDWinDetection(QObject * parent)
 {
     // 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()
@@ -97,21 +98,17 @@ void QHiMDWinDetection::scan_for_himd_devices()
     return;
 }
 
-win_himd_device *QHiMDWinDetection::win_dev_at(int idx)
+QMDDevice *QHiMDWinDetection::find_by_handle(HANDLE devhandle)
 {
-    return static_cast<win_himd_device*>(device_list.at(idx));
-}
-
-win_himd_device *QHiMDWinDetection::find_by_path(QString path)
-{
-    return static_cast<win_himd_device*>(QHiMDDetection::find_by_path(path));
-}
+    QMDDevice *mddev;
 
-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;
+        if(mddev->deviceHandle() == devhandle)
+            return mddev;
+    }
 
     return NULL;
 }
@@ -192,13 +189,13 @@ 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();
     int k;
     char drv[] = "\\\\.\\X:";
     QByteArray device = "\\\\.\\PHYSICALDRIVE";
     char file[] = "X:\\HI-MD.IND";
     DWORD retbytes;
-    HANDLE hdev;
+    HANDLE hdev, dev;
     STORAGE_DEVICE_NUMBER sdn;
     OFSTRUCT OFfile;
 
@@ -214,81 +211,84 @@ 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,
+    dev = CreateFileA(device.data(), NULL , FILE_SHARE_READ, NULL,
                                            OPEN_EXISTING, 0, NULL);
-    if(new_device->devhandle == INVALID_HANDLE_VALUE)
+    if(dev == 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;
+    new_device->setDeviceHandle(dev);
+    new_device->registerMdChange(register_mediaChange(new_device->deviceHandle()));
+    new_device->setBusy(false);
+    new_device->setPath(path);
+    new_device->setName(name);
 
     file[0] = path.at(0).toLatin1();
     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 + ")";
-    }
+        new_device->setMdInserted(true);
     else
-    {
-        qDebug() << "himd device at " + new_device->path + " added (" + new_device->recorder_name + ")" + " ; without MD";
-        new_device->md_inserted = false;
-    }
+        new_device->setMdInserted(false);
 
-    device_list.append(new_device);
+    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;
+    QHiMDDevice * dev = static_cast<QHiMDDevice *>(find_by_path(path));
+
     if (!dev)
         return;
 
-    unregister_mediaChange(dev->himdChange);
-
-    if (dev->devhandle != NULL)
-        CloseHandle(dev->devhandle);
+    index = dlist.indexOf(dev);
 
-    emit himd_removed(dev->path);
+    if(dev->isOpen())
+        dev->close();
 
-    qDebug() << "himd device at " + dev->path + " removed (" + dev->recorder_name + ")";
+    if(dev->name() != "disc image")
+    {
+        if(dev->MdChange() != NULL)
+            unregister_mediaChange((HDEVNOTIFY)dev->MdChange());
+        if(dev->deviceHandle() != NULL)
+             CloseHandle(dev->deviceHandle());
+     }
 
-    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);
+    QMDDevice * dev = find_by_handle(devhandle);
     if (!dev)
         return;
 
-    if(!dev->md_inserted)
-    {
-        dev->md_inserted = true;
-        emit himd_found(dev->path);
-        qDebug() << "himd device at " + dev->path + " : md inserted";
-    }
+    if(!dev->mdInserted())
+        dev->setMdInserted(true);
+
+    if(!dev->isOpen())
+        dev->open();
+
     return;
 }
 
 void QHiMDWinDetection::remove_himd(HANDLE devhandle)
 {
-    win_himd_device * dev = find_by_handle(devhandle);
+    QMDDevice * dev = find_by_handle(devhandle);
     if (!dev)
         return;
 
-    if(dev->md_inserted)
-    {
-        dev->md_inserted = false;
-        emit himd_removed(dev->path);
-        qDebug() << "himd device at " + dev->path + " :  md removed";
-    }
+    if(dev->isOpen())
+        dev->close();
+
+    dev->setMdInserted(false);
+
     return;
 }
 
@@ -312,6 +312,26 @@ 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( (HWND)this->winId(), &filter, DEVICE_NOTIFY_WINDOW_HANDLE);
+
+}
+
+void QHiMDWinDetection::unregister_usbDeviceNotification()
+{
+    if(listen_usbdev != NULL)
+       UnregisterDeviceNotification(listen_usbdev);
+}
+
 bool QHiMDWinDetection::nativeEvent(const QByteArray & eventType, void * message, long *result)
 {
     if (eventType == "windows_generic_MSG")
@@ -344,6 +364,17 @@ 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 :
@@ -355,6 +386,16 @@ 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 :
@@ -362,13 +403,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";
@@ -377,7 +418,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;
-- 
1.8.4.msysgit.0

>From 4ca19aebe23aed1a4e1026f1264ffe0c3f204e85 Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Sat, 21 Dec 2013 17:26:56 +0100
Subject: [PATCH 5/6] netmd support: changed qhimduploaddialog to make it
 usable for netmd and himd devices

---
 qhimdtransfer/qhimduploaddialog.cpp | 10 +++++++++-
 qhimdtransfer/qhimduploaddialog.h   |  4 ++--
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/qhimdtransfer/qhimduploaddialog.cpp b/qhimdtransfer/qhimduploaddialog.cpp
index acff6a7..390848e 100644
--- a/qhimdtransfer/qhimduploaddialog.cpp
+++ b/qhimdtransfer/qhimduploaddialog.cpp
@@ -34,6 +34,14 @@ void QHiMDUploadDialog::finished()
     /* Prevent shrinking of the box when hiding the indicators */
     m_ui->current->setMinimumSize(m_ui->current->size());
     m_ui->TrkPBar->hide();
+    /* set AllPBar to 100% if it is not used during transfer,
+     * current netmd uploads doesn´t set the range correctly
+     */
+    if(m_ui->AllPBar->maximum() == 0)
+    {
+        m_ui->AllPBar->setMaximum(1);
+        m_ui->AllPBar->setValue(1);
+    }
     m_ui->curtrack_label->hide();
 
     m_ui->cancel_button->hide();
@@ -42,7 +50,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));
diff --git a/qhimdtransfer/qhimduploaddialog.h b/qhimdtransfer/qhimduploaddialog.h
index 157c58d..621fffd 100644
--- a/qhimdtransfer/qhimduploaddialog.h
+++ b/qhimdtransfer/qhimduploaddialog.h
@@ -2,7 +2,7 @@
 #define QHIMDUPLOADDIALOG_H
 
 #include <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();
-- 
1.8.4.msysgit.0

>From d4f29483c31b0a857fc7c52ad355676962b4a06f Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Sat, 21 Dec 2013 17:28:48 +0100
Subject: [PATCH 6/6] netmd support: changed qhimdmainwindow to use the new
 classes/device structure , removed upload routines which are already moved to
 other classes, updated qhimdtransfer.pro file

---
 qhimdtransfer/qhimdmainwindow.cpp | 482 ++++++++++----------------------------
 qhimdtransfer/qhimdmainwindow.h   |  30 +--
 qhimdtransfer/qhimdmainwindow.ui  |  18 +-
 qhimdtransfer/qhimdtransfer.pro   |  13 +-
 4 files changed, 161 insertions(+), 382 deletions(-)

diff --git a/qhimdtransfer/qhimdmainwindow.cpp b/qhimdtransfer/qhimdmainwindow.cpp
index 1b86651..d5031b2 100644
--- a/qhimdtransfer/qhimdmainwindow.cpp
+++ b/qhimdtransfer/qhimdmainwindow.cpp
@@ -1,213 +1,9 @@
 #include "qhimdmainwindow.h"
 #include "ui_qhimdmainwindow.h"
 #include "qhimdaboutdialog.h"
-#include "qhimduploaddialog.h"
 #include <QMessageBox>
 #include <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,23 +17,29 @@ 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(QMDTracksModel * model)
 {
-    int i = 0;
+    int i, width;
+    QString browser = current_device->deviceType() == NETMD_DEVICE ? "netmd_browser" : "himd_browser";
+    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&)));
+
+    // read saved column width for this model
+    for(i = 0; i < ui->TrackList->model()->columnCount(); i++)
+    {
+        width = settings.value(browser + QString::number(i), 0).toInt();
+        if(width != 0)
+            ui->TrackList->setColumnWidth(i, width);
+    }
 }
 
 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.setNameFilters(QStringList() << "*.mp3" << "*.wav" << "*.oma" << "*.aea");
     localmodel.setNameFilterDisables(false);
     localmodel.setReadOnly(false);
     localmodel.setRootPath("/");
@@ -255,130 +57,93 @@ void QHiMDMainWindow::init_local_browser()
 
 void QHiMDMainWindow::save_window_settings()
 {
-    int i = 0;
-
     settings.setValue("geometry", QMainWindow::saveGeometry());
     settings.setValue("windowState", QMainWindow::saveState());
-    for(;i < trackmodel.columnCount(); i++)
-        settings.setValue("himd_browser" + QString::number(i), ui->TrackList->columnWidth(i));
 }
 
 void QHiMDMainWindow::read_window_settings()
 {
-    int i = 0;
-    int width;
-
     QMainWindow::restoreGeometry(settings.value("geometry").toByteArray());
     QMainWindow::restoreState(settings.value("windowState").toByteArray());
-    for(; i < trackmodel.columnCount(); i++)
-    {
-        width = settings.value("himd_browser" + QString::number(i), 0).toInt();
-        if(width != 0)
-            ui->TrackList->setColumnWidth(i, width);
-    }
 }
 
 bool QHiMDMainWindow::autodetect_init()
 {
-    int k;
+    if(!QObject::connect(detect, SIGNAL(deviceListChanged(QMDDevicePtrList)), this, SLOT(device_list_changed(QMDDevicePtrList))))
+        return false;
 
-    k = (bool)QObject::connect(detect, SIGNAL(himd_found(QString)), this, SLOT(himd_found(QString)));
-    k += (bool)QObject::connect(detect, SIGNAL(himd_removed(QString)), this, SLOT(himd_removed(QString)));
+    detect->scan_for_minidisc_devices();
+    return true;
+}
 
-    if(!k)
-        return false;
+void QHiMDMainWindow::setCurrentDevice(QMDDevice *dev)
+{
+    current_device = dev;
+    QObject::connect(current_device, SIGNAL(closed()), this, SLOT(current_device_closed()));
 
-    QObject::connect(this, SIGNAL(himd_busy(QString)), detect, SLOT(himd_busy(QString)));
-    QObject::connect(this, SIGNAL(himd_idle(QString)), detect, SLOT(himd_idle(QString)));
+    if(current_device->deviceType() == NETMD_DEVICE)
+        init_himd_browser(&ntmodel);
 
-    detect->scan_for_himd_devices();
-    return true;
+    else if(current_device->deviceType() == HIMD_DEVICE)
+        init_himd_browser(&htmodel);
 }
 
-void QHiMDMainWindow::open_himd_at(const QString & path)
+void QHiMDMainWindow::open_device(QMDDevice * dev)
 {
-    QMessageBox himdStatus;
+    QMessageBox mdStatus;
     QString error;
+    QMDTracksModel * mod;
 
-    error = trackmodel.open(path.toLatin1());
+    int index = ui->himd_devices->currentIndex();  // remember current index of devices combo box, will be resetted by current_device_closed() function
+
+    if (!dev)
+    {
+        current_device_closed();
+        ui->himd_devices->setCurrentIndex(0);
+        return;
+    }
 
-    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(current_device)
+    {
+        current_device_closed();
+        ui->himd_devices->setCurrentIndex(index);  // set correct device index in the combo box
+    }
+
+    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);
+        ui->himd_devices->setCurrentIndex(0);
         return;
     }
 
-    ui->himdpath->setText(path);
-    settings.setValue("lastHiMDDirectory", path);
+    setCurrentDevice(dev);
+    mod = (QMDTracksModel *)ui->TrackList->model();
+    error = mod->open(dev);
 
-    himd_device * dev = detect->find_by_path(path);
-    if(dev)
-        ui->statusBar->showMessage(dev->recorder_name);
-    else
-        ui->statusBar->clearMessage();
+    if (!error.isEmpty())
+    {
+        mdStatus.setText(tr("Error opening minidisc device (") + current_device->name() + "):\n" + error);
+        mdStatus.exec();
+        set_buttons_enable(1,0,0,0,0,0,1);
+        ui->himd_devices->setCurrentIndex(0);
+        return;
+     }
 
+    localmodel.setSelectableExtensions(mod->downloadableFileExtensions());
+    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());
-
-    QHiMDTrackList tracks = trackmodel.tracks(ui->TrackList->selectionModel()->selectedRows(0));
-
-    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");
+    QMDTrackIndexList tlist;
 
-        if(errmsg.isNull())
-            uploadDialog->trackSucceeded();
-        else
-            uploadDialog->trackFailed(errmsg);
+    foreach(QModelIndex index, ui->TrackList->selectionModel()->selectedRows(0))
+        tlist.append(index.row());
 
-        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 +151,24 @@ 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_local_browser();
     read_window_settings();
-    ui->himd_devices->hide();
+    ui->himdpath->hide();   // not needed, replaced by combo box
     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 +221,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 +232,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);
-
-    if(ui->himd_devices->isHidden())
-    {
-        ui->himd_devices->show();
-        ui->himdpath->hide();
-    }
+    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() )));
 
-    open_himd_at(HiMDDirectory);
+    open_device(dev);
 }
 
 void QHiMDMainWindow::on_upload_button_clicked()
@@ -506,67 +266,79 @@ void QHiMDMainWindow::handle_local_selection_change(const QItemSelection&, const
     }
 
     if(localmodel.fileInfo(index).isFile())
-        download_possible = trackmodel.is_open();
+        download_possible = current_device && 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, just to be sure, should be handled by closed() signal */
+    if(current_device && 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();
+    // add dummy entry for <disconnected>
+    ui->himd_devices->addItem("<disconnected>");
 
-    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())
+    if(current_device)
+        ui->himd_devices->setCurrentIndex(dplist.indexOf(current_device) + 1);
+    else
     {
-        index = ui->himd_devices->findText(HiMDPath);
-        ui->himd_devices->setCurrentIndex(index);
-        open_himd_at(HiMDPath);
+        if(dplist.count() > 1)   // open first autodetected device
+        {
+            ui->himd_devices->setCurrentIndex(2);
+            open_device(dplist.at(1));
+        }
     }
-
 }
 
-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)
+    if (index == 0)  // disconnected
     {
-        ui->himd_devices->removeItem(index);
+        current_device_closed();
+        return;
     }
 
-    if(ui->himd_devices->count() == 0)
-    {
-        ui->himd_devices->hide();
-        ui->himdpath->show();
-    }
+    dev = (QMDDevice *)ui->himd_devices->itemData(index).value<void *>();
+    open_device(dev);
 }
 
-void QHiMDMainWindow::on_himd_devices_activated(QString device)
+void QHiMDMainWindow::current_device_closed()
 {
-    open_himd_at(device);
+    int i;
+
+    if(!current_device)
+        return;
+
+    QString browser = current_device->deviceType() == NETMD_DEVICE ? "netmd_browser" : "himd_browser";
+    QMDTracksModel * mod = (QMDTracksModel *)ui->TrackList->model();
+
+    QObject::disconnect(current_device, SIGNAL(closed()), this, SLOT(current_device_closed()));
+
+    // save column width for this model first
+    for(i = 0;i < mod->columnCount(); i++)
+        settings.setValue(browser + QString::number(i), ui->TrackList->columnWidth(i));
+
+    mod->close();
+    current_device = NULL;
+    ui->DiscTitle->setText(QString());
+    ui->himd_devices->setCurrentIndex(0);
+    set_buttons_enable(1,0,0,0,0,0,1);
 }
 
 void QHiMDMainWindow::on_download_button_clicked()
diff --git a/qhimdtransfer/qhimdmainwindow.h b/qhimdtransfer/qhimdmainwindow.h
index b9b14f7..a462f84 100644
--- a/qhimdtransfer/qhimdmainwindow.h
+++ b/qhimdtransfer/qhimdmainwindow.h
@@ -6,14 +6,8 @@
 #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"
 
 extern "C" {
 #include <sox.h>
@@ -36,22 +30,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(QMDTracksModel *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 +56,10 @@ 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..d54f20d 100644
--- a/qhimdtransfer/qhimdmainwindow.ui
+++ b/qhimdtransfer/qhimdmainwindow.ui
@@ -35,7 +35,7 @@
            </font>
           </property>
           <property name="text">
-           <string>HiMD path</string>
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;minidisc device (path):&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;disc title:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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/qhimdtransfer.pro b/qhimdtransfer/qhimdtransfer.pro
index 6d3e39d..9ac07cb 100644
--- a/qhimdtransfer/qhimdtransfer.pro
+++ b/qhimdtransfer/qhimdtransfer.pro
@@ -54,8 +54,10 @@ HEADERS += qhimdaboutdialog.h \
     qhimdformatdialog.h \
     qhimduploaddialog.h \
     qhimdmainwindow.h \
-    qhimdmodel.h \
-    qhimddetection.h
+    qhimddetection.h \
+    qmdmodel.h \
+    qmdtrack.h \
+    qmddevice.h
 FORMS += qhimdaboutdialog.ui \
     qhimdformatdialog.ui \
     qhimduploaddialog.ui \
@@ -65,8 +67,10 @@ SOURCES += main.cpp \
     qhimdformatdialog.cpp \
     qhimduploaddialog.cpp \
     qhimdmainwindow.cpp \
-    qhimdmodel.cpp \
-    qhimddetection.cpp
+    qhimddetection.cpp \
+    qmdmodel.cpp \
+    qmdtrack.cpp \
+    qmddevice.cpp
 win32:SOURCES += qhimdwindetection.cpp
 else:SOURCES += qhimddummydetection.cpp
 RESOURCES += icons.qrc
@@ -82,6 +86,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)
-- 
1.8.4.msysgit.0

<-- thread -->
<-- date -->
  • Follow-Ups:
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
  • References:
    • [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] netmd support for qhimdtransfer gui application
      • From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
  • linux-minidisc - December 2013 - Archives indexes sorted by:
    [ thread ] [ subject ] [ author ] [ date ]
  • Complete archive of the linux-minidisc mailing list
  • More info on this list...

Hilfe

  • FAQ
  • Dienstbeschreibung
  • ZEDAT Beratung
  • postmaster@lists.fu-berlin.de

Service-Navigation

  • Startseite
  • Listenauswahl

Einrichtung Mailingliste

  • ZEDAT-Portal
  • Mailinglisten Portal