diff options
author | Bent Bisballe Nyeng <deva@aasimon.org> | 2014-09-08 20:48:38 +0200 |
---|---|---|
committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2014-09-08 20:48:38 +0200 |
commit | 7776aa97fc1c69a8f28ba4bab7387fb96be9a6e4 (patch) | |
tree | 8e23cfbfe7e93bdfad4421ccb0ec8c280a0b43ed /src/audioextractor.cc | |
parent | 56f5965ffa2538dfa482ae9460e9579b4a04f8fb (diff) |
Import dgedit from drumgizmo repository (split into its own project).
Diffstat (limited to 'src/audioextractor.cc')
-rw-r--r-- | src/audioextractor.cc | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/audioextractor.cc b/src/audioextractor.cc new file mode 100644 index 0000000..71e0b2a --- /dev/null +++ b/src/audioextractor.cc @@ -0,0 +1,283 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * audioextractor.cc + * + * Sat Nov 21 13:09:35 CET 2009 + * Copyright 2009 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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. + * + * DrumGizmo 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 DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "audioextractor.h" + +#include <QDomDocument> +#include <QFile> +#include <QDir> +#include <QApplication> + +#include <sndfile.h> + +#define INSTRUMENT_VERSION "2.0" + +typedef struct { + SNDFILE *fh; + float *data; +} audiodata_t; + +AudioExtractor::AudioExtractor(Selections &s, QObject *parent) + : QObject(parent), selections(s) +{ +} + +void AudioExtractor::exportSelections() +{ + emit setMaximumProgress(selections.ids().size() + 1/* for xml writing*/); + int progress = 0; + emit progressUpdate(progress++); + qApp->processEvents(); + + // Open all input audio files: + audiodata_t audiodata[audiofiles.size()]; + + int idx = 0; + AudioFileList::iterator j = audiofiles.begin(); + while(j != audiofiles.end()) { + QString file = j->first; + + SF_INFO sf_info; + audiodata[idx].fh = sf_open(file.toStdString().c_str(), SFM_READ, &sf_info); + if(!audiodata[idx].fh) { + printf("Load error '%s'\n", file.toStdString().c_str()); + return; + } + + audiodata[idx].data = NULL; + + j++; + idx++; + } + + idx = 1; + QVector<sel_id_t> sels = selections.ids(); + + // Sort selections by velocity + for(int v1 = 0; v1 < sels.size(); v1++) { + for(int v2 = 0; v2 < sels.size(); v2++) { + + Selection sel1 = selections.get(sels[v1]); + Selection sel2 = selections.get(sels[v2]); + + if(sel1.energy < sel2.energy) { + sel_id_t vtmp = sels[v1]; + sels[v1] = sels[v2]; + sels[v2] = vtmp; + } + } + } + + // Iterate and write audio files + QVector<sel_id_t>::iterator si = sels.begin(); + while(si != sels.end()) { + Selection sel = selections.get(*si); + size_t offset = sel.from; + size_t size = sel.to - sel.from; + size_t fadein = sel.fadein; + size_t fadeout = sel.fadeout; + + + // Read all input audio file chunks: + for(int i = 0; i < audiofiles.size(); i++) { + + // Clear out old buffer (if one exists) + if(audiodata[i].data) { + delete audiodata[i].data; + audiodata[i].data = NULL; + } + + SNDFILE *fh = audiodata[i].fh; + + sf_seek(fh, offset, SEEK_SET); + + float *data = new float[size]; + sf_read_float(fh, data, size); + + // Apply linear fadein + for(size_t fi = 0; fi < fadein; fi++) { + float val = ((float)fi / (float)fadein); + if(fi < size) data[fi] *= val; + } + + // Apply fadeout + for(size_t fo = 0; fo < fadeout; fo++) { + float val = 1.0 - ((float)fo / (float)fadeout); + if( (((size - fadeout) + fo) < size) && + (((size - fadeout) + fo) >= 0) ) { + data[(size - fadeout) + fo] *= val; + } + } + + audiodata[i].data = data; + } + + // Create output path: + QString path = exportpath + "/" + prefix + "/samples"; + QDir d; + d.mkpath(path); + + // Write all sample chunks to single output file: + QString file = path + "/" + QString::number(idx) + "-" + prefix + ".wav"; + + SF_INFO sf_info; + sf_info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; + sf_info.samplerate = 44100; + sf_info.channels = audiofiles.size(); + + SNDFILE *ofh = sf_open(file.toStdString().c_str(), SFM_WRITE, &sf_info); + if(!ofh) { + printf("Open for write error...\n"); + return; + } + + for(size_t ob = 0; ob < size; ob++) { + float obuf[audiofiles.size()]; + for(int ai = 0; ai < audiofiles.size(); ai++) { + obuf[ai] = audiodata[ai].data[ob]; + } + sf_write_float(ofh, obuf, audiofiles.size()); + } + sf_close(ofh); + + idx++; + si++; + + emit progressUpdate(progress++); + qApp->processEvents(); + } + + // Close all input audio files: + for(int i = 0; i < audiofiles.size(); i++) { + if(audiodata[i].data) { + delete audiodata[i].data; + audiodata[i].data = NULL; + } + + sf_close(audiodata[i].fh); + } + + QDomDocument doc; + QDomProcessingInstruction header = + doc.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'"); + doc.appendChild(header); + + QDomElement instrument = doc.createElement("instrument"); + instrument.setAttribute("version", INSTRUMENT_VERSION); + instrument.setAttribute("name", prefix); + doc.appendChild(instrument); + + QDomElement samples = doc.createElement("samples"); + instrument.appendChild(samples); + + { + // Do the adding to the xml file one sample at the time. + int index = 0; + QVector<sel_id_t>::iterator si = sels.begin(); + while(si != sels.end()) { + index++; + + Selection i = selections.get(*si); + i.name = prefix + "-" + QString::number(index); + + QDomElement sample = doc.createElement("sample"); + sample.setAttribute("name", i.name); + sample.setAttribute("power", QString::number(i.energy)); + samples.appendChild(sample); + + selections.update(*si, i); + + int channelnum = 1; // Filechannel numbers are 1-based. + AudioFileList::iterator j = audiofiles.begin(); + while(j != audiofiles.end()) { + + QString file = j->first; + QString name = j->second; + + QDomElement audiofile = doc.createElement("audiofile"); + audiofile.setAttribute("file", "samples/" + + QString::number(index) + "-" + prefix + ".wav"); + audiofile.setAttribute("channel", name); + audiofile.setAttribute("filechannel", QString::number(channelnum)); + sample.appendChild(audiofile); + channelnum++; + j++; + } + + si++; + } + } + + QFile xmlfile(exportpath + "/" + prefix + "/" + prefix + ".xml"); + xmlfile.open(QIODevice::WriteOnly); + xmlfile.write(doc.toByteArray()); + xmlfile.close(); + + emit progressUpdate(progress++); + qApp->processEvents(); +} + +void AudioExtractor::addFile(QString file, QString name) +{ + QPair<QString, QString> pair; + pair.first = file; + pair.second = name; + audiofiles.push_back(pair); +} + +void AudioExtractor::removeFile(QString file, QString name) +{ + AudioFileList::iterator j = audiofiles.begin(); + while(j != audiofiles.end()) { + if(file == j->first/* && name == j->second*/) { + audiofiles.erase(j); + return; + } + j++; + } +} + +void AudioExtractor::setOutputPrefix(const QString &p) +{ + prefix = p; +} + +void AudioExtractor::setExportPath(const QString &path) +{ + exportpath = path; +} + +void AudioExtractor::changeName(QString file, QString name) +{ + AudioFileList::iterator j = audiofiles.begin(); + while(j != audiofiles.end()) { + if(file == j->first) { + j->second = name; + return; + } + j++; + } +} |