summaryrefslogtreecommitdiffstats
path: root/src/sound/AudioFileTimeStretcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sound/AudioFileTimeStretcher.cpp')
-rw-r--r--src/sound/AudioFileTimeStretcher.cpp268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/sound/AudioFileTimeStretcher.cpp b/src/sound/AudioFileTimeStretcher.cpp
new file mode 100644
index 0000000..d5b2321
--- /dev/null
+++ b/src/sound/AudioFileTimeStretcher.cpp
@@ -0,0 +1,268 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "AudioFileTimeStretcher.h"
+#include "AudioTimeStretcher.h"
+#include "AudioFileManager.h"
+#include "WAVAudioFile.h"
+#include "base/RealTime.h"
+
+#include <kapplication.h>
+
+#include <iostream>
+#include <fstream>
+
+namespace Rosegarden {
+
+
+AudioFileTimeStretcher::AudioFileTimeStretcher(AudioFileManager *manager) :
+ m_manager(manager),
+ m_timestretchCancelled(false)
+{
+}
+
+AudioFileTimeStretcher::~AudioFileTimeStretcher()
+{
+}
+
+AudioFileId
+AudioFileTimeStretcher::getStretchedAudioFile(AudioFileId source,
+ float ratio)
+{
+ AudioFile *sourceFile = m_manager->getAudioFile(source);
+ if (!sourceFile) {
+ throw SoundFile::BadSoundFileException
+ ("<unknown source>",
+ "Source file not found in AudioFileTimeStretcher::getStretchedAudioFile");
+ }
+
+ std::cerr << "AudioFileTimeStretcher: got source file id " << source
+ << ", name " << sourceFile->getFilename() << std::endl;
+
+ AudioFile *file = m_manager->createDerivedAudioFile(source, "stretch");
+ if (!file) {
+ throw AudioFileManager::BadAudioPathException(m_manager->getAudioPath());
+ }
+
+ std::cerr << "AudioFileTimeStretcher: got derived file id " << file->getId()
+ << ", name " << file->getFilename() << std::endl;
+
+ std::ifstream streamIn(sourceFile->getFilename().c_str(),
+ std::ios::in | std::ios::binary);
+ if (!streamIn) {
+ throw SoundFile::BadSoundFileException
+ (file->getFilename().c_str(),
+ "Failed to open source stream for time stretcher");
+ }
+
+ //!!!
+ //...
+ // Need to make SoundDriver::getAudioRecFileFormat available?
+ // -- the sound file classes should just have a float interface
+ // (like libsndfile, or hey!, we could use libsndfile...)
+
+ WAVAudioFile writeFile
+ (file->getFilename(),
+ sourceFile->getChannels(),
+ sourceFile->getSampleRate(),
+ sourceFile->getSampleRate() * 4 * sourceFile->getChannels(),
+ 4 * sourceFile->getChannels(),
+ 32);
+
+ if (!writeFile.write()) {
+ throw AudioFileManager::BadAudioPathException
+ (file->getFilename());
+ }
+
+ int obs = 1024;
+ int ibs = obs / ratio;
+ int ch = sourceFile->getChannels();
+ int sr = sourceFile->getSampleRate();
+
+ AudioTimeStretcher stretcher(sr, ch, ratio, true, obs);
+
+ // We'll first prime the timestretcher with half its window size
+ // of silence, an amount which we then discard at the start of the
+ // output (as well as its own processing latency). Really the
+ // timestretcher should handle this itself and report it in its
+ // own latency calculation
+
+ size_t padding = stretcher.getWindowSize()/2;
+
+ char *ebf = (char *)alloca
+ (ch * ibs * sourceFile->getBytesPerFrame());
+
+ std::vector<float *> dbfs;
+ for (int c = 0; c < ch; ++c) {
+ dbfs.push_back((float *)alloca((ibs > padding ? ibs : padding)
+ * sizeof(float)));
+ }
+
+ float **ibfs = (float **)alloca(ch * sizeof(float *));
+ float **obfs = (float **)alloca(ch * sizeof(float *));
+
+ for (int c = 0; c < ch; ++c) {
+ ibfs[c] = dbfs[c];
+ }
+
+ for (int c = 0; c < ch; ++c) {
+ obfs[c] = (float *)alloca(obs * sizeof(float));
+ }
+
+ char *oebf = (char *)alloca(ch * obs * sizeof(float));
+
+ int totalIn = 0, totalOut = 0;
+
+ for (int c = 0; c < ch; ++c) {
+ for (size_t i = 0; i < padding; ++i) {
+ ibfs[c][i] = 0.f;
+ }
+ }
+ stretcher.putInput(ibfs, padding);
+
+ RealTime totalTime = sourceFile->getLength();
+ long fileTotalIn = RealTime::realTime2Frame
+ (totalTime, sourceFile->getSampleRate());
+ int progressCount = 0;
+
+ long expectedOut = ceil(fileTotalIn * ratio);
+
+ m_timestretchCancelled = false;
+ bool inputExhausted = false;
+
+ sourceFile->scanTo(&streamIn, RealTime::zeroTime);
+
+ while (1) {
+
+ if (m_timestretchCancelled) {
+ std::cerr << "AudioFileTimeStretcher::getStretchedAudioFile: cancelled" << std::endl;
+ throw CancelledException();
+ }
+
+ unsigned int thisRead = 0;
+
+ if (!inputExhausted) {
+ thisRead = sourceFile->getSampleFrames(&streamIn, ebf, ibs);
+ if (thisRead < ibs) inputExhausted = true;
+ }
+
+ if (thisRead == 0) {
+ if (totalOut >= expectedOut) break;
+ else {
+ // run out of input data, continue feeding zeroes until
+ // we have enough output data
+ for (int c = 0; c < ch; ++c) {
+ for (int i = 0; i < ibs; ++i) {
+ ibfs[c][i] = 0.f;
+ }
+ }
+ thisRead = ibs;
+ }
+ }
+
+ if (!sourceFile->decode((unsigned char *)ebf,
+ thisRead * sourceFile->getBytesPerFrame(),
+ sr, ch,
+ thisRead, dbfs, false)) {
+ std::cerr << "ERROR: Stupid audio file class failed to decode its own output" << std::endl;
+ break;
+ }
+
+ stretcher.putInput(ibfs, thisRead);
+ totalIn += thisRead;
+
+ unsigned int available = stretcher.getAvailableOutputSamples();
+
+ while (available > 0) {
+
+ unsigned int count = available;
+ if (count > obs) count = obs;
+
+ if (padding > 0) {
+ if (count <= padding) {
+ stretcher.getOutput(obfs, count);
+ padding -= count;
+ available -= count;
+ continue;
+ } else {
+ stretcher.getOutput(obfs, padding);
+ count -= padding;
+ available -= padding;
+ padding = 0;
+ }
+ }
+
+ stretcher.getOutput(obfs, count);
+
+ char *encodePointer = oebf;
+ for (int i = 0; i < count; ++i) {
+ for (int c = 0; c < ch; ++c) {
+ float sample = obfs[c][i];
+ *(float *)encodePointer = sample;
+ encodePointer += sizeof(float);
+ }
+ }
+
+ if (totalOut < expectedOut &&
+ totalOut + count > expectedOut) {
+ count = expectedOut - totalOut;
+ }
+
+ writeFile.appendSamples(oebf, count);
+ totalOut += count;
+ available -= count;
+
+ if (totalOut >= expectedOut) break;
+ }
+
+ if (++progressCount == 100) {
+ int progress = int
+ ((100.f * float(totalIn)) / float(fileTotalIn));
+ emit setProgress(progress);
+ kapp->processEvents();
+ progressCount = 0;
+ }
+ }
+
+ emit setProgress(100);
+ kapp->processEvents();
+ writeFile.close();
+
+ std::cerr << "AudioFileTimeStretcher::getStretchedAudioFile: success, id is "
+ << file->getId() << std::endl;
+
+ return file->getId();
+}
+
+void
+AudioFileTimeStretcher::slotStopTimestretch()
+{
+ m_timestretchCancelled = true;
+}
+
+
+}
+
+#include "AudioFileTimeStretcher.moc"
+