[synthclone] push by surfacep...@gmail.com - Add import functionality to Hydrogen plugin (note that import of FLAC ... on 2013-03-03 08:22 GMT

1 view
Skip to first unread message

synth...@googlecode.com

unread,
Mar 3, 2013, 3:22:46 AM3/3/13
to synthclone-...@googlegroups.com
Revision: e7e1ce08a9be
Author: Devin Anderson <surface...@gmail.com>
Date: Sun Mar 3 00:22:20 2013
Log: Add import functionality to Hydrogen plugin (note that import of
FLAC samples doesn't currently work due to what I believe is a bug in
libsndfile, filed at https://github.com/erikd/libsndfile/issues/34). Make
sure that samples added to a session always go through the session sample
data update process to ensure we have 32-bit float WAV files by default in
the session.

http://code.google.com/p/synthclone/source/detail?r=e7e1ce08a9be

Added:
/src/plugins/hydrogen/archiveheader.cpp
/src/plugins/hydrogen/archiveheader.h
/src/plugins/hydrogen/archivereader.cpp
/src/plugins/hydrogen/archivereader.h
/src/plugins/hydrogen/importer.cpp
/src/plugins/hydrogen/importer.h
/src/plugins/hydrogen/temporarydir.cpp
/src/plugins/hydrogen/temporarydir.h
Modified:
/src/plugins/hydrogen/archivewriter.cpp
/src/plugins/hydrogen/archivewriter.h
/src/plugins/hydrogen/hydrogen.pro
/src/plugins/hydrogen/participant.cpp
/src/plugins/hydrogen/participant.h
/src/plugins/hydrogen/target.cpp
/src/synthclone/session.cpp
/src/synthclone/sessionsampledata.cpp
/src/synthclone/sessionsampledata.h
/src/synthclone/util.cpp
/src/synthclone/zone.cpp
/src/synthclone/zone.h
/src/synthclone/zonelistloader.cpp

=======================================
--- /dev/null
+++ /src/plugins/hydrogen/archiveheader.cpp Sun Mar 3 00:22:20 2013
@@ -0,0 +1,53 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <cassert>
+
+#include "archiveheader.h"
+
+ArchiveHeader::ArchiveHeader(const QString &path, qint64 size, QObject
*parent):
+ QObject(parent)
+{
+ assert(size >= 0);
+ this->path = path;
+ this->size = size;
+}
+
+ArchiveHeader::~ArchiveHeader()
+{
+ // Empty
+}
+
+QString
+ArchiveHeader::getPath() const
+{
+ return path;
+}
+
+qint64
+ArchiveHeader::getSize() const
+{
+ return size;
+}
+
+bool
+ArchiveHeader::isFile() const
+{
+ return static_cast<bool>(size);
+}
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/archiveheader.h Sun Mar 3 00:22:20 2013
@@ -0,0 +1,52 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ARCHIVEHEADER_H__
+#define __ARCHIVEHEADER_H__
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+class ArchiveHeader: public QObject {
+
+ Q_OBJECT
+
+public:
+
+ ArchiveHeader(const QString &path, qint64 size=0, QObject *parent=0);
+
+ ~ArchiveHeader();
+
+ QString
+ getPath() const;
+
+ qint64
+ getSize() const;
+
+ bool
+ isFile() const;
+
+private:
+
+ QString path;
+ qint64 size;
+
+};
+
+#endif
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/archivereader.cpp Sun Mar 3 00:22:20 2013
@@ -0,0 +1,131 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <cassert>
+#include <stdexcept>
+
+#include <QtCore/QDebug>
+
+#include <archive_entry.h>
+
+#include <synthclone/error.h>
+
+#include "archivereader.h"
+
+ArchiveReader::ArchiveReader(const QString &path, QObject *parent):
+ QObject(parent)
+{
+ arch = archive_read_new();
+ if (! arch) {
+ throw std::bad_alloc();
+ }
+ try {
+ if (archive_read_support_compression_gzip(arch) != ARCHIVE_OK) {
+ throw synthclone::Error(archive_error_string(arch));
+ }
+ if (archive_read_support_format_tar(arch) != ARCHIVE_OK) {
+ throw synthclone::Error(archive_error_string(arch));
+ }
+ QByteArray pathBytes = path.toLocal8Bit();
+ if (archive_read_open_filename(arch, pathBytes.constData(),
8192) !=
+ ARCHIVE_OK) {
+ throw synthclone::Error(archive_error_string(arch));
+ }
+ } catch (...) {
+ archive_read_finish(arch);
+ throw;
+ }
+ closed = false;
+ header = 0;
+}
+
+ArchiveReader::~ArchiveReader()
+{
+ if (! closed) {
+ try {
+ close();
+ } catch (synthclone::Error &e) {
+ qWarning() << tr("Error in ArchiveReader destructor: %1").
+ arg(e.getMessage());
+ }
+ }
+ archive_read_finish(arch);
+ if (header) {
+ delete header;
+ }
+}
+
+void
+ArchiveReader::close()
+{
+ assert(! closed);
+ if (archive_read_close(arch) != ARCHIVE_OK) {
+ throw synthclone::Error(archive_error_string(arch));
+ }
+ closed = true;
+}
+
+size_t
+ArchiveReader::readData(char *buffer, size_t size)
+{
+ assert(buffer);
+ assert(size);
+
+ ssize_t result = archive_read_data(arch, buffer, size);
+ if (result < 0) {
+ throw synthclone::Error(archive_error_string(arch));
+ }
+ return static_cast<size_t>(result);
+}
+
+const ArchiveHeader *
+ArchiveReader::readHeader()
+{
+ struct archive_entry *entry;
+ switch (archive_read_next_header(arch, &entry)) {
+ case ARCHIVE_EOF:
+ return 0;
+ case ARCHIVE_OK:
+ break;
+ default:
+ throw synthclone::Error(archive_error_string(arch));
+ }
+
+ const char *path = archive_entry_pathname(entry);
+ assert(path);
+ qint64 size = static_cast<qint64>(archive_entry_size(entry));
+ assert(size >= 0);
+
+ if (! header) {
+ header = new ArchiveHeader(path, size, this);
+ } else {
+ header->~ArchiveHeader();
+ header = new (header) ArchiveHeader(path, size, this);
+ }
+
+ return header;
+}
+
+void
+ArchiveReader::skipData()
+{
+ if (archive_read_data_skip(arch) < 0) {
+ throw synthclone::Error(archive_error_string(arch));
+ }
+}
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/archivereader.h Sun Mar 3 00:22:20 2013
@@ -0,0 +1,60 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ARCHIVEREADER_H__
+#define __ARCHIVEREADER_H__
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+#include <archive.h>
+
+#include "archiveheader.h"
+
+class ArchiveReader: public QObject {
+
+ Q_OBJECT
+
+public:
+
+ ArchiveReader(const QString &path, QObject *parent=0);
+
+ ~ArchiveReader();
+
+ void
+ close();
+
+ size_t
+ readData(char *buffer, size_t size);
+
+ const ArchiveHeader *
+ readHeader();
+
+ void
+ skipData();
+
+private:
+
+ archive *arch;
+ bool closed;
+ ArchiveHeader *header;
+
+};
+
+#endif
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/importer.cpp Sun Mar 3 00:22:20 2013
@@ -0,0 +1,212 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <cassert>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+
+#include <synthclone/error.h>
+#include <synthclone/sampleinputstream.h>
+
+#include "archivereader.h"
+#include "importer.h"
+#include "temporarydir.h"
+
+Importer::Importer(QObject *parent):
+ QObject(parent)
+{
+ path = "";
+}
+
+Importer::~Importer()
+{
+ // Empty
+}
+
+QString
+Importer::getPath() const
+{
+ return path;
+}
+
+void
+Importer::import()
+{
+ QDir kitDir;
+ TemporaryDir *tempDir;
+ QScopedPointer<TemporaryDir> tempDirPtr;
+ if (QFileInfo(path).isDir()) {
+ kitDir.setPath(path);
+ } else {
+
+ // Extract data from archive into temporary directory.
+ ArchiveReader archiveReader(path);
+ tempDir = new TemporaryDir(this);
+ tempDirPtr.reset(tempDir);
+ kitDir.setPath(tempDir->getPath());
+ char readBuffer[8192];
+ for (;;) {
+ const ArchiveHeader *header = archiveReader.readHeader();
+ if (! header) {
+ break;
+ }
+ if (! header->isFile()) {
+ continue;
+ }
+ QString headerPath = QFileInfo(header->getPath()).fileName();
+ QFile file(kitDir.absoluteFilePath(headerPath));
+ if (! file.open(QIODevice::WriteOnly)) {
+ throw synthclone::Error(file.errorString());
+ }
+ for (;;) {
+ size_t bytesRead = archiveReader.readData(readBuffer,
8192);
+ if (! bytesRead) {
+ break;
+ }
+ size_t bytesWritten = file.write(readBuffer, bytesRead);
+ assert(bytesRead == bytesWritten);
+ }
+ file.close();
+ }
+ }
+
+ // Parse the drumkit.xml file
+ QFile kitFile(kitDir.absoluteFilePath("drumkit.xml"));
+ if (! kitFile.open(QIODevice::ReadOnly)) {
+ throw synthclone::Error(kitFile.errorString());
+ }
+ QDomDocument document;
+ int column;
+ int line;
+ QString message;
+ bool result = document.setContent(&kitFile, &message, &line, &column);
+ kitFile.close();
+ if (! result) {
+ message = tr("'%1': error in drumkit.xml at line %2,
column %3: %4").
+ arg(path).arg(line).arg(column).arg(message);
+ throw synthclone::Error(message);
+ }
+
+ // Walk through the drumkit.xml file, creating zones for each layer.
+ QDomElement element = document.documentElement();
+ if (element.tagName() != "drumkit_info") {
+ message = tr("'%1': drumkit.xml: no 'drumkit_info' element");
+ throw synthclone::Error(message);
+ }
+ element = element.firstChildElement("instrumentList");
+ if (element.isNull()) {
+ message = tr("'%1' drumkit.xml: no 'instrumentList' element");
+ throw synthclone::Error(message);
+ }
+ synthclone::MIDIData note;
+ for (element = element.firstChildElement("instrument"), note = 0;
+ ! element.isNull();
+ element = element.nextSiblingElement("instrument"),
+ note = (note + 1) % 128) {
+ QDomElement layerElement = element.firstChildElement("layer");
+ if (layerElement.isNull()) {
+ importSample(kitDir, element, note, 127);
+ continue;
+ }
+ for (; ! layerElement.isNull();
+ layerElement = layerElement.nextSiblingElement("layer")) {
+ QDomElement maxElement = layerElement.firstChildElement("max");
+ synthclone::MIDIData velocity;
+ if (maxElement.isNull()) {
+ qWarning() << "'layer' element doesn't contain 'max'
element - "
+ "assuming velocity of 127";
+ velocity = 127;
+ } else {
+ QString strValue = maxElement.text();
+ bool success;
+ float value = strValue.toFloat(&success);
+ if (! success) {
+ qWarning() << "'layer' element contains
non-float 'max' "
+ "element - assuming velocity of 127";
+ velocity = 127;
+ } else if (value > 1.0) {
+ qWarning() << "'layer' element contains 'max' value
that's "
+ "too large - assuming velocity of 127";
+ velocity = 127;
+ } else if (value < 0.0) {
+ qWarning() << "'layer' element contains 'max' value
that's "
+ "too small - assuming velocity of 0";
+ velocity = 0;
+ } else {
+ velocity = static_cast<synthclone::MIDIData>(value *
127.0);
+ }
+ }
+ importSample(kitDir, layerElement, note, velocity);
+ }
+ }
+}
+
+void
+Importer::importSample(const QDir &kitDir, const QDomElement &element,
+ synthclone::MIDIData note, synthclone::MIDIData
velocity)
+{
+ // If there is no filename element, then we're done.
+ QDomElement filenameElement = element.firstChildElement("filename");
+ if (filenameElement.isNull()) {
+ return;
+ }
+ try {
+ synthclone::Sample sample
+ (kitDir.absoluteFilePath(QFileInfo(filenameElement.text()).
+ fileName()));
+ synthclone::SampleInputStream stream(sample);
+
+ // Check the total time consumed by the sample. If it isn't in the
+ // acceptable range for `synthclone` dry samples, then raise a
+ // `synthclone::Error`.
+ synthclone::SampleRate sampleRate = stream.getSampleRate();
+ synthclone::SampleTime time = stream.getFrames() /
+ static_cast<synthclone::SampleTime>(sampleRate);
+ QString message;
+ if (time > synthclone::SAMPLE_TIME_MAXIMUM) {
+ message = tr("%1: sample time is %2, which is greater than %3 "
+ "seconds").
+ arg(path).arg(time).arg(synthclone::SAMPLE_TIME_MAXIMUM);
+ throw synthclone::Error(message);
+ }
+ if (time < synthclone::SAMPLE_TIME_MINIMUM) {
+ message = tr("%1: sample time is %2, which is less than %3 "
+ "seconds").
+ arg(path).arg(time).arg(synthclone::SAMPLE_TIME_MINIMUM);
+ throw synthclone::Error(message);
+ }
+
+ emit layerImported(note, velocity, time, sample);
+ } catch (synthclone::Error &e) {
+ qWarning() << tr("%1, line %2, column %3: %4").
+ arg(kitDir.absoluteFilePath("drumkit.xml")).
+ arg(element.lineNumber()).arg(element.columnNumber()).
+ arg(e.getMessage());
+ }
+}
+
+void
+Importer::setPath(const QString &path)
+{
+ if (this->path != path) {
+ this->path = path;
+ emit pathChanged(path);
+ }
+}
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/importer.h Sun Mar 3 00:22:20 2013
@@ -0,0 +1,72 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __IMPORTER_H__
+#define __IMPORTER_H__
+
+#include <QtCore/QDir>
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtXml/QDomDocument>
+
+#include <synthclone/sample.h>
+#include <synthclone/types.h>
+
+class Importer: public QObject {
+
+ Q_OBJECT
+
+public:
+
+ explicit
+ Importer(QObject *parent=0);
+
+ ~Importer();
+
+ QString
+ getPath() const;
+
+public slots:
+
+ void
+ import();
+
+ void
+ setPath(const QString &path);
+
+signals:
+
+ void
+ layerImported(synthclone::MIDIData note, synthclone::MIDIData velocity,
+ synthclone::SampleTime time, synthclone::Sample &sample);
+
+ void
+ pathChanged(const QString &path);
+
+private:
+
+ void
+ importSample(const QDir &kitDir, const QDomElement &element,
+ synthclone::MIDIData note, synthclone::MIDIData velocity);
+
+ QString path;
+
+};
+
+#endif
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/temporarydir.cpp Sun Mar 3 00:22:20 2013
@@ -0,0 +1,90 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <cerrno>
+#include <cstring>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+
+#include <synthclone/error.h>
+
+#include "temporarydir.h"
+
+#if defined(SYNTHCLONE_PLATFORM_MACX) || defined(SYNTHCLONE_PLATFORM_UNIX)
+#include <cstdlib>
+#else
+#error "A temp directory implementation has not been defined for your
platform"
+#endif
+
+TemporaryDir::TemporaryDir(QObject *parent):
+ QObject(parent)
+{
+ QDir tempDir(QDir::tempPath());
+
+#if SYNTHCLONE_PLATFORM_MACX || SYNTHCLONE_PLATFORM_UNIX
+ QByteArray templateBytes =
tempDir.absoluteFilePath("XXXXXX").toLocal8Bit();
+ if (! mkdtemp(templateBytes.data())) {
+ throw synthclone::Error(tr("failed to create temporary
directory: %1").
+ arg(strerror(errno)));
+ }
+ path = QString(templateBytes);
+#endif
+
+}
+
+TemporaryDir::~TemporaryDir()
+{
+ try {
+ remove(path);
+ } catch (synthclone::Error &e) {
+ qWarning() << e.getMessage();
+ }
+}
+
+QString
+TemporaryDir::getPath() const
+{
+ return path;
+}
+
+void
+TemporaryDir::remove(const QString &path)
+{
+ QDir directory(path);
+ Q_FOREACH(QFileInfo info,
+ directory.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
+ QDir::Hidden | QDir::AllDirs |
+ QDir::Files)) {
+ QString subPath = info.absoluteFilePath();
+ if (info.isDir()) {
+ remove(subPath);
+ } else {
+ QFile file(subPath);
+ if (! file.remove()) {
+ throw synthclone::Error(file.errorString());
+ }
+ }
+ }
+ if (! directory.rmdir(path)) {
+ throw synthclone::Error(tr("failed to remove '%1': %2").
+ arg(path).arg(strerror(errno)));
+ }
+}
=======================================
--- /dev/null
+++ /src/plugins/hydrogen/temporarydir.h Sun Mar 3 00:22:20 2013
@@ -0,0 +1,49 @@
+/*
+ * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
+ * Copyright (C) 2013 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
Free
+ * Software Foundation; either version 2 of the License, or (at your
option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
Mass
+ * Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __TEMPORARYDIR_H__
+#define __TEMPORARYDIR_H__
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+class TemporaryDir: public QObject {
+
+ Q_OBJECT
+
+public:
+
+ explicit
+ TemporaryDir(QObject *parent=0);
+
+ ~TemporaryDir();
+
+ QString
+ getPath() const;
+
+private:
+
+ void
+ remove(const QString &path);
+
+ QString path;
+
+};
+
+#endif
=======================================
--- /src/plugins/hydrogen/archivewriter.cpp Sun Nov 4 16:01:32 2012
+++ /src/plugins/hydrogen/archivewriter.cpp Sun Mar 3 00:22:20 2013
@@ -1,6 +1,6 @@
/*
* libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
- * Copyright (C) 2011-2012 Devin Anderson
+ * Copyright (C) 2011-2013 Devin Anderson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
@@ -23,14 +23,12 @@
#include <archive_entry.h>

#include <QtCore/QDebug>
-#include <QtCore/QFileInfo>

#include <synthclone/error.h>

#include "archivewriter.h"

-ArchiveWriter::ArchiveWriter(const QString &path, const QString &kitName,
- QObject *parent):
+ArchiveWriter::ArchiveWriter(const QString &path, QObject *parent):
QObject(parent)
{
arch = archive_write_new();
@@ -54,7 +52,6 @@
throw;
}
closed = false;
- this->kitName = kitName;
}

ArchiveWriter::~ArchiveWriter()
@@ -71,69 +68,37 @@
}

void
-ArchiveWriter::addConfiguration(const QString &configuration)
+ArchiveWriter::close()
{
- int64_t size = static_cast<int64_t>(configuration.count());
- writeEntry("drumkit.xml", configuration.count());
- QByteArray confBytes = configuration.toLocal8Bit();
- ssize_t n = archive_write_data(arch, confBytes.constData(), size);
- if (n == -1) {
+ assert(! closed);
+ if (archive_write_close(arch) != ARCHIVE_OK) {
throw synthclone::Error(archive_error_string(arch));
}
- assert(n == size);
+ closed = true;
}

void
-ArchiveWriter::addSample(const QString &fileName,
- const synthclone::Sample &sample)
+ArchiveWriter::writeData(const QByteArray &data)
{
- QString path = sample.getPath();
- QFileInfo info(path);
- assert(info.exists());
- assert(info.isFile());
- writeEntry(fileName, info.size());
- QFile file(path);
- if (! file.open(QFile::ReadOnly)) {
- QString message = tr("could not open '%1': %2").
- arg(info.absoluteFilePath(), file.errorString());
- throw synthclone::Error(message);
- }
- for (;;) {
- QByteArray data = file.read(8192);
- if (data.isEmpty()) {
- break;
- }
- int length = data.count();
- ssize_t n = archive_write_data(arch, data.constData(), length);
- if (n == -1) {
- throw synthclone::Error(archive_error_string(arch));
- }
- assert(n == length);
- }
-}
-
-void
-ArchiveWriter::close()
-{
- assert(! closed);
- if (archive_write_close(arch) != ARCHIVE_OK) {
+ ssize_t size = static_cast<ssize_t>(data.count());
+ ssize_t n = archive_write_data(arch, data.constData(), size);
+ if (n == -1) {
throw synthclone::Error(archive_error_string(arch));
}
- closed = true;
+ assert(n == size);
}

void
-ArchiveWriter::writeEntry(const QString &fileName, int64_t size)
+ArchiveWriter::writeHeader(const ArchiveHeader &header)
{
- assert(! fileName.isEmpty());
- assert(size > 0);
struct archive_entry *entry = archive_entry_new();
if (! entry) {
throw std::bad_alloc();
}
- QByteArray path = QString("%1/%2").arg(kitName,
fileName).toLocal8Bit();
+ QByteArray path = header.getPath().toLocal8Bit();
+ qint64 size = header.getSize();
archive_entry_set_pathname(entry, path.constData());
- archive_entry_set_size(entry, size);
+ archive_entry_set_size(entry, static_cast<int64_t>(size));
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_perm(entry, 0644);
int result = archive_write_header(arch, entry);
=======================================
--- /src/plugins/hydrogen/archivewriter.h Sun Nov 4 16:01:32 2012
+++ /src/plugins/hydrogen/archivewriter.h Sun Mar 3 00:22:20 2013
@@ -1,6 +1,6 @@
/*
* libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
- * Copyright (C) 2011-2012 Devin Anderson
+ * Copyright (C) 2011-2013 Devin Anderson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
@@ -22,7 +22,7 @@

#include <archive.h>

-#include <synthclone/sample.h>
+#include "archiveheader.h"

class ArchiveWriter: public QObject {

@@ -30,28 +30,23 @@

public:

- ArchiveWriter(const QString &path, const QString &kitName,
- QObject *parent=0);
+ ArchiveWriter(const QString &path, QObject *parent=0);

~ArchiveWriter();

void
- addConfiguration(const QString &configuration);
+ close();

void
- addSample(const QString &fileName, const synthclone::Sample &sample);
+ writeData(const QByteArray &data);

void
- close();
+ writeHeader(const ArchiveHeader &header);

private:

- void
- writeEntry(const QString &fileName, int64_t size);
-
archive *arch;
bool closed;
- QString kitName;

};

=======================================
--- /src/plugins/hydrogen/hydrogen.pro Fri Nov 25 14:00:11 2011
+++ /src/plugins/hydrogen/hydrogen.pro Sun Mar 3 00:22:20 2013
@@ -4,11 +4,15 @@
# Build

################################################################################

-HEADERS += archivewriter.h \
+HEADERS += archiveheader.h \
+ archivereader.h \
+ archivewriter.h \
+ importer.h \
participant.h \
plugin.h \
target.h \
targetview.h \
+ temporarydir.h \
types.h \
velocitycomparer.h \
zonekey.h
@@ -17,11 +21,15 @@
OBJECTS_DIR = $${MAKEDIR}/plugins/hydrogen
RCC_DIR = $${MAKEDIR}/plugins/hydrogen
RESOURCES += hydrogen.qrc
-SOURCES += archivewriter.cpp \
+SOURCES += archiveheader.cpp \
+ archivereader.cpp \
+ archivewriter.cpp \
+ importer.cpp \
participant.cpp \
plugin.cpp \
target.cpp \
targetview.cpp \
+ temporarydir.cpp \
velocitycomparer.cpp \
zonekey.cpp
TARGET = $$qtLibraryTarget(synthclone_hydrogen)
=======================================
--- /src/plugins/hydrogen/participant.cpp Sun Oct 7 22:12:09 2012
+++ /src/plugins/hydrogen/participant.cpp Sun Mar 3 00:22:20 2013
@@ -1,6 +1,6 @@
/*
- * libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
- * Copyright (C) 2011 Devin Anderson
+ * libsynthclone_hydrogen - Hydrogen target and import plugin for
`synthclone`
+ * Copyright (C) 2011-2013 Devin Anderson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
@@ -22,23 +22,52 @@
#include "participant.h"

Participant::Participant(QObject *parent):
- synthclone::Participant(tr("Hydrogen"), 0, 0, 1, "Devin Anderson",
- tr("Creates Hydrogen drum kits."), parent),
- addTargetAction(tr("Hydrogen"))
+ synthclone::Participant(tr("Hydrogen"), 0, 1, 1, "Devin Anderson",
+ tr("Creates and imports samples from Hydrogen
drum "
+ "kits"), parent),
+ addTargetAction(tr("Hydrogen")),
+ importArchiveAction(tr("Hydrogen Archive")),
+ importKitAction(tr("Hydrogen Kit"))
{
directoryView.setFilesVisible(false);

directoryView.setOperation(synthclone::FileSelectionView::OPERATION_SAVE);
directoryView.setTitle(tr("Save Hydrogen Kit"));
+
+ importView.setFilesVisible(true);
+ importView.setOperation(synthclone::FileSelectionView::OPERATION_OPEN);
+ importView.setTitle(tr("Import Layers from Hydrogen Kit"));
+
connect(&addTargetAction, SIGNAL(triggered()),
SLOT(handleTargetAddition()));
+
connect(&directoryView, SIGNAL(closeRequest()),
SLOT(handleDirectoryViewCloseRequest()));
connect(&directoryView, SIGNAL(pathsSelected(const QStringList &)),
SLOT(handleDirectoryViewPathSelection(const QStringList &)));
+
+ connect(&importArchiveAction, SIGNAL(triggered()),
+ SLOT(handleImportArchiveRequest()));
+
+ connect(&importKitAction, SIGNAL(triggered()),
+ SLOT(handleImportKitRequest()));
+
+ connect(&importer,
+ SIGNAL(layerImported(synthclone::MIDIData,
synthclone::MIDIData,
+ synthclone::SampleTime,
synthclone::Sample &)),
+ SLOT(handleLayerImport(synthclone::MIDIData,
synthclone::MIDIData,
+ synthclone::SampleTime,
+ synthclone::Sample &)));
+
+ connect(&importView, SIGNAL(closeRequest()),
+ SLOT(handleImportViewCloseRequest()));
+ connect(&importView, SIGNAL(pathsSelected(const QStringList &)),
+ SLOT(handleImportViewPathSelection(const QStringList &)));
+
connect(&targetView, SIGNAL(closeRequest()),
SLOT(handleTargetViewCloseRequest()));
connect(&targetView, SIGNAL(pathLookupRequest()),
SLOT(handleTargetViewPathLookupRequest()));
+
configuredTarget = 0;
context = 0;
}
@@ -52,6 +81,12 @@
Participant::activate(synthclone::Context &context, const QVariant
&/*state*/)
{
context.addMenuAction(&addTargetAction, synthclone::MENU_ADD_TARGET);
+ QStringList importSubMenus;
+ importSubMenus << tr("Import From ...");
+ context.addMenuAction(&importArchiveAction, synthclone::MENU_ZONES,
+ importSubMenus);
+ context.addMenuAction(&importKitAction, synthclone::MENU_ZONES,
+ importSubMenus);
this->context = &context;
configuredTarget = 0;
}
@@ -149,6 +184,8 @@
Participant::deactivate(synthclone::Context &context)
{
context.removeMenuAction(&addTargetAction);
+ context.removeMenuAction(&importArchiveAction);
+ context.removeMenuAction(&importKitAction);
this->context = 0;
}

@@ -258,6 +295,56 @@
configuredTarget->setPath(paths[0]);
directoryView.setVisible(false);
}
+
+void
+Participant::handleImportArchiveRequest()
+{
+ importView.setFilesVisible(true);
+ importView.setSelectionFilter
+ (synthclone::FileSelectionView::SELECTIONFILTER_EXISTING_FILE);
+ importView.setVisible(true);
+}
+
+void
+Participant::handleImportKitRequest()
+{
+ importView.setFilesVisible(false);
+ importView.setSelectionFilter
+ (synthclone::FileSelectionView::SELECTIONFILTER_DIRECTORY);
+ importView.setVisible(true);
+}
+
+void
+Participant::handleImportViewCloseRequest()
+{
+ importView.setVisible(false);
+}
+
+void
+Participant::handleImportViewPathSelection(const QStringList &paths)
+{
+ assert(paths.count() == 1);
+ QString path = paths[0];
+ importer.setPath(path);
+ importView.setVisible(false);
+ if (path.count()) {
+ importer.import();
+ }
+}
+
+void
+Participant::handleLayerImport(synthclone::MIDIData note,
+ synthclone::MIDIData velocity,
+ synthclone::SampleTime time,
+ synthclone::Sample &sample)
+{
+ synthclone::Zone *zone = context->addZone();
+ zone->setDrySample(&sample);
+ zone->setNote(note);
+ zone->setReleaseTime(synthclone::SAMPLE_TIME_MINIMUM);
+ zone->setSampleTime(time);
+ zone->setVelocity(velocity);
+}

void
Participant::handleTargetAddition()
=======================================
--- /src/plugins/hydrogen/participant.h Sun Oct 9 12:02:52 2011
+++ /src/plugins/hydrogen/participant.h Sun Mar 3 00:22:20 2013
@@ -1,6 +1,6 @@
/*
* libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
- * Copyright (C) 2011 Devin Anderson
+ * Copyright (C) 2011-2013 Devin Anderson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
@@ -25,6 +25,7 @@
#include <synthclone/fileselectionview.h>
#include <synthclone/participant.h>

+#include "importer.h"
#include "target.h"
#include "targetview.h"

@@ -59,6 +60,22 @@
void
handleDirectoryViewPathSelection(const QStringList &paths);

+ void
+ handleImportArchiveRequest();
+
+ void
+ handleImportKitRequest();
+
+ void
+ handleImportViewCloseRequest();
+
+ void
+ handleImportViewPathSelection(const QStringList &paths);
+
+ void
+ handleLayerImport(synthclone::MIDIData note, synthclone::MIDIData
velocity,
+ synthclone::SampleTime time, synthclone::Sample
&sample);
+
void
handleTargetAddition();

@@ -86,6 +103,10 @@
Target *configuredTarget;
synthclone::Context *context;
synthclone::FileSelectionView directoryView;
+ synthclone::MenuAction importArchiveAction;
+ synthclone::MenuAction importKitAction;
+ Importer importer;
+ synthclone::FileSelectionView importView;
TargetView targetView;

};
=======================================
--- /src/plugins/hydrogen/target.cpp Mon Oct 3 01:06:07 2011
+++ /src/plugins/hydrogen/target.cpp Sun Mar 3 00:22:20 2013
@@ -1,6 +1,6 @@
/*
* libsynthclone_hydrogen - Hydrogen target plugin for `synthclone`
- * Copyright (C) 2011 Devin Anderson
+ * Copyright (C) 2011-2013 Devin Anderson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
Free
@@ -19,6 +19,7 @@

#include <cassert>

+#include <QtCore/QFileInfo>
#include <QtCore/QLocale>

#include <synthclone/error.h>
@@ -64,8 +65,7 @@
throw synthclone::Error(message);
}
ArchiveWriter archiveWriter
- (directory.absoluteFilePath(QString("%1.h2drumkit").arg(kitName)),
- kitName);
+ (directory.absoluteFilePath(QString("%1.h2drumkit").arg(kitName)));

// This plugin builds different instruments for every different
combination
// of channel, note, channel pressure, aftertouch, and control
values. If
@@ -225,7 +225,12 @@
confWriter.writeEndElement();
confWriter.writeEndDocument();

- archiveWriter.addConfiguration(configuration);
+ // Add the configuration to the archive writer.
+ QByteArray configurationBytes = configuration.toLocal8Bit();
+ ArchiveHeader header(QString("%1/drumkit.xml").arg(kitName),
+ configurationBytes.count());
+ archiveWriter.writeHeader(header);
+ archiveWriter.writeData(configurationBytes);

if (layerOverflows) {
message = tr("%1 instruments contained more than %2 layers.
Hydrogen "
@@ -456,7 +461,27 @@
copier.copy(inputStream, outputStream, inputStream.getFrames());
outputStream.close();

- archiveWriter.addSample(sampleName, outSample);
+ // Write sample to archive.
+ QString path = outSample.getPath();
+ QFileInfo info(path);
+ assert(info.exists());
+ assert(info.isFile());
+ ArchiveHeader header(QString("%1/%2").arg(kitName, sampleName),
+ info.size());
+ archiveWriter.writeHeader(header);
+ QFile file(path);
+ if (! file.open(QFile::ReadOnly)) {
+ QString message = tr("could not open '%1': %2").
+ arg(path, file.errorString());
+ throw synthclone::Error(message);
+ }
+ for (;;) {
+ QByteArray data = file.read(8192);
+ if (data.isEmpty()) {
+ break;
+ }
+ archiveWriter.writeData(data);
+ }

confWriter.writeStartElement("layer");
writeElement(confWriter, "filename", sampleName);
=======================================
--- /src/synthclone/session.cpp Mon Feb 18 21:45:49 2013
+++ /src/synthclone/session.cpp Sun Mar 3 00:22:20 2013
@@ -769,6 +769,7 @@
Zone *zone = qobject_cast<EffectJob *>(currentEffectJob)->getZone();
zone->setStatus(synthclone::Zone::STATUS_NORMAL);
zone->setWetSample(currentEffectJobWetSample, false);
+ assert(currentEffectJobWetSample == zone->getWetSample());
recycleCurrentEffectJob();
}

@@ -806,6 +807,7 @@
if (zones.contains(zone)) {
zone->setStatus(synthclone::Zone::STATUS_NORMAL);
zone->setDrySample(currentSamplerJobSample, false);
+ assert(currentSamplerJobSample == zone->getDrySample());
currentSamplerJobSample = 0;
}
recycleCurrentSamplerJob();
@@ -951,7 +953,6 @@
void
Session::load(const QDir &directory)
{
-
QDomDocument document;
if (! loadXML(directory, document)) {
throw synthclone::Error(tr("'%1' is not a valid session
directory").
=======================================
--- /src/synthclone/sessionsampledata.cpp Mon Feb 18 21:45:49 2013
+++ /src/synthclone/sessionsampledata.cpp Sun Mar 3 00:22:20 2013
@@ -101,7 +101,8 @@
}

synthclone::Sample *
-SessionSampleData::updateSample(synthclone::Sample &sample, QObject
*parent)
+SessionSampleData::updateSample(synthclone::Sample &sample, bool forceCopy,
+ QObject *parent)
{
if (! sampleDirectory) {
// The session is being unloaded.
@@ -124,11 +125,17 @@
return 0;
}

+ // If the sample rate isn't set, then set it to the sample rate of the
new
+ // sample.
synthclone::SampleRate inputSampleRate = inputStream.getSampleRate();
+ if (sampleRate == synthclone::SAMPLE_RATE_NOT_SET) {
+ setSampleRate(inputSampleRate);
+ }
+
bool sampleConversionRequired = inputSampleRate != sampleRate;
QString newPath = createUniqueFile(sampleDirectory);
if ((channelConvertAlgorithm == CHANNELCONVERTALGORITHM_NONE) &&
- (! sampleConversionRequired)) {
+ (! sampleConversionRequired) && (! forceCopy)) {
if (sampleDirectory) {
if (QFileInfo(sample.getPath()).absolutePath() ==
sampleDirectory->absolutePath()) {
@@ -136,11 +143,11 @@
return &sample;
}
}
- // We just need to move the sample to the new sample directory.
- return new synthclone::Sample(sample, newPath, parent);
}

- // At this point, some sort of conversion is required.
+ // At this point, either some sort of conversion is required, the
sample is
+ // being moved from outside the sample directory into the sample
directory,
+ // or a forced copy was requested.

float *channelBuffer;

=======================================
--- /src/synthclone/sessionsampledata.h Tue Sep 20 20:21:35 2011
+++ /src/synthclone/sessionsampledata.h Sun Mar 3 00:22:20 2013
@@ -57,7 +57,8 @@
setSampleRate(synthclone::SampleRate sampleRate);

synthclone::Sample *
- updateSample(synthclone::Sample &sample, QObject *parent=0);
+ updateSample(synthclone::Sample &sample, bool forceCopy=false,
+ QObject *parent=0);

signals:

=======================================
--- /src/synthclone/util.cpp Thu Dec 20 22:19:11 2012
+++ /src/synthclone/util.cpp Sun Mar 3 00:22:20 2013
@@ -161,9 +161,8 @@
QString name = sampleInfo.fileName();
if (sampleInfo.absolutePath() !=
samplesDirectory->absolutePath()) {
QString path = samplesDirectory->absoluteFilePath(name);
- synthclone::Sample *newDrySample =
- new synthclone::Sample(*drySample, path, parent);
- zone->setDrySample(newDrySample);
+ synthclone::Sample newDrySample(*drySample, path, parent);
+ zone->setDrySample(&newDrySample);
}
writer.writeAttribute("dry-sample", name);
} else {
@@ -180,9 +179,8 @@
QString name = sampleInfo.fileName();
if (sampleInfo.absolutePath() !=
samplesDirectory->absolutePath()) {
QString path = samplesDirectory->absoluteFilePath(name);
- synthclone::Sample *newWetSample =
- new synthclone::Sample(*wetSample, path, parent);
- zone->setWetSample(newWetSample);
+ synthclone::Sample newWetSample(*wetSample, path, parent);
+ zone->setWetSample(&newWetSample);
}
writer.writeAttribute("wet-sample", name);
} else {
=======================================
--- /src/synthclone/zone.cpp Mon Feb 18 21:45:49 2013
+++ /src/synthclone/zone.cpp Sun Mar 3 00:22:20 2013
@@ -141,18 +141,11 @@
void
Zone::handleSessionSampleDataChange()
{
- synthclone::Sample *sample;
if (drySample) {
- sample = sessionSampleData.updateSample(*drySample, this);
- if (sample != drySample) {
- setDrySample(sample, false);
- }
+ setDrySample(drySample, false);
}
if (wetSample) {
- sample = sessionSampleData.updateSample(*wetSample, this);
- if (sample != wetSample) {
- setWetSample(sample, false);
- }
+ setWetSample(wetSample, false);
}
}

@@ -242,21 +235,17 @@
}

void
-Zone::setDrySample(synthclone::Sample *sample, bool copy)
+Zone::setDrySample(synthclone::Sample *sample, bool forceCopy)
{
- if (sample && copy) {
- QString path =
createUniqueFile(sessionSampleData.getSampleDirectory());
- sample = new synthclone::Sample(*sample, path, false, this);
+ if (sample) {
+ sample = sessionSampleData.updateSample(*sample, forceCopy, this);
+ sample->setTemporary(false);
}
if (this->drySample != sample) {
if (this->drySample) {
delete this->drySample;
}
this->drySample = sample;
- if (sample) {
- updateSampleRate(*sample);
- sample->setTemporary(false);
- }
emit drySampleChanged(sample);
setDrySampleStale(! static_cast<bool>(sample));
}
@@ -341,23 +330,19 @@
}

void
-Zone::setWetSample(synthclone::Sample *sample, bool copy)
+Zone::setWetSample(synthclone::Sample *sample, bool forceCopy)
{
- if (sample && copy) {
- QString path =
createUniqueFile(sessionSampleData.getSampleDirectory());
- sample = new synthclone::Sample(*sample, path, false, this);
+ if (sample) {
+ sample = sessionSampleData.updateSample(*sample, forceCopy, this);
+ sample->setTemporary(false);
}
if (this->wetSample != sample) {
if (this->wetSample) {
delete this->wetSample;
}
this->wetSample = sample;
+ emit drySampleChanged(sample);
if (sample) {
- updateSampleRate(*sample);
- sample->setTemporary(false);
- }
- emit wetSampleChanged(sample);
- if (wetSample) {
if (! drySampleStale) {
setWetSampleStale(false);
}
=======================================
--- /src/synthclone/zone.h Mon Feb 18 21:45:49 2013
+++ /src/synthclone/zone.h Sun Mar 3 00:22:20 2013
@@ -97,7 +97,7 @@
setDrySample(synthclone::Sample *sample);

void
- setDrySample(synthclone::Sample *sample, bool copy);
+ setDrySample(synthclone::Sample *sample, bool forceCopy);

void
setDrySampleStale(bool stale);
@@ -118,7 +118,7 @@
setVelocity(synthclone::MIDIData velocity);

void
- setWetSample(synthclone::Sample *sample, bool copy=true);
+ setWetSample(synthclone::Sample *sample, bool forceCopy=true);

void
setWetSampleStale();
=======================================
--- /src/synthclone/zonelistloader.cpp Fri Nov 25 14:00:45 2011
+++ /src/synthclone/zonelistloader.cpp Sun Mar 3 00:22:20 2013
@@ -88,6 +88,7 @@
if (sample) {
try {
zone->setDrySample(sample, false);
+ assert(sample == zone->getDrySample());
} catch (synthclone::Error &e) {
emitWarning(element, e.getMessage());
}
@@ -96,6 +97,7 @@
if (sample) {
try {
zone->setWetSample(sample, false);
+ assert(sample == zone->getWetSample());
} catch (synthclone::Error &e) {
emitWarning(element, e.getMessage());
}
Reply all
Reply to author
Forward
0 new messages