summaryrefslogtreecommitdiff
path: root/muse2/muse/wave.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/wave.cpp')
-rw-r--r--muse2/muse/wave.cpp1175
1 files changed, 1175 insertions, 0 deletions
diff --git a/muse2/muse/wave.cpp b/muse2/muse/wave.cpp
new file mode 100644
index 00000000..761df539
--- /dev/null
+++ b/muse2/muse/wave.cpp
@@ -0,0 +1,1175 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: wave.cpp,v 1.19.2.20 2009/12/20 05:00:35 terminator356 Exp $
+//
+// (C) Copyright 2000-2004 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <qfileinfo.h>
+#include <cmath>
+#include <qdatetime.h>
+#include <qmessagebox.h>
+#include <q3progressdialog.h>
+
+#include "xml.h"
+#include "song.h"
+#include "wave.h"
+#include "app.h"
+#include "filedialog.h"
+#include "arranger/arranger.h"
+#include "globals.h"
+#include "event.h"
+#include "audio.h"
+#include "sig.h"
+
+//#define WAVE_DEBUG
+//#define WAVE_DEBUG_PRC
+
+/*
+const char* audioFilePattern[] = {
+ "Wave/Binary (*.wav *.ogg *.bin)",
+ "Wave (*.wav *.ogg)",
+ "Binary (*.bin)",
+ "All Files (*)",
+ 0
+ };
+*/
+const int cacheMag = 128;
+
+// ClipList* waveClips;
+
+SndFileList SndFile::sndFiles;
+
+//---------------------------------------------------------
+// SndFile
+//---------------------------------------------------------
+
+SndFile::SndFile(const QString& name)
+ {
+ finfo = new QFileInfo(name);
+ sf = 0;
+ sfUI = 0;
+ csize = 0;
+ cache = 0;
+ openFlag = false;
+ sndFiles.push_back(this);
+ refCount=0;
+ }
+
+SndFile::~SndFile()
+ {
+ if (openFlag)
+ close();
+ for (iSndFile i = sndFiles.begin(); i != sndFiles.end(); ++i) {
+ if (*i == this) {
+ sndFiles.erase(i);
+ break;
+ }
+ }
+ delete finfo;
+ if (cache) {
+ for (unsigned i = 0; i < channels(); ++i)
+ delete cache[i];
+ delete[] cache;
+ cache = 0;
+ }
+ }
+
+//---------------------------------------------------------
+// openRead
+//---------------------------------------------------------
+
+bool SndFile::openRead()
+ {
+ if (openFlag) {
+ printf("SndFile:: alread open\n");
+ return false;
+ }
+ QString p = path();
+ sfinfo.format = 0;
+ sf = sf_open(p.latin1(), SFM_READ, &sfinfo);
+ sfinfo.format = 0;
+ sfUI = sf_open(p.latin1(), SFM_READ, &sfinfo);
+ if (sf == 0 || sfUI == 0)
+ return true;
+
+ writeFlag = false;
+ openFlag = true;
+ QString cacheName = finfo->dirPath(true) + QString("/") + finfo->baseName(true) + QString(".wca");
+ readCache(cacheName, true);
+ return false;
+ }
+
+//---------------------------------------------------------
+// update
+// called after recording to file
+//---------------------------------------------------------
+
+void SndFile::update()
+ {
+ close();
+
+ // force recreation of wca data
+ QString cacheName = finfo->dirPath(true) +
+ QString("/") + finfo->baseName(true) + QString(".wca");
+ ::remove(cacheName.latin1());
+ if (openRead()) {
+ printf("SndFile::update openRead(%s) failed: %s\n", path().latin1(), strerror().latin1());
+ }
+ }
+
+//---------------------------------------------------------
+// readCache
+//---------------------------------------------------------
+
+void SndFile::readCache(const QString& path, bool showProgress)
+ {
+// printf("readCache %s for %d samples channel %d\n",
+// path.latin1(), samples(), channels());
+
+ if (cache) {
+ for (unsigned i = 0; i < channels(); ++i)
+ delete cache[i];
+ delete[] cache;
+ }
+ if (samples() == 0) {
+// printf("SndFile::readCache: file empty\n");
+ return;
+ }
+ csize = (samples() + cacheMag - 1)/cacheMag;
+ cache = new SampleV*[channels()];
+ for (unsigned ch = 0; ch < channels(); ++ch)
+ cache[ch] = new SampleV[csize];
+
+ FILE* cfile = fopen(path.latin1(), "r");
+ if (cfile) {
+ for (unsigned ch = 0; ch < channels(); ++ch)
+ fread(cache[ch], csize * sizeof(SampleV), 1, cfile);
+ fclose(cfile);
+ return;
+ }
+
+ //---------------------------------------------------
+ // create cache
+ //---------------------------------------------------
+
+ Q3ProgressDialog* progress = 0;
+ if (showProgress) {
+ QString label(QWidget::tr("create peakfile for "));
+ label += basename();
+ progress = new Q3ProgressDialog(label,
+ QString::null, csize, 0, 0, true);
+ progress->setMinimumDuration(0);
+ progress->show();
+ }
+ float data[channels()][cacheMag];
+ float* fp[channels()];
+ for (unsigned k = 0; k < channels(); ++k)
+ fp[k] = &data[k][0];
+ int interval = csize / 10;
+
+ if(!interval)
+ interval = 1;
+ for (int i = 0; i < csize; i++) {
+ if (showProgress && ((i % interval) == 0))
+ progress->setProgress(i);
+ seek(i * cacheMag, 0);
+ read(channels(), fp, cacheMag);
+ for (unsigned ch = 0; ch < channels(); ++ch) {
+ float rms = 0.0;
+ cache[ch][i].peak = 0;
+ for (int n = 0; n < cacheMag; n++) {
+ float fd = data[ch][n];
+ rms += fd * fd;
+ int idata = int(fd * 255.0);
+ if (idata < 0)
+ idata = -idata;
+ if (cache[ch][i].peak < idata)
+ cache[ch][i].peak = idata;
+ }
+ // amplify rms value +12dB
+ int rmsValue = int((sqrt(rms/cacheMag) * 255.0));
+ if (rmsValue > 255)
+ rmsValue = 255;
+ cache[ch][i].rms = rmsValue;
+ }
+ }
+ if (showProgress)
+ progress->setProgress(csize);
+ writeCache(path);
+ if (showProgress)
+ delete progress;
+ }
+
+//---------------------------------------------------------
+// writeCache
+//---------------------------------------------------------
+
+void SndFile::writeCache(const QString& path)
+ {
+ FILE* cfile = fopen(path.latin1(), "w");
+ if (cfile == 0)
+ return;
+ for (unsigned ch = 0; ch < channels(); ++ch)
+ fwrite(cache[ch], csize * sizeof(SampleV), 1, cfile);
+ fclose(cfile);
+ }
+
+//---------------------------------------------------------
+// read
+//---------------------------------------------------------
+
+void SndFile::read(SampleV* s, int mag, unsigned pos, bool overwrite)
+ {
+ if(overwrite)
+ for (unsigned ch = 0; ch < channels(); ++ch) {
+ s[ch].peak = 0;
+ s[ch].rms = 0;
+ }
+
+ if (pos > samples()) {
+// printf("%p pos %d > samples %d\n", this, pos, samples());
+ return;
+ }
+
+ if (mag < cacheMag) {
+ float data[channels()][mag];
+ float* fp[channels()];
+ for (unsigned i = 0; i < channels(); ++i)
+ fp[i] = &data[i][0];
+
+ sf_count_t ret = 0;
+ if(sfUI)
+ ret = sf_seek(sfUI, pos, SEEK_SET);
+ else
+ ret = sf_seek(sf, pos, SEEK_SET);
+ if(ret == -1)
+ return;
+ {
+ int srcChannels = channels();
+ int dstChannels = sfinfo.channels;
+ size_t n = mag;
+ float** dst = fp;
+ float buffer[n * dstChannels];
+
+ size_t rn = 0;
+ if(sfUI)
+ rn = sf_readf_float(sfUI, buffer, n);
+ else
+ rn = sf_readf_float(sf, buffer, n);
+ if(rn != n)
+ return;
+ float* src = buffer;
+
+ if (srcChannels == dstChannels) {
+ for (size_t i = 0; i < rn; ++i) {
+ for (int ch = 0; ch < srcChannels; ++ch)
+ *(dst[ch]+i) = *src++;
+ }
+ }
+ else if ((srcChannels == 1) && (dstChannels == 2)) {
+ // stereo to mono
+ for (size_t i = 0; i < rn; ++i)
+ *(dst[0] + i) = src[i + i] + src[i + i + 1];
+ }
+ else if ((srcChannels == 2) && (dstChannels == 1)) {
+ // mono to stereo
+ for (size_t i = 0; i < rn; ++i) {
+ float data = *src++;
+ *(dst[0]+i) = data;
+ *(dst[1]+i) = data;
+ }
+ }
+ }
+
+ for (unsigned ch = 0; ch < channels(); ++ch) {
+
+ if(overwrite)
+ s[ch].peak = 0;
+
+ float rms = 0.0;
+ for (int i = 0; i < mag; i++) {
+ float fd = data[ch][i];
+ rms += fd;
+ int idata = int(fd * 255.0);
+ if (idata < 0)
+ idata = -idata;
+ if (s[ch].peak < idata)
+ s[ch].peak = idata;
+ }
+
+ s[ch].rms = 0; // TODO rms / mag;
+ }
+ }
+ else {
+ mag /= cacheMag;
+ int rest = csize - (pos/cacheMag);
+ int end = mag;
+ if (rest < mag)
+ end = rest;
+
+ for (unsigned ch = 0; ch < channels(); ++ch) {
+ int rms = 0;
+ int off = pos/cacheMag;
+ for (int offset = off; offset < off+end; offset++) {
+ rms += cache[ch][offset].rms;
+ if (s[ch].peak < cache[ch][offset].peak)
+ s[ch].peak = cache[ch][offset].peak;
+ }
+
+ if(overwrite)
+ s[ch].rms = rms / mag;
+
+ else
+ s[ch].rms += rms / mag;
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// openWrite
+//---------------------------------------------------------
+
+bool SndFile::openWrite()
+ {
+ if (openFlag) {
+ printf("SndFile:: alread open\n");
+ return false;
+ }
+ QString p = path();
+ sf = sf_open(p.latin1(), SFM_RDWR, &sfinfo);
+ sfUI = 0;
+ if (sf) {
+ openFlag = true;
+ writeFlag = true;
+ QString cacheName = finfo->dirPath(true) +
+ QString("/") + finfo->baseName(true) + QString(".wca");
+ readCache(cacheName, true);
+ }
+ return sf == 0;
+ }
+
+//---------------------------------------------------------
+// close
+//---------------------------------------------------------
+
+void SndFile::close()
+ {
+ if (!openFlag) {
+ printf("SndFile:: alread closed\n");
+ return;
+ }
+ sf_close(sf);
+ if (sfUI)
+ sf_close(sfUI);
+ openFlag = false;
+ }
+
+//---------------------------------------------------------
+// remove
+//---------------------------------------------------------
+
+void SndFile::remove()
+ {
+ if (openFlag)
+ close();
+ QFile::remove(finfo->filePath());
+ }
+
+QString SndFile::basename() const
+ {
+ return finfo->baseName(true);
+ }
+
+QString SndFile::path() const
+ {
+ return finfo->filePath();
+ }
+
+QString SndFile::dirPath() const
+ {
+ return finfo->dirPath(true);
+ }
+
+QString SndFile::name() const
+ {
+ return finfo->fileName();
+ }
+
+//---------------------------------------------------------
+// samples
+//---------------------------------------------------------
+
+unsigned SndFile::samples() const
+ {
+ if (!writeFlag) // if file is read only sfinfo is reliable
+ return sfinfo.frames;
+ sf_count_t curPos = sf_seek(sf, 0, SEEK_CUR);
+ int frames = sf_seek(sf, 0, SEEK_END);
+ sf_seek(sf, curPos, SEEK_SET);
+ return frames;
+ }
+
+//---------------------------------------------------------
+// channels
+//---------------------------------------------------------
+
+unsigned SndFile::channels() const
+ {
+ return sfinfo.channels;
+ }
+
+unsigned SndFile::samplerate() const
+ {
+ return sfinfo.samplerate;
+ }
+
+unsigned SndFile::format() const
+ {
+ return sfinfo.format;
+ }
+
+void SndFile::setFormat(int fmt, int ch, int rate)
+ {
+ sfinfo.samplerate = rate;
+ sfinfo.channels = ch;
+ sfinfo.format = fmt;
+ sfinfo.seekable = true;
+ sfinfo.frames = 0;
+ }
+
+//---------------------------------------------------------
+// readWithHeap
+// not as realtime friendly but can retrieve bigger data
+//---------------------------------------------------------
+size_t SndFile::readWithHeap(int srcChannels, float** dst, size_t n, bool overwrite)
+ {
+ float *buffer = new float[n * sfinfo.channels];
+ int rn = readInternal(srcChannels,dst,n,overwrite, buffer);
+ delete buffer;
+ return rn;
+ }
+
+//---------------------------------------------------------
+// read
+//---------------------------------------------------------
+size_t SndFile::read(int srcChannels, float** dst, size_t n, bool overwrite)
+ {
+ float buffer[n * sfinfo.channels];
+ int rn = readInternal(srcChannels,dst,n,overwrite, buffer);
+ return rn;
+ }
+
+size_t SndFile::readInternal(int srcChannels, float** dst, size_t n, bool overwrite, float *buffer)
+{
+ size_t rn = sf_readf_float(sf, buffer, n);
+
+ float* src = buffer;
+ int dstChannels = sfinfo.channels;
+ if (srcChannels == dstChannels) {
+ if(overwrite)
+ for (size_t i = 0; i < rn; ++i) {
+ for (int ch = 0; ch < srcChannels; ++ch)
+ *(dst[ch]+i) = *src++;
+ }
+ else
+ for (size_t i = 0; i < rn; ++i) {
+ for (int ch = 0; ch < srcChannels; ++ch)
+ *(dst[ch]+i) += *src++;
+ }
+ }
+ else if ((srcChannels == 1) && (dstChannels == 2)) {
+ // stereo to mono
+ if(overwrite)
+ for (size_t i = 0; i < rn; ++i)
+ *(dst[0] + i) = src[i + i] + src[i + i + 1];
+ else
+ for (size_t i = 0; i < rn; ++i)
+ *(dst[0] + i) += src[i + i] + src[i + i + 1];
+ }
+ else if ((srcChannels == 2) && (dstChannels == 1)) {
+ // mono to stereo
+ if(overwrite)
+ for (size_t i = 0; i < rn; ++i) {
+ float data = *src++;
+ *(dst[0]+i) = data;
+ *(dst[1]+i) = data;
+ }
+ else
+ for (size_t i = 0; i < rn; ++i) {
+ float data = *src++;
+ *(dst[0]+i) += data;
+ *(dst[1]+i) += data;
+ }
+ }
+ else {
+ printf("SndFile:read channel mismatch %d -> %d\n",
+ srcChannels, dstChannels);
+ }
+
+ return rn;
+
+}
+
+
+//---------------------------------------------------------
+// write
+//
+// A hardcoded limiter was added that limits the output at 0.99/-0.99
+// libsndfile handles signal betwee -1.0/1.0 with current setting
+// outside these values there will be heavy distortion
+//
+//---------------------------------------------------------
+
+size_t SndFile::write(int srcChannels, float** src, size_t n)
+ {
+ int dstChannels = sfinfo.channels;
+ //float buffer[n * dstChannels];
+ float *buffer = new float[n * dstChannels];
+ float *dst = buffer;
+
+ const float limitValue=0.9999;
+
+
+ if (srcChannels == dstChannels) {
+ for (size_t i = 0; i < n; ++i) {
+ for (int ch = 0; ch < dstChannels; ++ch)
+ //*dst++ = *(src[ch]+i); // < limitValue ? *(src[ch]+i) : limitValue;
+ if (*(src[ch]+i) > 0)
+ *dst++ = *(src[ch]+i) < limitValue ? *(src[ch]+i) : limitValue;
+ else
+ *dst++ = *(src[ch]+i) > -limitValue ? *(src[ch]+i) : -limitValue;
+ }
+ }
+ else if ((srcChannels == 1) && (dstChannels == 2)) {
+ // mono to stereo
+ for (size_t i = 0; i < n; ++i) {
+ float data = *(src[0]+i);
+ if (data > 0) {
+ *dst++ = data < limitValue ? data : limitValue;
+ *dst++ = data < limitValue ? data : limitValue;
+ }
+ else {
+ *dst++ = data > -limitValue ? data : -limitValue;
+ *dst++ = data > -limitValue ? data : -limitValue;
+ }
+ }
+ }
+ else if ((srcChannels == 2) && (dstChannels == 1)) {
+ // stereo to mono
+ for (size_t i = 0; i < n; ++i)
+ if (*(src[0]+i) + *(src[1]+i) > 0)
+ *dst++ = (*(src[0]+i) + *(src[1]+i)) < limitValue ? (*(src[0]+i) + *(src[1]+i)) : limitValue;
+ else
+ *dst++ = (*(src[0]+i) + *(src[1]+i)) > -limitValue ? (*(src[0]+i) + *(src[1]+i)) : -limitValue;
+ }
+ else {
+ printf("SndFile:write channel mismatch %d -> %d\n",
+ srcChannels, dstChannels);
+ delete buffer;
+ return 0;
+ }
+ int nbr = sf_writef_float(sf, buffer, n) ;
+ delete buffer;
+ return nbr;
+ }
+
+//---------------------------------------------------------
+// seek
+//---------------------------------------------------------
+
+off_t SndFile::seek(off_t frames, int whence)
+ {
+ return sf_seek(sf, frames, whence);
+ }
+
+//---------------------------------------------------------
+// strerror
+//---------------------------------------------------------
+
+QString SndFile::strerror() const
+ {
+ char buffer[128];
+ buffer[0] = 0;
+ sf_error_str(sf, buffer, 128);
+ return QString(buffer);
+ }
+
+//---------------------------------------------------------
+// search
+//---------------------------------------------------------
+
+SndFile* SndFileList::search(const QString& name)
+ {
+ for (iSndFile i = begin(); i != end(); ++i) {
+ if ((*i)->path() == name)
+ return *i;
+ }
+ return 0;
+ }
+
+//---------------------------------------------------------
+// getSnd
+//---------------------------------------------------------
+
+SndFile* getWave(const QString& inName, bool readOnlyFlag)
+ {
+ QString name = inName;
+
+ if (QFileInfo(name).isRelative()) {
+ name = museProject + QString("/") + name;
+ }
+ else {
+ if (!QFile::exists(name)) {
+ if (QFile::exists(museProject + QString("/") + name)) {
+ name = museProject + QString("/") + name;
+ }
+ }
+ }
+// printf("=====%s %s\n", inName.latin1(), name.latin1());
+
+ // only open one instance of wave file
+ SndFile* f = SndFile::sndFiles.search(name);
+ if (f == 0) {
+ if (!QFile::exists(name)) {
+ fprintf(stderr, "wave file <%s> not found\n",
+ name.latin1());
+ return 0;
+ }
+ f = new SndFile(name);
+ bool error;
+ if (readOnlyFlag)
+ error = f->openRead();
+ else {
+ error = f->openWrite();
+ // if peak cache is older than wave file we reaquire the cache
+ QFileInfo wavinfo(name);
+ QString cacheName = wavinfo.dirPath(true) + QString("/") + wavinfo.baseName(true) + QString(".wca");
+ QFileInfo wcainfo(cacheName);
+ if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) {
+ //printf("wcafile is older or does not exist!\n");
+ QFile(cacheName).remove();
+ f->readCache(cacheName,true);
+ }
+
+ }
+ if (error) {
+ fprintf(stderr, "open wave file(%s) for %s failed: %s\n",
+ name.latin1(),
+ readOnlyFlag ? "writing" : "reading",
+ f->strerror().latin1());
+ QMessageBox::critical(NULL, "MusE import error.",
+ "MusE failed to import the file.\n"
+ "Possibly this wasn't a sound file?\n"
+ "If it was check the permissions, MusE\n"
+ "sometimes requires write access to the file.");
+
+ delete f;
+ f = 0;
+ }
+ }
+ else {
+ if (!readOnlyFlag && ! f->isWritable()) {
+ if (f->isOpen())
+ f->close();
+ f->openWrite();
+ }
+ else {
+ // if peak cache is older than wave file we reaquire the cache
+ QFileInfo wavinfo(name);
+ QString cacheName = wavinfo.dirPath(true) + QString("/") + wavinfo.baseName(true) + QString(".wca");
+ QFileInfo wcainfo(cacheName);
+ if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) {
+ //printf("wcafile is older or does not exist!\n");
+ QFile(cacheName).remove();
+ f->readCache(cacheName,true);
+ }
+
+ }
+ }
+ return f;
+ }
+
+//---------------------------------------------------------
+// applyUndoFile
+//---------------------------------------------------------
+void SndFile::applyUndoFile(const QString& original, const QString& tmpfile, unsigned startframe, unsigned endframe)
+ {
+ // This one is called on both undo and redo of a wavfile
+ // For redo to be called, undo must have been called first, and we don't store both the original data and the modified data in separate
+ // files. Thus, each time this function is called the data in the "original"-file will be written to the tmpfile, after the data
+ // from the tmpfile has been applied.
+ //
+ // F.ex. if mute has been made on part of a wavfile, the unmuted data is stored in the tmpfile when
+ // the undo operation occurs. The unmuted data is then written back to the original file, and the mute data will be
+ // put in the tmpfile, and when redo is eventually called the data is switched again (causing the muted data to be written to the "original"
+ // file. The data is merely switched.
+
+ //printf("Applying undofile: orig=%s tmpfile=%s startframe=%d endframe=%d\n", original.latin1(), tmpfile.latin1(), startframe, endframe);
+ SndFile* orig = sndFiles.search(original);
+ SndFile tmp = SndFile(tmpfile);
+ if (!orig) {
+ printf("Internal error: could not find original file: %s in filelist - Aborting\n", original.latin1());
+ return;
+ }
+
+ if (!orig->isOpen()) {
+ if (orig->openRead()) {
+ printf("Cannot open original file %s for reading - cannot undo! Aborting\n", original.latin1());
+ return;
+ }
+ }
+
+ if (!tmp.isOpen()) {
+ if (tmp.openRead()) {
+ printf("Could not open temporary file %s for writing - cannot undo! Aborting\n", tmpfile.latin1());
+ return;
+ }
+ }
+
+ audio->msgIdle(true);
+ tmp.setFormat(orig->format(), orig->channels(), orig->samplerate());
+
+ // Read data in original file to memory before applying tmpfile to original
+ unsigned file_channels = orig->channels();
+ unsigned tmpdatalen = endframe - startframe;
+ float* data2beoverwritten[file_channels];
+
+ for (unsigned i=0; i<file_channels; i++) {
+ data2beoverwritten[i] = new float[tmpdatalen];
+ }
+ orig->seek(startframe, 0);
+ orig->readWithHeap(file_channels, data2beoverwritten, tmpdatalen);
+
+ orig->close();
+
+ // Read data from temporary file to memory
+ float* tmpfiledata[file_channels];
+ for (unsigned i=0; i<file_channels; i++) {
+ tmpfiledata[i] = new float[tmpdatalen];
+ }
+ tmp.seek(0, 0);
+ tmp.readWithHeap(file_channels, tmpfiledata, tmpdatalen);
+ tmp.close();
+
+ // Write temporary data to original file:
+ if (orig->openWrite()) {
+ printf("Cannot open orig for write - aborting.\n");
+ return;
+ }
+
+ orig->seek(startframe, 0);
+ orig->write(file_channels, tmpfiledata, tmpdatalen);
+
+ // Delete dataholder for temporary file
+ for (unsigned i=0; i<file_channels; i++) {
+ delete[] tmpfiledata[i];
+ }
+
+ // Write the overwritten data to the tmpfile
+ if (tmp.openWrite()) {
+ printf("Cannot open tmpfile for writing - redo operation of this file won't be possible. Aborting.\n");
+ audio->msgIdle(false);
+ return;
+ }
+ tmp.seek(0, 0);
+ tmp.write(file_channels, data2beoverwritten, tmpdatalen);
+ tmp.close();
+
+ // Delete dataholder for replaced original file
+ for (unsigned i=0; i<file_channels; i++) {
+ delete[] data2beoverwritten[i];
+ }
+
+ orig->close();
+ orig->openRead();
+ orig->update();
+ audio->msgIdle(false);
+ }
+
+//---------------------------------------------------------
+// importAudio
+//---------------------------------------------------------
+
+void MusE::importWave()
+ {
+ Track* track = arranger->curTrack();
+ if (track == 0 || track->type() != Track::WAVE) {
+ QMessageBox::critical(this, QString("MusE"),
+ tr("to import an audio file you have first to select"
+ "a wave track"));
+ return;
+ }
+ //QString fn = getOpenFileName(lastWavePath, audioFilePattern, this,
+ QString fn = getOpenFileName(lastWavePath, audio_file_pattern, this,
+ tr("Import Wave File"), 0);
+ if (!fn.isEmpty()) {
+ lastWavePath = fn;
+ importWaveToTrack(fn);
+ }
+ }
+
+//---------------------------------------------------------
+// importWaveToTrack
+//---------------------------------------------------------
+
+bool MusE::importWaveToTrack(QString& name, unsigned tick, Track* track)
+ {
+ if (track==NULL)
+ track = (WaveTrack*)(arranger->curTrack());
+
+ SndFile* f = getWave(name, true);
+
+ if (f == 0) {
+ printf("import audio file failed\n");
+ return true;
+ }
+ int samples = f->samples();
+ if ((unsigned)sampleRate !=f->samplerate()) {
+ if(QMessageBox::question(this, tr("Import Wavefile"),
+ tr("This wave file has a samplerate of %1,\n"
+ "as opposed to current setting %2.\n"
+ "Do you still want to import it?").arg(f->samplerate()).arg(sampleRate),
+ tr("&Yes"), tr("&No"),
+ QString::null, 0, 1 ))
+ {
+ //printf("why won't muse let me delete the file object? %d\n", f->getRefCount());
+ if (f->getRefCount() == 0)
+ delete f;
+ return true;
+ }
+ }
+ track->setChannels(f->channels());
+
+ WavePart* part = new WavePart((WaveTrack *)track);
+ if (tick)
+ part->setTick(tick);
+ else
+ part->setTick(song->cpos());
+ part->setLenFrame(samples);
+
+ Event event(Wave);
+ SndFileR sf(f);
+ event.setSndFile(sf);
+ event.setSpos(0);
+ event.setLenFrame(samples);
+ part->addEvent(event);
+
+ part->setName(QFileInfo(name).baseName(true));
+ audio->msgAddPart(part);
+ unsigned endTick = part->tick() + part->lenTick();
+ if (song->len() < endTick)
+ song->setLen(endTick);
+ return false;
+ }
+#if 0
+//---------------------------------------------------------
+// Clip
+//---------------------------------------------------------
+
+ClipBase::ClipBase(const SndFileR& file, int start, int l)
+ : f(file)
+ {
+ refCount = 0;
+ for (int i = 1; true; ++i) {
+ _name.sprintf("%s.%d", f.basename().latin1(), i);
+ ciClip ic = waveClips->begin();
+ for (; ic != waveClips->end(); ++ic) {
+ if ((*ic)->name() == _name)
+ break;
+ }
+ if (ic == waveClips->end())
+ break;
+ // try another name
+ }
+ _spos = start;
+ len = l;
+ deleted = false;
+ lrefs = 0;
+ waveClips->add(this);
+ }
+
+//---------------------------------------------------------
+// read
+//---------------------------------------------------------
+
+void ClipBase::read(unsigned srcOffset, float** buffer, int channel, unsigned n)
+ {
+ if (f.isNull())
+ return;
+ f.seek(srcOffset + _spos, 0);
+ f.read(channel, buffer, n);
+ }
+
+ClipBase::~ClipBase()
+ {
+ waveClips->remove(this);
+ }
+
+//---------------------------------------------------------
+// ClipList::write(level, xml)
+//---------------------------------------------------------
+
+void ClipList::write(int level, Xml& xml) const
+ {
+ for (ciClip i = begin(); i != end(); ++i) {
+ ClipBase* clip = *i;
+ // only write visible clips
+ if (clip->references())
+ (*i)->write(level, xml);
+ }
+ }
+
+//---------------------------------------------------------
+// ClipBase::write(level, xml)
+//---------------------------------------------------------
+
+void ClipBase::write(int level, Xml& xml) const
+ {
+ xml.tag(level++, "clip");
+ QString path = f.dirPath();
+
+ //
+ // waves in the project dirctory are stored
+ // with relative path name, others with absolute path
+ //
+ if (path == museProject)
+ xml.strTag(level, "file", f.name());
+ else
+ xml.strTag(level, "file", f.path());
+
+ xml.strTag(level, "name", _name);
+ xml.intTag(level, "tick", _spos);
+ xml.intTag(level, "len", len);
+ xml.etag(level, "clip");
+ }
+
+//---------------------------------------------------------
+// ClipBase::read
+//---------------------------------------------------------
+
+ClipBase* readClip(Xml& xml)
+ {
+ SndFile* f = 0;
+ QString name;
+ unsigned spos = 0;
+ int len = 0;
+
+ for (;;) {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token) {
+ case Xml::Error:
+ case Xml::End:
+ return 0;
+ case Xml::TagStart:
+ if (tag == "file")
+ f = getWave(xml.parse1(), false);
+ else if (tag == "name")
+ name = xml.parse1();
+ else if (tag == "tick")
+ spos = xml.parseInt();
+ else if (tag == "len")
+ len = xml.parseInt();
+ else
+ xml.unknown("Clip");
+ break;
+ case Xml::TagEnd:
+ if (tag == "clip") {
+ if (!f)
+ printf("clip: file not found\n");
+ ClipBase* clip = new ClipBase(f, spos, len);
+ clip->setName(name);
+ return clip;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// search
+//---------------------------------------------------------
+
+Clip ClipList::search(const QString& name) const
+ {
+ for (ciClip i = begin(); i != end(); ++i)
+ if ((*i)->name() == name)
+ return Clip(*i);
+ fprintf(stderr, "ClipList: clip <%s> not found\n",
+ name.latin1());
+ return Clip();
+ }
+
+//---------------------------------------------------------
+// remove
+//---------------------------------------------------------
+
+void ClipList::remove(ClipBase* clip)
+ {
+ for (iClip i = begin(); i != end(); ++i) {
+ if (*i == clip) {
+ erase(i);
+ return;
+ }
+ }
+ printf("ClipList:remove: clip not found\n");
+ }
+
+//---------------------------------------------------------
+// idx
+//---------------------------------------------------------
+
+int ClipList::idx(const Clip& clip) const
+ {
+ int n = 0;
+ for (ciClip i = begin(); i != end(); ++i, ++n) {
+ if (clip == *i)
+ return n;
+ }
+ return -1;
+ }
+#endif
+
+//---------------------------------------------------------
+// cmdAddRecordedWave
+//---------------------------------------------------------
+
+//void Song::cmdAddRecordedWave(WaveTrack* track, const Pos& s, const Pos& e)
+void Song::cmdAddRecordedWave(WaveTrack* track, Pos s, Pos e)
+ {
+ SndFile* f = track->recFile();
+ if (f == 0) {
+ printf("cmdAddRecordedWave: no snd file for track <%s>\n",
+ track->name().latin1());
+ return;
+ }
+
+ // Removed by Tim. p3.3.8
+ //unsigned startTick = roundDownBar(s.tick());
+ //unsigned endTick = roundUpBar(e.tick());
+
+ // Added by Tim. p3.3.8
+
+ if((audio->loopCount() > 0 && s.tick() > lPos().tick()) || (punchin() && s.tick() < lPos().tick()))
+ s.setTick(lPos().tick());
+ // If we are looping, just set the end to the right marker, since we don't know how many loops have occurred.
+ // (Fixed: Added Audio::loopCount)
+ // Otherwise if punchout is on, limit the end to the right marker.
+ //if(loop() || (punchout() && e.tick() > rPos().tick()) )
+ if((audio->loopCount() > 0) || (punchout() && e.tick() > rPos().tick()) )
+ e.setTick(rPos().tick());
+ // No part to be created? Delete the rec sound file.
+ if(s.tick() >= e.tick())
+ {
+ QString s = f->path();
+ delete f;
+ // The function which calls this function already does this immediately after. But do it here anyway.
+ track->setRecFile(0);
+ remove(s.latin1());
+ if(debugMsg)
+ printf("Song::cmdAddRecordedWave: remove file %s\n", s.latin1());
+ return;
+ }
+ // Round the start down using the Arranger part snap raster value.
+ unsigned startTick = sigmap.raster1(s.tick(), song->recRaster());
+ // Round the end up using the Arranger part snap raster value.
+ unsigned endTick = sigmap.raster2(e.tick(), song->recRaster());
+
+ f->update();
+
+ WavePart* part = new WavePart(track);
+ part->setTick(startTick);
+ part->setLenTick(endTick - startTick);
+ part->setName(track->name());
+
+ // create Event
+ Event event(Wave);
+ SndFileR sf(f);
+ event.setSndFile(sf);
+ // We are done with the _recFile member. Set to zero. The function which
+ // calls this function already does this immediately after. But do it here anyway.
+ track->setRecFile(0);
+
+ event.setSpos(0);
+
+ // Since the part start was snapped down, we must apply the difference so that the
+ // wave event tick lines up with when the user actually started recording.
+ // Added by Tim. p3.3.8
+ event.setTick(s.tick() - startTick);
+
+
+ event.setLenFrame(e.frame() - s.frame());
+ part->addEvent(event);
+
+ song->cmdAddPart(part);
+
+ if (song->len() < endTick)
+ song->setLen(endTick);
+ }
+
+//---------------------------------------------------------
+// cmdChangeWave
+// called from GUI context
+//---------------------------------------------------------
+void Song::cmdChangeWave(QString original, QString tmpfile, unsigned sx, unsigned ex)
+ {
+ char* original_charstr = new char[original.length() + 1];
+ char* tmpfile_charstr = new char[tmpfile.length() + 1];
+ strcpy(original_charstr, original.latin1());
+ strcpy(tmpfile_charstr, tmpfile.latin1());
+ song->undoOp(UndoOp::ModifyClip, original_charstr, tmpfile_charstr, sx, ex);
+ }
+
+//---------------------------------------------------------
+// SndFileR
+//---------------------------------------------------------
+
+SndFileR::SndFileR(SndFile* _sf)
+ {
+ sf = _sf;
+ if (sf)
+ (sf->refCount)++;
+ }
+
+SndFileR::SndFileR(const SndFileR& ed)
+ {
+ sf = ed.sf;
+ if (sf)
+ (sf->refCount)++;
+ }
+
+//---------------------------------------------------------
+// operator=
+//---------------------------------------------------------
+
+SndFileR& SndFileR::operator=(const SndFileR& ed)
+ {
+ if (sf == ed.sf)
+ return *this;
+ if (sf && --(sf->refCount) == 0) {
+ delete sf;
+ }
+ sf = ed.sf;
+ if (sf)
+ (sf->refCount)++;
+ return *this;
+ }
+
+//---------------------------------------------------------
+// ~SndFileR
+//---------------------------------------------------------
+
+SndFileR::~SndFileR()
+ {
+ if (sf)
+ if (--(sf->refCount) == 0) {
+ delete sf;
+ sf=NULL;
+ }
+ }