Am 10.01.2013 00:18, schrieb Thomas
Arp:
Hi,Hi, now i have implemeted netmd usb upload routine (supported by Sony MZ-RH1 only). You can find my work at netmd_integration_latest branch on my github account [1]. I made a patch attached to this mail. Thomas [1] http://github.com/tharp/linux-minidisc |
>From fcb9ecc352ad6687edb708cfd83a7341759f8060 Mon Sep 17 00:00:00 2001 From: Thomas Arp <manner.moe@gmx.de> Date: Sat, 12 Jan 2013 23:31:15 +0100 Subject: [PATCH 2/2] implement netmd usb uploads (supported by SONY MZ-RH1 only) --- libnetmd/libnetmd.pro | 3 +- libnetmd/libnetmd_extended.h | 37 +++++++++ qhimdtransfer/qhimdmainwindow.cpp | 2 +- qhimdtransfer/qhimduploaddialog.cpp | 7 ++ qhimdtransfer/qmddevice.cpp | 145 +++++++++++++++++++++++++++++++++++- qhimdtransfer/qmddevice.h | 11 ++- qhimdtransfer/qmdmodel.cpp | 2 +- qhimdtransfer/qmdtrack.cpp | 16 +++- qhimdtransfer/qmdtrack.h | 11 ++- 9 files changed, 218 insertions(+), 16 deletions(-) create mode 100644 libnetmd/libnetmd_extended.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/qhimdmainwindow.cpp b/qhimdtransfer/qhimdmainwindow.cpp index a406ea5..8c4d19f 100644 --- a/qhimdtransfer/qhimdmainwindow.cpp +++ b/qhimdtransfer/qhimdmainwindow.cpp @@ -31,7 +31,7 @@ void QHiMDMainWindow::init_local_browser() { QStringList DownloadFileList; localmodel.setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - localmodel.setNameFilters(QStringList() << "*.mp3" << "*.wav" << "*.oma"); + localmodel.setNameFilters(QStringList() << "*.mp3" << "*.wav" << "*.oma" << "*.aea"); localmodel.setNameFilterDisables(false); localmodel.setReadOnly(false); localmodel.setRootPath("/"); diff --git a/qhimdtransfer/qhimduploaddialog.cpp b/qhimdtransfer/qhimduploaddialog.cpp index 13f327e..605c868 100644 --- a/qhimdtransfer/qhimduploaddialog.cpp +++ b/qhimdtransfer/qhimduploaddialog.cpp @@ -34,6 +34,13 @@ 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(); diff --git a/qhimdtransfer/qmddevice.cpp b/qhimdtransfer/qmddevice.cpp index eeb7e78..be85492 100644 --- a/qhimdtransfer/qmddevice.cpp +++ b/qhimdtransfer/qmddevice.cpp @@ -166,12 +166,151 @@ QNetMDTrack QNetMDDevice::netmdTrack(unsigned int trkindex) 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(); + /* 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 change libnetmd to make use of a single chunk transfer function 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; + + // create filename first + if(track.title().isEmpty()) + filename = tr("Track %1").arg(track.tracknum()); + 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() needed by 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); + + fclose(file); + if(!errmsg.isNull()) { + QFile f(filepath); + if(f.exists()) + f.remove(); + } +} + void QNetMDDevice::batchUpload(QMDTrackIndexList tlist, QString path) { - QMessageBox mdStatus; + int allblocks = 0; + + setBusy(true); + + /* 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; + } - mdStatus.setText(tr("netmd uploads are not implemented, yet\n will be comming soon")); - mdStatus.exec(); + uploadDialog.finished(); + setBusy(false); } /* himd device members */ diff --git a/qhimdtransfer/qmddevice.h b/qhimdtransfer/qmddevice.h index 2e92102..3be6c36 100644 --- a/qhimdtransfer/qmddevice.h +++ b/qhimdtransfer/qmddevice.h @@ -65,6 +65,8 @@ 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(); @@ -74,22 +76,23 @@ public: virtual QString discTitle(); // virtual QNetMDTrack netmdTrack(unsigned int trkindex); virtual void batchUpload(QMDTrackIndexList tlist, QString path); - virtual void upload(unsigned int trackidx, 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); // - QString dumpmp3(const QHiMDTrack &trk, QString file); - QString dumpoma(const QHiMDTrack & track, QString file); - QString dumppcm(const QHiMDTrack &track, QString file); virtual void upload(unsigned int trackidx, QString path); virtual void batchUpload(QMDTrackIndexList tlist, QString path); diff --git a/qhimdtransfer/qmdmodel.cpp b/qhimdtransfer/qmdmodel.cpp index abbdddc..04e67da 100644 --- a/qhimdtransfer/qmdmodel.cpp +++ b/qhimdtransfer/qmdmodel.cpp @@ -73,7 +73,7 @@ QVariant QNetMDTracksModel::data(const QModelIndex & index, int role) const QNetMDTrack track = allTracks[index.row()]; if(role == Qt::CheckStateRole && index.column() == CoUploadable) - return track.copyprotected() ? Qt::Unchecked : Qt::Checked; + return ((ndev->name() != "SONY MZ-RH1 (NetMD)") && track.copyprotected()) ? Qt::Unchecked : Qt::Checked; if(role == Qt::DisplayRole) { diff --git a/qhimdtransfer/qmdtrack.cpp b/qhimdtransfer/qmdtrack.cpp index 84643f8..e3ea7d7 100644 --- a/qhimdtransfer/qmdtrack.cpp +++ b/qhimdtransfer/qmdtrack.cpp @@ -1,4 +1,5 @@ #include "qmdtrack.h" +#include <QDebug> static QString get_himd_str(struct himd * himd, int idx) { @@ -112,8 +113,6 @@ QNetMDTrack::QNetMDTrack(netmd_dev_handle * deviceh, minidisc * my_md, unsigned { uint8_t g; struct netmd_pair const *bitrate; - unsigned char bitrate_id; - unsigned char channel; char *name, buffer[256]; devh = deviceh; @@ -147,11 +146,15 @@ QNetMDTrack::QNetMDTrack(netmd_dev_handle * deviceh, minidisc * my_md, unsigned titlestring = QString(name); codecstring = QString(bitrate->name); + blocks = 0; } unsigned int QNetMDTrack::tracknum() const { - return (trkindex < 0 ? trkindex : trkindex + 1); + /* 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 @@ -198,7 +201,12 @@ bool QNetMDTrack::copyprotected() const } } +void QNetMDTrack::setBlocks(int cnt) +{ + blocks = cnt; +} + int QNetMDTrack::blockcount() const { - return 0; // not implemented, yet + return blocks; } diff --git a/qhimdtransfer/qmdtrack.h b/qhimdtransfer/qmdtrack.h index c7f30ad..47f93f8 100644 --- a/qhimdtransfer/qmdtrack.h +++ b/qhimdtransfer/qmdtrack.h @@ -13,9 +13,12 @@ #endif extern "C" { -#include <libnetmd.h> +#include <libnetmd_extended.h> } +/* define buffer size for netmd downloads */ +#define NETMD_RECV_BUF_SIZE 0x10000 + class QMDTrack { public: @@ -61,8 +64,11 @@ class QNetMDTrack : public QMDTrack { 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, unsigned int trackindex); virtual unsigned int tracknum() const; virtual QString group() const; @@ -70,6 +76,7 @@ public: virtual QString codecname() const; virtual QTime duration() const; virtual bool copyprotected() const; + virtual void setBlocks(int cnt); virtual int blockcount() const; }; -- 1.8.0.msysgit.0