Mercurial > hg > sonic-visualiser
changeset 203:0de89bce24e4
* Move some things around to facilitate plundering libraries for other
applications without needing to duplicate so much code.
sv/osc -> data/osc
sv/audioio -> audioio
sv/transform -> plugin/transform
sv/document -> document (will rename to framework in next commit)
author | Chris Cannam |
---|---|
date | Wed, 24 Oct 2007 16:34:31 +0000 |
parents | 738968d96e19 |
children | 688604a2c038 |
files | audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/AudioCallbackPlayTarget.cpp audioio/AudioCallbackPlayTarget.h audioio/AudioCoreAudioTarget.cpp audioio/AudioCoreAudioTarget.h audioio/AudioGenerator.cpp audioio/AudioGenerator.h audioio/AudioJACKTarget.cpp audioio/AudioJACKTarget.h audioio/AudioPortAudioTarget.cpp audioio/AudioPortAudioTarget.h audioio/AudioTargetFactory.cpp audioio/AudioTargetFactory.h audioio/PhaseVocoderTimeStretcher.cpp audioio/PhaseVocoderTimeStretcher.h audioio/PlaySpeedRangeMapper.cpp audioio/PlaySpeedRangeMapper.h document/Document.cpp document/Document.h document/SVFileReader.cpp document/SVFileReader.h main/MainWindow.cpp main/MainWindow.h main/MainWindowBase.cpp main/MainWindowBase.h osc/OSCMessage.cpp osc/OSCMessage.h osc/OSCQueue.cpp osc/OSCQueue.h osc/demoscript.sh osc/sv-command osc/sv-osc-send.c sv.pro transform/FeatureExtractionPluginTransform.cpp transform/FeatureExtractionPluginTransform.h transform/PluginTransform.cpp transform/PluginTransform.h transform/RealTimePluginTransform.cpp transform/RealTimePluginTransform.h transform/Transform.cpp transform/Transform.h transform/TransformFactory.cpp transform/TransformFactory.h |
diffstat | 44 files changed, 14 insertions(+), 13254 deletions(-) [+] |
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1493 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "AudioCallbackPlaySource.h" - -#include "AudioGenerator.h" - -#include "data/model/Model.h" -#include "view/ViewManager.h" -#include "base/PlayParameterRepository.h" -#include "base/Preferences.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/WaveFileModel.h" -#include "data/model/SparseOneDimensionalModel.h" -#include "plugin/RealTimePluginInstance.h" -#include "PhaseVocoderTimeStretcher.h" - -#include <iostream> -#include <cassert> - -//#define DEBUG_AUDIO_PLAY_SOURCE 1 -//#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1 - -const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071; - -AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManager *manager) : - m_viewManager(manager), - m_audioGenerator(new AudioGenerator()), - m_readBuffers(0), - m_writeBuffers(0), - m_readBufferFill(0), - m_writeBufferFill(0), - m_bufferScavenger(1), - m_sourceChannelCount(0), - m_blockSize(1024), - m_sourceSampleRate(0), - m_targetSampleRate(0), - m_playLatency(0), - m_playing(false), - m_exiting(false), - m_lastModelEndFrame(0), - m_outputLeft(0.0), - m_outputRight(0.0), - m_auditioningPlugin(0), - m_auditioningPluginBypassed(false), - m_timeStretcher(0), - m_fillThread(0), - m_converter(0), - m_crapConverter(0), - m_resampleQuality(Preferences::getInstance()->getResampleQuality()) -{ - m_viewManager->setAudioPlaySource(this); - - connect(m_viewManager, SIGNAL(selectionChanged()), - this, SLOT(selectionChanged())); - connect(m_viewManager, SIGNAL(playLoopModeChanged()), - this, SLOT(playLoopModeChanged())); - connect(m_viewManager, SIGNAL(playSelectionModeChanged()), - this, SLOT(playSelectionModeChanged())); - - connect(PlayParameterRepository::getInstance(), - SIGNAL(playParametersChanged(PlayParameters *)), - this, SLOT(playParametersChanged(PlayParameters *))); - - connect(Preferences::getInstance(), - SIGNAL(propertyChanged(PropertyContainer::PropertyName)), - this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); -} - -AudioCallbackPlaySource::~AudioCallbackPlaySource() -{ - m_exiting = true; - - if (m_fillThread) { - m_condition.wakeAll(); - m_fillThread->wait(); - delete m_fillThread; - } - - clearModels(); - - if (m_readBuffers != m_writeBuffers) { - delete m_readBuffers; - } - - delete m_writeBuffers; - - delete m_audioGenerator; - - m_bufferScavenger.scavenge(true); - m_pluginScavenger.scavenge(true); - m_timeStretcherScavenger.scavenge(true); -} - -void -AudioCallbackPlaySource::addModel(Model *model) -{ - if (m_models.find(model) != m_models.end()) return; - - bool canPlay = m_audioGenerator->addModel(model); - - m_mutex.lock(); - - m_models.insert(model); - if (model->getEndFrame() > m_lastModelEndFrame) { - m_lastModelEndFrame = model->getEndFrame(); - } - - bool buffersChanged = false, srChanged = false; - - size_t modelChannels = 1; - DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); - if (dtvm) modelChannels = dtvm->getChannelCount(); - if (modelChannels > m_sourceChannelCount) { - m_sourceChannelCount = modelChannels; - } - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Adding model with " << modelChannels << " channels " << std::endl; -#endif - - if (m_sourceSampleRate == 0) { - - m_sourceSampleRate = model->getSampleRate(); - srChanged = true; - - } else if (model->getSampleRate() != m_sourceSampleRate) { - - // If this is a dense time-value model and we have no other, we - // can just switch to this model's sample rate - - if (dtvm) { - - bool conflicting = false; - - for (std::set<Model *>::const_iterator i = m_models.begin(); - i != m_models.end(); ++i) { - // Only wave file models can be considered conflicting -- - // writable wave file models are derived and we shouldn't - // take their rates into account. Also, don't give any - // particular weight to a file that's already playing at - // the wrong rate anyway - WaveFileModel *wfm = dynamic_cast<WaveFileModel *>(*i); - if (wfm && wfm != dtvm && - wfm->getSampleRate() != model->getSampleRate() && - wfm->getSampleRate() == m_sourceSampleRate) { - std::cerr << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << std::endl; - conflicting = true; - break; - } - } - - if (conflicting) { - - std::cerr << "AudioCallbackPlaySource::addModel: ERROR: " - << "New model sample rate does not match" << std::endl - << "existing model(s) (new " << model->getSampleRate() - << " vs " << m_sourceSampleRate - << "), playback will be wrong" - << std::endl; - - emit sampleRateMismatch(model->getSampleRate(), - m_sourceSampleRate, - false); - } else { - m_sourceSampleRate = model->getSampleRate(); - srChanged = true; - } - } - } - - if (!m_writeBuffers || (m_writeBuffers->size() < getTargetChannelCount())) { - clearRingBuffers(true, getTargetChannelCount()); - buffersChanged = true; - } else { - if (canPlay) clearRingBuffers(true); - } - - if (buffersChanged || srChanged) { - if (m_converter) { - src_delete(m_converter); - src_delete(m_crapConverter); - m_converter = 0; - m_crapConverter = 0; - } - } - - m_mutex.unlock(); - - m_audioGenerator->setTargetChannelCount(getTargetChannelCount()); - - if (!m_fillThread) { - m_fillThread = new FillThread(*this); - m_fillThread->start(); - } - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s) -- emitting modelReplaced" << std::endl; -#endif - - if (buffersChanged || srChanged) { - emit modelReplaced(); - } - - connect(model, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(modelChanged(size_t, size_t))); - - m_condition.wakeAll(); -} - -void -AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame) -{ -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; -#endif - if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame; -} - -void -AudioCallbackPlaySource::removeModel(Model *model) -{ - m_mutex.lock(); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << std::endl; -#endif - - disconnect(model, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(modelChanged(size_t, size_t))); - - m_models.erase(model); - - if (m_models.empty()) { - if (m_converter) { - src_delete(m_converter); - src_delete(m_crapConverter); - m_converter = 0; - m_crapConverter = 0; - } - m_sourceSampleRate = 0; - } - - size_t lastEnd = 0; - for (std::set<Model *>::const_iterator i = m_models.begin(); - i != m_models.end(); ++i) { -// std::cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << std::endl; - if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame(); -// std::cout << "(done, lastEnd now " << lastEnd << ")" << std::endl; - } - m_lastModelEndFrame = lastEnd; - - m_mutex.unlock(); - - m_audioGenerator->removeModel(model); - - clearRingBuffers(); -} - -void -AudioCallbackPlaySource::clearModels() -{ - m_mutex.lock(); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySource::clearModels()" << std::endl; -#endif - - m_models.clear(); - - if (m_converter) { - src_delete(m_converter); - src_delete(m_crapConverter); - m_converter = 0; - m_crapConverter = 0; - } - - m_lastModelEndFrame = 0; - - m_sourceSampleRate = 0; - - m_mutex.unlock(); - - m_audioGenerator->clearModels(); -} - -void -AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count) -{ - if (!haveLock) m_mutex.lock(); - - if (count == 0) { - if (m_writeBuffers) count = m_writeBuffers->size(); - } - - size_t sf = m_readBufferFill; - RingBuffer<float> *rb = getReadRingBuffer(0); - if (rb) { - //!!! This is incorrect if we're in a non-contiguous selection - //Same goes for all related code (subtracting the read space - //from the fill frame to try to establish where the effective - //pre-resample/timestretch read pointer is) - size_t rs = rb->getReadSpace(); - if (rs < sf) sf -= rs; - else sf = 0; - } - m_writeBufferFill = sf; - - if (m_readBuffers != m_writeBuffers) { - delete m_writeBuffers; - } - - m_writeBuffers = new RingBufferVector; - - for (size_t i = 0; i < count; ++i) { - m_writeBuffers->push_back(new RingBuffer<float>(m_ringBufferSize)); - } - -// std::cout << "AudioCallbackPlaySource::clearRingBuffers: Created " -// << count << " write buffers" << std::endl; - - if (!haveLock) { - m_mutex.unlock(); - } -} - -void -AudioCallbackPlaySource::play(size_t startFrame) -{ - if (m_viewManager->getPlaySelectionMode() && - !m_viewManager->getSelections().empty()) { - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - MultiSelection::SelectionList::iterator i = selections.begin(); - if (i != selections.end()) { - if (startFrame < i->getStartFrame()) { - startFrame = i->getStartFrame(); - } else { - MultiSelection::SelectionList::iterator j = selections.end(); - --j; - if (startFrame >= j->getEndFrame()) { - startFrame = i->getStartFrame(); - } - } - } - } else { - if (startFrame >= m_lastModelEndFrame) { - startFrame = 0; - } - } - - // The fill thread will automatically empty its buffers before - // starting again if we have not so far been playing, but not if - // we're just re-seeking. - - m_mutex.lock(); - if (m_playing) { - m_readBufferFill = m_writeBufferFill = startFrame; - if (m_readBuffers) { - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - RingBuffer<float> *rb = getReadRingBuffer(c); - if (rb) rb->reset(); - } - } - if (m_converter) src_reset(m_converter); - if (m_crapConverter) src_reset(m_crapConverter); - } else { - if (m_converter) src_reset(m_converter); - if (m_crapConverter) src_reset(m_crapConverter); - m_readBufferFill = m_writeBufferFill = startFrame; - } - m_mutex.unlock(); - - m_audioGenerator->reset(); - - bool changed = !m_playing; - m_playing = true; - m_condition.wakeAll(); - if (changed) emit playStatusChanged(m_playing); -} - -void -AudioCallbackPlaySource::stop() -{ - bool changed = m_playing; - m_playing = false; - m_condition.wakeAll(); - if (changed) emit playStatusChanged(m_playing); -} - -void -AudioCallbackPlaySource::selectionChanged() -{ - if (m_viewManager->getPlaySelectionMode()) { - clearRingBuffers(); - } -} - -void -AudioCallbackPlaySource::playLoopModeChanged() -{ - clearRingBuffers(); -} - -void -AudioCallbackPlaySource::playSelectionModeChanged() -{ - if (!m_viewManager->getSelections().empty()) { - clearRingBuffers(); - } -} - -void -AudioCallbackPlaySource::playParametersChanged(PlayParameters *) -{ - clearRingBuffers(); -} - -void -AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName n) -{ - if (n == "Resample Quality") { - setResampleQuality(Preferences::getInstance()->getResampleQuality()); - } -} - -void -AudioCallbackPlaySource::audioProcessingOverload() -{ - RealTimePluginInstance *ap = m_auditioningPlugin; - if (ap && m_playing && !m_auditioningPluginBypassed) { - m_auditioningPluginBypassed = true; - emit audioOverloadPluginDisabled(); - } -} - -void -AudioCallbackPlaySource::setTargetBlockSize(size_t size) -{ -// std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl; - assert(size < m_ringBufferSize); - m_blockSize = size; -} - -size_t -AudioCallbackPlaySource::getTargetBlockSize() const -{ -// std::cout << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << std::endl; - return m_blockSize; -} - -void -AudioCallbackPlaySource::setTargetPlayLatency(size_t latency) -{ - m_playLatency = latency; -} - -size_t -AudioCallbackPlaySource::getTargetPlayLatency() const -{ - return m_playLatency; -} - -size_t -AudioCallbackPlaySource::getCurrentPlayingFrame() -{ - bool resample = false; - double ratio = 1.0; - - if (getSourceSampleRate() != getTargetSampleRate()) { - resample = true; - ratio = double(getSourceSampleRate()) / double(getTargetSampleRate()); - } - - size_t readSpace = 0; - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - RingBuffer<float> *rb = getReadRingBuffer(c); - if (rb) { - size_t spaceHere = rb->getReadSpace(); - if (c == 0 || spaceHere < readSpace) readSpace = spaceHere; - } - } - - if (resample) { - readSpace = size_t(readSpace * ratio + 0.1); - } - - size_t latency = m_playLatency; - if (resample) latency = size_t(m_playLatency * ratio + 0.1); - - PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher; - if (timeStretcher) { - latency += timeStretcher->getProcessingLatency(); - } - - latency += readSpace; - size_t bufferedFrame = m_readBufferFill; - - bool looping = m_viewManager->getPlayLoopMode(); - bool constrained = (m_viewManager->getPlaySelectionMode() && - !m_viewManager->getSelections().empty()); - - size_t framePlaying = bufferedFrame; - - if (looping && !constrained) { - while (framePlaying < latency) framePlaying += m_lastModelEndFrame; - } - - if (framePlaying > latency) framePlaying -= latency; - else framePlaying = 0; - - if (!constrained) { - if (!looping && framePlaying > m_lastModelEndFrame) { - framePlaying = m_lastModelEndFrame; - stop(); - } - return framePlaying; - } - - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - MultiSelection::SelectionList::const_iterator i; - -// i = selections.begin(); -// size_t rangeStart = i->getStartFrame(); - - i = selections.end(); - --i; - size_t rangeEnd = i->getEndFrame(); - - for (i = selections.begin(); i != selections.end(); ++i) { - if (i->contains(bufferedFrame)) break; - } - - size_t f = bufferedFrame; - -// std::cout << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl; - - if (i == selections.end()) { - --i; - if (i->getEndFrame() + latency < f) { -// std::cout << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl; - - if (!looping && (framePlaying > rangeEnd)) { -// std::cout << "STOPPING" << std::endl; - stop(); - return rangeEnd; - } else { - return framePlaying; - } - } else { -// std::cout << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl; - latency -= (f - i->getEndFrame()); - f = i->getEndFrame(); - } - } - -// std::cout << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl; - - while (latency > 0) { - size_t offset = f - i->getStartFrame(); - if (offset >= latency) { - if (f > latency) { - framePlaying = f - latency; - } else { - framePlaying = 0; - } - break; - } else { - if (i == selections.begin()) { - if (looping) { - i = selections.end(); - } - } - latency -= offset; - --i; - f = i->getEndFrame(); - } - } - - return framePlaying; -} - -void -AudioCallbackPlaySource::setOutputLevels(float left, float right) -{ - m_outputLeft = left; - m_outputRight = right; -} - -bool -AudioCallbackPlaySource::getOutputLevels(float &left, float &right) -{ - left = m_outputLeft; - right = m_outputRight; - return true; -} - -void -AudioCallbackPlaySource::setTargetSampleRate(size_t sr) -{ - m_targetSampleRate = sr; - initialiseConverter(); -} - -void -AudioCallbackPlaySource::initialiseConverter() -{ - m_mutex.lock(); - - if (m_converter) { - src_delete(m_converter); - src_delete(m_crapConverter); - m_converter = 0; - m_crapConverter = 0; - } - - if (getSourceSampleRate() != getTargetSampleRate()) { - - int err = 0; - - m_converter = src_new(m_resampleQuality == 2 ? SRC_SINC_BEST_QUALITY : - m_resampleQuality == 1 ? SRC_SINC_MEDIUM_QUALITY : - m_resampleQuality == 0 ? SRC_SINC_FASTEST : - SRC_SINC_MEDIUM_QUALITY, - getTargetChannelCount(), &err); - - if (m_converter) { - m_crapConverter = src_new(SRC_LINEAR, - getTargetChannelCount(), - &err); - } - - if (!m_converter || !m_crapConverter) { - std::cerr - << "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: " - << src_strerror(err) << std::endl; - - if (m_converter) { - src_delete(m_converter); - m_converter = 0; - } - - if (m_crapConverter) { - src_delete(m_crapConverter); - m_crapConverter = 0; - } - - m_mutex.unlock(); - - emit sampleRateMismatch(getSourceSampleRate(), - getTargetSampleRate(), - false); - } else { - - m_mutex.unlock(); - - emit sampleRateMismatch(getSourceSampleRate(), - getTargetSampleRate(), - true); - } - } else { - m_mutex.unlock(); - } -} - -void -AudioCallbackPlaySource::setResampleQuality(int q) -{ - if (q == m_resampleQuality) return; - m_resampleQuality = q; - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cerr << "AudioCallbackPlaySource::setResampleQuality: setting to " - << m_resampleQuality << std::endl; -#endif - - initialiseConverter(); -} - -void -AudioCallbackPlaySource::setAuditioningPlugin(RealTimePluginInstance *plugin) -{ - RealTimePluginInstance *formerPlugin = m_auditioningPlugin; - m_auditioningPlugin = plugin; - m_auditioningPluginBypassed = false; - if (formerPlugin) m_pluginScavenger.claim(formerPlugin); -} - -void -AudioCallbackPlaySource::setSoloModelSet(std::set<Model *> s) -{ - m_audioGenerator->setSoloModelSet(s); - clearRingBuffers(); -} - -void -AudioCallbackPlaySource::clearSoloModelSet() -{ - m_audioGenerator->clearSoloModelSet(); - clearRingBuffers(); -} - -size_t -AudioCallbackPlaySource::getTargetSampleRate() const -{ - if (m_targetSampleRate) return m_targetSampleRate; - else return getSourceSampleRate(); -} - -size_t -AudioCallbackPlaySource::getSourceChannelCount() const -{ - return m_sourceChannelCount; -} - -size_t -AudioCallbackPlaySource::getTargetChannelCount() const -{ - if (m_sourceChannelCount < 2) return 2; - return m_sourceChannelCount; -} - -size_t -AudioCallbackPlaySource::getSourceSampleRate() const -{ - return m_sourceSampleRate; -} - -void -AudioCallbackPlaySource::setTimeStretch(float factor, bool sharpen, bool mono) -{ - // Avoid locks -- create, assign, mark old one for scavenging - // later (as a call to getSourceSamples may still be using it) - - PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher; - - size_t channels = getTargetChannelCount(); - if (mono) channels = 1; - - if (existingStretcher && - existingStretcher->getRatio() == factor && - existingStretcher->getSharpening() == sharpen && - existingStretcher->getChannelCount() == channels) { - return; - } - - if (factor != 1) { - - if (existingStretcher && - existingStretcher->getSharpening() == sharpen && - existingStretcher->getChannelCount() == channels) { - existingStretcher->setRatio(factor); - return; - } - - PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher - (getTargetSampleRate(), - channels, - factor, - sharpen, - getTargetBlockSize()); - - m_timeStretcher = newStretcher; - - } else { - m_timeStretcher = 0; - } - - if (existingStretcher) { - m_timeStretcherScavenger.claim(existingStretcher); - } -} - -size_t -AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer) -{ - if (!m_playing) { - for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { - for (size_t i = 0; i < count; ++i) { - buffer[ch][i] = 0.0; - } - } - return 0; - } - - // Ensure that all buffers have at least the amount of data we - // need -- else reduce the size of our requests correspondingly - - for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { - - RingBuffer<float> *rb = getReadRingBuffer(ch); - - if (!rb) { - std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " - << "No ring buffer available for channel " << ch - << ", returning no data here" << std::endl; - count = 0; - break; - } - - size_t rs = rb->getReadSpace(); - if (rs < count) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: " - << "Ring buffer for channel " << ch << " has only " - << rs << " (of " << count << ") samples available, " - << "reducing request size" << std::endl; -#endif - count = rs; - } - } - - if (count == 0) return 0; - - PhaseVocoderTimeStretcher *ts = m_timeStretcher; - - if (!ts || ts->getRatio() == 1) { - - size_t got = 0; - - for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { - - RingBuffer<float> *rb = getReadRingBuffer(ch); - - if (rb) { - - // this is marginally more likely to leave our channels in - // sync after a processing failure than just passing "count": - size_t request = count; - if (ch > 0) request = got; - - got = rb->read(buffer[ch], request); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << std::endl; -#endif - } - - for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { - for (size_t i = got; i < count; ++i) { - buffer[ch][i] = 0.0; - } - } - } - - applyAuditioningEffect(count, buffer); - - m_condition.wakeAll(); - return got; - } - - float ratio = ts->getRatio(); - -// std::cout << "ratio = " << ratio << std::endl; - - size_t channels = getTargetChannelCount(); - bool mix = (channels > 1 && ts->getChannelCount() == 1); - - size_t available; - - int warned = 0; - - // We want output blocks of e.g. 1024 (probably fixed, certainly - // bounded). We can provide input blocks of any size (unbounded) - // at the timestretcher's request. The input block for a given - // output is approx output / ratio, but we can't predict it - // exactly, for an adaptive timestretcher. The stretcher will - // need some additional buffer space. See the time stretcher code - // and comments. - - while ((available = ts->getAvailableOutputSamples()) < count) { - - size_t reqd = lrintf((count - available) / ratio); - reqd = std::max(reqd, ts->getRequiredInputSamples()); - if (reqd == 0) reqd = 1; - - float *ib[channels]; - - size_t got = reqd; - - if (mix) { - for (size_t c = 0; c < channels; ++c) { - if (c == 0) ib[c] = new float[reqd]; //!!! fix -- this is a rt function - else ib[c] = 0; - RingBuffer<float> *rb = getReadRingBuffer(c); - if (rb) { - size_t gotHere; - if (c > 0) gotHere = rb->readAdding(ib[0], got); - else gotHere = rb->read(ib[0], got); - if (gotHere < got) got = gotHere; - } - } - } else { - for (size_t c = 0; c < channels; ++c) { - ib[c] = new float[reqd]; //!!! fix -- this is a rt function - RingBuffer<float> *rb = getReadRingBuffer(c); - if (rb) { - size_t gotHere = rb->read(ib[c], got); - if (gotHere < got) got = gotHere; - } - } - } - - if (got < reqd) { - std::cerr << "WARNING: Read underrun in playback (" - << got << " < " << reqd << ")" << std::endl; - } - - ts->putInput(ib, got); - - for (size_t c = 0; c < channels; ++c) { - delete[] ib[c]; - } - - if (got == 0) break; - - if (ts->getAvailableOutputSamples() == available) { - std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl; - if (++warned == 5) break; - } - } - - ts->getOutput(buffer, count); - - if (mix) { - for (size_t c = 1; c < channels; ++c) { - for (size_t i = 0; i < count; ++i) { - buffer[c][i] = buffer[0][i] / channels; - } - } - for (size_t i = 0; i < count; ++i) { - buffer[0][i] /= channels; - } - } - - applyAuditioningEffect(count, buffer); - - m_condition.wakeAll(); - - return count; -} - -void -AudioCallbackPlaySource::applyAuditioningEffect(size_t count, float **buffers) -{ - if (m_auditioningPluginBypassed) return; - RealTimePluginInstance *plugin = m_auditioningPlugin; - if (!plugin) return; - - if (plugin->getAudioInputCount() != getTargetChannelCount()) { -// std::cerr << "plugin input count " << plugin->getAudioInputCount() -// << " != our channel count " << getTargetChannelCount() -// << std::endl; - return; - } - if (plugin->getAudioOutputCount() != getTargetChannelCount()) { -// std::cerr << "plugin output count " << plugin->getAudioOutputCount() -// << " != our channel count " << getTargetChannelCount() -// << std::endl; - return; - } - if (plugin->getBufferSize() != count) { -// std::cerr << "plugin buffer size " << plugin->getBufferSize() -// << " != our block size " << count -// << std::endl; - return; - } - - float **ib = plugin->getAudioInputBuffers(); - float **ob = plugin->getAudioOutputBuffers(); - - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - for (size_t i = 0; i < count; ++i) { - ib[c][i] = buffers[c][i]; - } - } - - plugin->run(Vamp::RealTime::zeroTime); - - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - for (size_t i = 0; i < count; ++i) { - buffers[c][i] = ob[c][i]; - } - } -} - -// Called from fill thread, m_playing true, mutex held -bool -AudioCallbackPlaySource::fillBuffers() -{ - static float *tmp = 0; - static size_t tmpSize = 0; - - size_t space = 0; - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) { - size_t spaceHere = wb->getWriteSpace(); - if (c == 0 || spaceHere < space) space = spaceHere; - } - } - - if (space == 0) return false; - - size_t f = m_writeBufferFill; - - bool readWriteEqual = (m_readBuffers == m_writeBuffers); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl; -#endif - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "buffered to " << f << " already" << std::endl; -#endif - - bool resample = (getSourceSampleRate() != getTargetSampleRate()); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << std::endl; -#endif - - size_t channels = getTargetChannelCount(); - - size_t orig = space; - size_t got = 0; - - static float **bufferPtrs = 0; - static size_t bufferPtrCount = 0; - - if (bufferPtrCount < channels) { - if (bufferPtrs) delete[] bufferPtrs; - bufferPtrs = new float *[channels]; - bufferPtrCount = channels; - } - - size_t generatorBlockSize = m_audioGenerator->getBlockSize(); - - if (resample && !m_converter) { - static bool warned = false; - if (!warned) { - std::cerr << "WARNING: sample rates differ, but no converter available!" << std::endl; - warned = true; - } - } - - if (resample && m_converter) { - - double ratio = - double(getTargetSampleRate()) / double(getSourceSampleRate()); - orig = size_t(orig / ratio + 0.1); - - // orig must be a multiple of generatorBlockSize - orig = (orig / generatorBlockSize) * generatorBlockSize; - if (orig == 0) return false; - - size_t work = std::max(orig, space); - - // We only allocate one buffer, but we use it in two halves. - // We place the non-interleaved values in the second half of - // the buffer (orig samples for channel 0, orig samples for - // channel 1 etc), and then interleave them into the first - // half of the buffer. Then we resample back into the second - // half (interleaved) and de-interleave the results back to - // the start of the buffer for insertion into the ringbuffers. - // What a faff -- especially as we've already de-interleaved - // the audio data from the source file elsewhere before we - // even reach this point. - - if (tmpSize < channels * work * 2) { - delete[] tmp; - tmp = new float[channels * work * 2]; - tmpSize = channels * work * 2; - } - - float *nonintlv = tmp + channels * work; - float *intlv = tmp; - float *srcout = tmp + channels * work; - - for (size_t c = 0; c < channels; ++c) { - for (size_t i = 0; i < orig; ++i) { - nonintlv[channels * i + c] = 0.0f; - } - } - - for (size_t c = 0; c < channels; ++c) { - bufferPtrs[c] = nonintlv + c * orig; - } - - got = mixModels(f, orig, bufferPtrs); - - // and interleave into first half - for (size_t c = 0; c < channels; ++c) { - for (size_t i = 0; i < got; ++i) { - float sample = nonintlv[c * got + i]; - intlv[channels * i + c] = sample; - } - } - - SRC_DATA data; - data.data_in = intlv; - data.data_out = srcout; - data.input_frames = got; - data.output_frames = work; - data.src_ratio = ratio; - data.end_of_input = 0; - - int err = 0; - - if (m_timeStretcher && m_timeStretcher->getRatio() < 0.4) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Using crappy converter" << std::endl; -#endif - err = src_process(m_crapConverter, &data); - } else { - err = src_process(m_converter, &data); - } - - size_t toCopy = size_t(got * ratio + 0.1); - - if (err) { - std::cerr - << "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: " - << src_strerror(err) << std::endl; - //!!! Then what? - } else { - got = data.input_frames_used; - toCopy = data.output_frames_gen; -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Resampled " << got << " frames to " << toCopy << " frames" << std::endl; -#endif - } - - for (size_t c = 0; c < channels; ++c) { - for (size_t i = 0; i < toCopy; ++i) { - tmp[i] = srcout[channels * i + c]; - } - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) wb->write(tmp, toCopy); - } - - m_writeBufferFill = f; - if (readWriteEqual) m_readBufferFill = f; - - } else { - - // space must be a multiple of generatorBlockSize - space = (space / generatorBlockSize) * generatorBlockSize; - if (space == 0) return false; - - if (tmpSize < channels * space) { - delete[] tmp; - tmp = new float[channels * space]; - tmpSize = channels * space; - } - - for (size_t c = 0; c < channels; ++c) { - - bufferPtrs[c] = tmp + c * space; - - for (size_t i = 0; i < space; ++i) { - tmp[c * space + i] = 0.0f; - } - } - - size_t got = mixModels(f, space, bufferPtrs); - - for (size_t c = 0; c < channels; ++c) { - - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) { - size_t actual = wb->write(bufferPtrs[c], got); -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Wrote " << actual << " samples for ch " << c << ", now " - << wb->getReadSpace() << " to read" - << std::endl; -#endif - if (actual < got) { - std::cerr << "WARNING: Buffer overrun in channel " << c - << ": wrote " << actual << " of " << got - << " samples" << std::endl; - } - } - } - - m_writeBufferFill = f; - if (readWriteEqual) m_readBufferFill = f; - - //!!! how do we know when ended? need to mark up a fully-buffered flag and check this if we find the buffers empty in getSourceSamples - } - - return true; -} - -size_t -AudioCallbackPlaySource::mixModels(size_t &frame, size_t count, float **buffers) -{ - size_t processed = 0; - size_t chunkStart = frame; - size_t chunkSize = count; - size_t selectionSize = 0; - size_t nextChunkStart = chunkStart + chunkSize; - - bool looping = m_viewManager->getPlayLoopMode(); - bool constrained = (m_viewManager->getPlaySelectionMode() && - !m_viewManager->getSelections().empty()); - - static float **chunkBufferPtrs = 0; - static size_t chunkBufferPtrCount = 0; - size_t channels = getTargetChannelCount(); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl; -#endif - - if (chunkBufferPtrCount < channels) { - if (chunkBufferPtrs) delete[] chunkBufferPtrs; - chunkBufferPtrs = new float *[channels]; - chunkBufferPtrCount = channels; - } - - for (size_t c = 0; c < channels; ++c) { - chunkBufferPtrs[c] = buffers[c]; - } - - while (processed < count) { - - chunkSize = count - processed; - nextChunkStart = chunkStart + chunkSize; - selectionSize = 0; - - size_t fadeIn = 0, fadeOut = 0; - - if (constrained) { - - Selection selection = - m_viewManager->getContainingSelection(chunkStart, true); - - if (selection.isEmpty()) { - if (looping) { - selection = *m_viewManager->getSelections().begin(); - chunkStart = selection.getStartFrame(); - fadeIn = 50; - } - } - - if (selection.isEmpty()) { - - chunkSize = 0; - nextChunkStart = chunkStart; - - } else { - - selectionSize = - selection.getEndFrame() - - selection.getStartFrame(); - - if (chunkStart < selection.getStartFrame()) { - chunkStart = selection.getStartFrame(); - fadeIn = 50; - } - - nextChunkStart = chunkStart + chunkSize; - - if (nextChunkStart >= selection.getEndFrame()) { - nextChunkStart = selection.getEndFrame(); - fadeOut = 50; - } - - chunkSize = nextChunkStart - chunkStart; - } - - } else if (looping && m_lastModelEndFrame > 0) { - - if (chunkStart >= m_lastModelEndFrame) { - chunkStart = 0; - } - if (chunkSize > m_lastModelEndFrame - chunkStart) { - chunkSize = m_lastModelEndFrame - chunkStart; - } - nextChunkStart = chunkStart + chunkSize; - } - -// std::cout << "chunkStart " << chunkStart << ", chunkSize " << chunkSize << ", nextChunkStart " << nextChunkStart << ", frame " << frame << ", count " << count << ", processed " << processed << std::endl; - - if (!chunkSize) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Ending selection playback at " << nextChunkStart << std::endl; -#endif - // We need to maintain full buffers so that the other - // thread can tell where it's got to in the playback -- so - // return the full amount here - frame = frame + count; - return count; - } - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl; -#endif - - size_t got = 0; - - if (selectionSize < 100) { - fadeIn = 0; - fadeOut = 0; - } else if (selectionSize < 300) { - if (fadeIn > 0) fadeIn = 10; - if (fadeOut > 0) fadeOut = 10; - } - - if (fadeIn > 0) { - if (processed * 2 < fadeIn) { - fadeIn = processed * 2; - } - } - - if (fadeOut > 0) { - if ((count - processed - chunkSize) * 2 < fadeOut) { - fadeOut = (count - processed - chunkSize) * 2; - } - } - - for (std::set<Model *>::iterator mi = m_models.begin(); - mi != m_models.end(); ++mi) { - - got = m_audioGenerator->mixModel(*mi, chunkStart, - chunkSize, chunkBufferPtrs, - fadeIn, fadeOut); - } - - for (size_t c = 0; c < channels; ++c) { - chunkBufferPtrs[c] += chunkSize; - } - - processed += chunkSize; - chunkStart = nextChunkStart; - } - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "Returning selection playback " << processed << " frames to " << nextChunkStart << std::endl; -#endif - - frame = nextChunkStart; - return processed; -} - -void -AudioCallbackPlaySource::unifyRingBuffers() -{ - if (m_readBuffers == m_writeBuffers) return; - - // only unify if there will be something to read - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) { - if (wb->getReadSpace() < m_blockSize * 2) { - if ((m_writeBufferFill + m_blockSize * 2) < - m_lastModelEndFrame) { - // OK, we don't have enough and there's more to - // read -- don't unify until we can do better - return; - } - } - break; - } - } - - size_t rf = m_readBufferFill; - RingBuffer<float> *rb = getReadRingBuffer(0); - if (rb) { - size_t rs = rb->getReadSpace(); - //!!! incorrect when in non-contiguous selection, see comments elsewhere -// std::cout << "rs = " << rs << std::endl; - if (rs < rf) rf -= rs; - else rf = 0; - } - - //std::cout << "m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << std::endl; - - size_t wf = m_writeBufferFill; - size_t skip = 0; - for (size_t c = 0; c < getTargetChannelCount(); ++c) { - RingBuffer<float> *wb = getWriteRingBuffer(c); - if (wb) { - if (c == 0) { - - size_t wrs = wb->getReadSpace(); -// std::cout << "wrs = " << wrs << std::endl; - - if (wrs < wf) wf -= wrs; - else wf = 0; -// std::cout << "wf = " << wf << std::endl; - - if (wf < rf) skip = rf - wf; - if (skip == 0) break; - } - -// std::cout << "skipping " << skip << std::endl; - wb->skip(skip); - } - } - - m_bufferScavenger.claim(m_readBuffers); - m_readBuffers = m_writeBuffers; - m_readBufferFill = m_writeBufferFill; -// std::cout << "unified" << std::endl; -} - -void -AudioCallbackPlaySource::FillThread::run() -{ - AudioCallbackPlaySource &s(m_source); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySourceFillThread starting" << std::endl; -#endif - - s.m_mutex.lock(); - - bool previouslyPlaying = s.m_playing; - bool work = false; - - while (!s.m_exiting) { - - s.unifyRingBuffers(); - s.m_bufferScavenger.scavenge(); - s.m_pluginScavenger.scavenge(); - s.m_timeStretcherScavenger.scavenge(); - - if (work && s.m_playing && s.getSourceSampleRate()) { - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl; -#endif - - s.m_mutex.unlock(); - s.m_mutex.lock(); - - } else { - - float ms = 100; - if (s.getSourceSampleRate() > 0) { - ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0; - } - - if (s.m_playing) ms /= 10; - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - if (!s.m_playing) std::cout << std::endl; - std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl; -#endif - - s.m_condition.wait(&s.m_mutex, size_t(ms)); - } - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl; -#endif - - work = false; - - if (!s.getSourceSampleRate()) continue; - - bool playing = s.m_playing; - - if (playing && !previouslyPlaying) { -#ifdef DEBUG_AUDIO_PLAY_SOURCE - std::cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << std::endl; -#endif - for (size_t c = 0; c < s.getTargetChannelCount(); ++c) { - RingBuffer<float> *rb = s.getReadRingBuffer(c); - if (rb) rb->reset(); - } - } - previouslyPlaying = playing; - - work = s.fillBuffers(); - } - - s.m_mutex.unlock(); -} -
--- a/audioio/AudioCallbackPlaySource.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,344 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _AUDIO_CALLBACK_PLAY_SOURCE_H_ -#define _AUDIO_CALLBACK_PLAY_SOURCE_H_ - -#include "base/RingBuffer.h" -#include "base/AudioPlaySource.h" -#include "base/PropertyContainer.h" -#include "base/Scavenger.h" - -#include <QObject> -#include <QMutex> -#include <QWaitCondition> - -#include "base/Thread.h" - -#include <samplerate.h> - -#include <set> -#include <map> - -class Model; -class ViewManager; -class AudioGenerator; -class PlayParameters; -class PhaseVocoderTimeStretcher; -class RealTimePluginInstance; - -/** - * AudioCallbackPlaySource manages audio data supply to callback-based - * audio APIs such as JACK or CoreAudio. It maintains one ring buffer - * per channel, filled during playback by a non-realtime thread, and - * provides a method for a realtime thread to pick up the latest - * available sample data from these buffers. - */ -class AudioCallbackPlaySource : public virtual QObject, - public AudioPlaySource -{ - Q_OBJECT - -public: - AudioCallbackPlaySource(ViewManager *); - virtual ~AudioCallbackPlaySource(); - - /** - * Add a data model to be played from. The source can mix - * playback from a number of sources including dense and sparse - * models. The models must match in sample rate, but they don't - * have to have identical numbers of channels. - */ - virtual void addModel(Model *model); - - /** - * Remove a model. - */ - virtual void removeModel(Model *model); - - /** - * Remove all models. (Silence will ensue.) - */ - virtual void clearModels(); - - /** - * Start making data available in the ring buffers for playback, - * from the given frame. If playback is already under way, reseek - * to the given frame and continue. - */ - virtual void play(size_t startFrame); - - /** - * Stop playback and ensure that no more data is returned. - */ - virtual void stop(); - - /** - * Return whether playback is currently supposed to be happening. - */ - virtual bool isPlaying() const { return m_playing; } - - /** - * Return the frame number that is currently expected to be coming - * out of the speakers. (i.e. compensating for playback latency.) - */ - virtual size_t getCurrentPlayingFrame(); - - /** - * Return the frame at which playback is expected to end (if not looping). - */ - virtual size_t getPlayEndFrame() { return m_lastModelEndFrame; } - - /** - * Set the block size of the target audio device. This should - * be called by the target class. - */ - void setTargetBlockSize(size_t); - - /** - * Get the block size of the target audio device. - */ - size_t getTargetBlockSize() const; - - /** - * Set the playback latency of the target audio device, in frames - * at the target sample rate. This is the difference between the - * frame currently "leaving the speakers" and the last frame (or - * highest last frame across all channels) requested via - * getSamples(). The default is zero. - */ - void setTargetPlayLatency(size_t); - - /** - * Get the playback latency of the target audio device. - */ - size_t getTargetPlayLatency() const; - - /** - * Specify that the target audio device has a fixed sample rate - * (i.e. cannot accommodate arbitrary sample rates based on the - * source). If the target sets this to something other than the - * source sample rate, this class will resample automatically to - * fit. - */ - void setTargetSampleRate(size_t); - - /** - * Return the sample rate set by the target audio device (or the - * source sample rate if the target hasn't set one). - */ - virtual size_t getTargetSampleRate() const; - - /** - * Set the current output levels for metering (for call from the - * target) - */ - void setOutputLevels(float left, float right); - - /** - * Return the current (or thereabouts) output levels in the range - * 0.0 -> 1.0, for metering purposes. - */ - virtual bool getOutputLevels(float &left, float &right); - - /** - * Get the number of channels of audio that in the source models. - * This may safely be called from a realtime thread. Returns 0 if - * there is no source yet available. - */ - size_t getSourceChannelCount() const; - - /** - * Get the number of channels of audio that will be provided - * to the play target. This may be more than the source channel - * count: for example, a mono source will provide 2 channels - * after pan. - * This may safely be called from a realtime thread. Returns 0 if - * there is no source yet available. - */ - size_t getTargetChannelCount() const; - - /** - * Get the actual sample rate of the source material. This may - * safely be called from a realtime thread. Returns 0 if there is - * no source yet available. - */ - virtual size_t getSourceSampleRate() const; - - /** - * Get "count" samples (at the target sample rate) of the mixed - * audio data, in all channels. This may safely be called from a - * realtime thread. - */ - size_t getSourceSamples(size_t count, float **buffer); - - /** - * Set the time stretcher factor (i.e. playback speed). Also - * specify whether the time stretcher will be variable rate - * (sharpening transients), and whether time stretching will be - * carried out on data mixed down to mono for speed. - */ - void setTimeStretch(float factor, bool sharpen, bool mono); - - /** - * Set the resampler quality, 0 - 2 where 0 is fastest and 2 is - * highest quality. - */ - void setResampleQuality(int q); - - /** - * Set a single real-time plugin as a processing effect for - * auditioning during playback. - * - * The plugin must have been initialised with - * getTargetChannelCount() channels and a getTargetBlockSize() - * sample frame processing block size. - * - * This playback source takes ownership of the plugin, which will - * be deleted at some point after the following call to - * setAuditioningPlugin (depending on real-time constraints). - * - * Pass a null pointer to remove the current auditioning plugin, - * if any. - */ - void setAuditioningPlugin(RealTimePluginInstance *plugin); - - /** - * Specify that only the given set of models should be played. - */ - void setSoloModelSet(std::set<Model *>s); - - /** - * Specify that all models should be played as normal (if not - * muted). - */ - void clearSoloModelSet(); - -signals: - void modelReplaced(); - - void playStatusChanged(bool isPlaying); - - void sampleRateMismatch(size_t requested, size_t available, bool willResample); - - void audioOverloadPluginDisabled(); - -public slots: - void audioProcessingOverload(); - -protected slots: - void selectionChanged(); - void playLoopModeChanged(); - void playSelectionModeChanged(); - void playParametersChanged(PlayParameters *); - void preferenceChanged(PropertyContainer::PropertyName); - void modelChanged(size_t startFrame, size_t endFrame); - -protected: - ViewManager *m_viewManager; - AudioGenerator *m_audioGenerator; - - class RingBufferVector : public std::vector<RingBuffer<float> *> { - public: - virtual ~RingBufferVector() { - while (!empty()) { - delete *begin(); - erase(begin()); - } - } - }; - - std::set<Model *> m_models; - RingBufferVector *m_readBuffers; - RingBufferVector *m_writeBuffers; - size_t m_readBufferFill; - size_t m_writeBufferFill; - Scavenger<RingBufferVector> m_bufferScavenger; - size_t m_sourceChannelCount; - size_t m_blockSize; - size_t m_sourceSampleRate; - size_t m_targetSampleRate; - size_t m_playLatency; - bool m_playing; - bool m_exiting; - size_t m_lastModelEndFrame; - static const size_t m_ringBufferSize; - float m_outputLeft; - float m_outputRight; - RealTimePluginInstance *m_auditioningPlugin; - bool m_auditioningPluginBypassed; - Scavenger<RealTimePluginInstance> m_pluginScavenger; - - RingBuffer<float> *getWriteRingBuffer(size_t c) { - if (m_writeBuffers && c < m_writeBuffers->size()) { - return (*m_writeBuffers)[c]; - } else { - return 0; - } - } - - RingBuffer<float> *getReadRingBuffer(size_t c) { - RingBufferVector *rb = m_readBuffers; - if (rb && c < rb->size()) { - return (*rb)[c]; - } else { - return 0; - } - } - - void clearRingBuffers(bool haveLock = false, size_t count = 0); - void unifyRingBuffers(); - - PhaseVocoderTimeStretcher *m_timeStretcher; - Scavenger<PhaseVocoderTimeStretcher> m_timeStretcherScavenger; - - // Called from fill thread, m_playing true, mutex held - // Return true if work done - bool fillBuffers(); - - // Called from fillBuffers. Return the number of frames written, - // which will be count or fewer. Return in the frame argument the - // new buffered frame position (which may be earlier than the - // frame argument passed in, in the case of looping). - size_t mixModels(size_t &frame, size_t count, float **buffers); - - // Called from getSourceSamples. - void applyAuditioningEffect(size_t count, float **buffers); - - class FillThread : public Thread - { - public: - FillThread(AudioCallbackPlaySource &source) : - Thread(Thread::NonRTThread), - m_source(source) { } - - virtual void run(); - - protected: - AudioCallbackPlaySource &m_source; - }; - - QMutex m_mutex; - QWaitCondition m_condition; - FillThread *m_fillThread; - SRC_STATE *m_converter; - SRC_STATE *m_crapConverter; // for use when playing very fast - int m_resampleQuality; - void initialiseConverter(); -}; - -#endif - -
--- a/audioio/AudioCallbackPlayTarget.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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 "AudioCallbackPlayTarget.h" -#include "AudioCallbackPlaySource.h" - -#include <iostream> - -AudioCallbackPlayTarget::AudioCallbackPlayTarget(AudioCallbackPlaySource *source) : - m_source(source), - m_outputGain(1.0) -{ - if (m_source) { - connect(m_source, SIGNAL(modelReplaced()), - this, SLOT(sourceModelReplaced())); - } -} - -AudioCallbackPlayTarget::~AudioCallbackPlayTarget() -{ -} - -void -AudioCallbackPlayTarget::setOutputGain(float gain) -{ - m_outputGain = gain; -} -
--- a/audioio/AudioCallbackPlayTarget.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _AUDIO_CALLBACK_PLAY_TARGET_H_ -#define _AUDIO_CALLBACK_PLAY_TARGET_H_ - -#include <QObject> - -class AudioCallbackPlaySource; - -class AudioCallbackPlayTarget : public QObject -{ - Q_OBJECT - -public: - AudioCallbackPlayTarget(AudioCallbackPlaySource *source); - virtual ~AudioCallbackPlayTarget(); - - virtual bool isOK() const = 0; - - float getOutputGain() const { - return m_outputGain; - } - -public slots: - /** - * Set the playback gain (0.0 = silence, 1.0 = levels unmodified) - */ - virtual void setOutputGain(float gain); - - /** - * The main source model (providing the playback sample rate) has - * been changed. The target should query the source's sample - * rate, set its output sample rate accordingly, and call back on - * the source's setTargetSampleRate to indicate what sample rate - * it succeeded in setting at the output. If this differs from - * the model rate, the source will resample. - */ - virtual void sourceModelReplaced() = 0; - -protected: - AudioCallbackPlaySource *m_source; - float m_outputGain; -}; - -#endif -
--- a/audioio/AudioCoreAudioTarget.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifdef HAVE_COREAUDIO - -#include "AudioCoreAudioTarget.h" - - - -#endif
--- a/audioio/AudioCoreAudioTarget.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _AUDIO_CORE_AUDIO_TARGET_H_ -#define _AUDIO_CORE_AUDIO_TARGET_H_ - -#ifdef HAVE_COREAUDIO - -#include <jack/jack.h> -#include <vector> - -#include <CoreAudio/CoreAudio.h> -#include <CoreAudio/CoreAudioTypes.h> -#include <AudioUnit/AUComponent.h> -#include <AudioUnit/AudioUnitProperties.h> -#include <AudioUnit/AudioUnitParameters.h> -#include <AudioUnit/AudioOutputUnit.h> - -#include "AudioCallbackPlayTarget.h" - -class AudioCallbackPlaySource; - -class AudioCoreAudioTarget : public AudioCallbackPlayTarget -{ - Q_OBJECT - -public: - AudioCoreAudioTarget(AudioCallbackPlaySource *source); - ~AudioCoreAudioTarget(); - - virtual bool isOK() const; - -public slots: - virtual void sourceModelReplaced(); - -protected: - OSStatus process(void *data, - AudioUnitRenderActionFlags *flags, - const AudioTimeStamp *timestamp, - unsigned int inbus, - unsigned int inframes, - AudioBufferList *ioData); - - int m_bufferSize; - int m_sampleRate; - int m_latency; -}; - -#endif /* HAVE_COREAUDIO */ - -#endif -
--- a/audioio/AudioGenerator.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,799 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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 "AudioGenerator.h" - -#include "base/TempDirectory.h" -#include "base/PlayParameters.h" -#include "base/PlayParameterRepository.h" -#include "base/Pitch.h" -#include "base/Exceptions.h" - -#include "data/model/NoteModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/SparseOneDimensionalModel.h" - -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginIdentifier.h" -#include "plugin/PluginXml.h" -#include "plugin/api/alsa/seq_event.h" - -#include <iostream> -#include <math.h> - -#include <QDir> -#include <QFile> - -const size_t -AudioGenerator::m_pluginBlockSize = 2048; - -QString -AudioGenerator::m_sampleDir = ""; - -//#define DEBUG_AUDIO_GENERATOR 1 - -AudioGenerator::AudioGenerator() : - m_sourceSampleRate(0), - m_targetChannelCount(1), - m_soloing(false) -{ - connect(PlayParameterRepository::getInstance(), - SIGNAL(playPluginIdChanged(const Model *, QString)), - this, - SLOT(playPluginIdChanged(const Model *, QString))); - - connect(PlayParameterRepository::getInstance(), - SIGNAL(playPluginConfigurationChanged(const Model *, QString)), - this, - SLOT(playPluginConfigurationChanged(const Model *, QString))); -} - -AudioGenerator::~AudioGenerator() -{ -} - -bool -AudioGenerator::canPlay(const Model *model) -{ - if (dynamic_cast<const DenseTimeValueModel *>(model) || - dynamic_cast<const SparseOneDimensionalModel *>(model) || - dynamic_cast<const NoteModel *>(model)) { - return true; - } else { - return false; - } -} - -bool -AudioGenerator::addModel(Model *model) -{ - if (m_sourceSampleRate == 0) { - - m_sourceSampleRate = model->getSampleRate(); - - } else { - - DenseTimeValueModel *dtvm = - dynamic_cast<DenseTimeValueModel *>(model); - - if (dtvm) { - m_sourceSampleRate = model->getSampleRate(); - return true; - } - } - - RealTimePluginInstance *plugin = loadPluginFor(model); - if (plugin) { - QMutexLocker locker(&m_mutex); - m_synthMap[model] = plugin; - return true; - } - - return false; -} - -void -AudioGenerator::playPluginIdChanged(const Model *model, QString) -{ - if (m_synthMap.find(model) == m_synthMap.end()) return; - - RealTimePluginInstance *plugin = loadPluginFor(model); - if (plugin) { - QMutexLocker locker(&m_mutex); - delete m_synthMap[model]; - m_synthMap[model] = plugin; - } -} - -void -AudioGenerator::playPluginConfigurationChanged(const Model *model, - QString configurationXml) -{ -// std::cerr << "AudioGenerator::playPluginConfigurationChanged" << std::endl; - - if (m_synthMap.find(model) == m_synthMap.end()) { - std::cerr << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << std::endl; - return; - } - - RealTimePluginInstance *plugin = m_synthMap[model]; - if (plugin) { - PluginXml(plugin).setParametersFromXml(configurationXml); - } -} - -QString -AudioGenerator::getDefaultPlayPluginId(const Model *model) -{ - const SparseOneDimensionalModel *sodm = - dynamic_cast<const SparseOneDimensionalModel *>(model); - if (sodm) { - return QString("dssi:%1:sample_player"). - arg(PluginIdentifier::BUILTIN_PLUGIN_SONAME); - } - - const NoteModel *nm = dynamic_cast<const NoteModel *>(model); - if (nm) { - return QString("dssi:%1:sample_player"). - arg(PluginIdentifier::BUILTIN_PLUGIN_SONAME); - } - - return ""; -} - -QString -AudioGenerator::getDefaultPlayPluginConfiguration(const Model *model) -{ - QString program = ""; - - const SparseOneDimensionalModel *sodm = - dynamic_cast<const SparseOneDimensionalModel *>(model); - if (sodm) { - program = "tap"; - } - - const NoteModel *nm = dynamic_cast<const NoteModel *>(model); - if (nm) { - program = "piano"; - } - - if (program == "") return ""; - - return - QString("<plugin configuration=\"%1\" program=\"%2\"/>") - .arg(XmlExportable::encodeEntities - (QString("sampledir=%1") - .arg(PluginXml::encodeConfigurationChars(getSampleDir())))) - .arg(XmlExportable::encodeEntities(program)); -} - -QString -AudioGenerator::getSampleDir() -{ - if (m_sampleDir != "") return m_sampleDir; - - try { - m_sampleDir = TempDirectory::getInstance()->getSubDirectoryPath("samples"); - } catch (DirectoryCreationFailed f) { - std::cerr << "WARNING: AudioGenerator::getSampleDir: Failed to create " - << "temporary sample directory" << std::endl; - m_sampleDir = ""; - return ""; - } - - QDir sampleResourceDir(":/samples", "*.wav"); - - for (unsigned int i = 0; i < sampleResourceDir.count(); ++i) { - - QString fileName(sampleResourceDir[i]); - QFile file(sampleResourceDir.filePath(fileName)); - - if (!file.copy(QDir(m_sampleDir).filePath(fileName))) { - std::cerr << "WARNING: AudioGenerator::getSampleDir: " - << "Unable to copy " << fileName.toStdString() - << " into temporary directory \"" - << m_sampleDir.toStdString() << "\"" << std::endl; - } - } - - return m_sampleDir; -} - -void -AudioGenerator::setSampleDir(RealTimePluginInstance *plugin) -{ - plugin->configure("sampledir", getSampleDir().toStdString()); -} - -RealTimePluginInstance * -AudioGenerator::loadPluginFor(const Model *model) -{ - QString pluginId, configurationXml; - - PlayParameters *parameters = - PlayParameterRepository::getInstance()->getPlayParameters(model); - if (parameters) { - pluginId = parameters->getPlayPluginId(); - configurationXml = parameters->getPlayPluginConfiguration(); - } - - if (pluginId == "") { - pluginId = getDefaultPlayPluginId(model); - configurationXml = getDefaultPlayPluginConfiguration(model); - } - - if (pluginId == "") return 0; - - RealTimePluginInstance *plugin = loadPlugin(pluginId, ""); - if (!plugin) return 0; - - if (configurationXml != "") { - PluginXml(plugin).setParametersFromXml(configurationXml); - } - - if (parameters) { - parameters->setPlayPluginId(pluginId); - parameters->setPlayPluginConfiguration(configurationXml); - } - - return plugin; -} - -RealTimePluginInstance * -AudioGenerator::loadPlugin(QString pluginId, QString program) -{ - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "Failed to get plugin factory" << std::endl; - return false; - } - - RealTimePluginInstance *instance = - factory->instantiatePlugin - (pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount); - - if (!instance) { - std::cerr << "Failed to instantiate plugin " << pluginId.toStdString() << std::endl; - return 0; - } - - setSampleDir(instance); - - for (unsigned int i = 0; i < instance->getParameterCount(); ++i) { - instance->setParameterValue(i, instance->getParameterDefault(i)); - } - std::string defaultProgram = instance->getProgram(0, 0); - if (defaultProgram != "") { -// std::cerr << "first selecting default program " << defaultProgram << std::endl; - instance->selectProgram(defaultProgram); - } - if (program != "") { -// std::cerr << "now selecting desired program " << program.toStdString() << std::endl; - instance->selectProgram(program.toStdString()); - } - instance->setIdealChannelCount(m_targetChannelCount); // reset! - - return instance; -} - -void -AudioGenerator::removeModel(Model *model) -{ - SparseOneDimensionalModel *sodm = - dynamic_cast<SparseOneDimensionalModel *>(model); - if (!sodm) return; // nothing to do - - QMutexLocker locker(&m_mutex); - - if (m_synthMap.find(sodm) == m_synthMap.end()) return; - - RealTimePluginInstance *instance = m_synthMap[sodm]; - m_synthMap.erase(sodm); - delete instance; -} - -void -AudioGenerator::clearModels() -{ - QMutexLocker locker(&m_mutex); - while (!m_synthMap.empty()) { - RealTimePluginInstance *instance = m_synthMap.begin()->second; - m_synthMap.erase(m_synthMap.begin()); - delete instance; - } -} - -void -AudioGenerator::reset() -{ - QMutexLocker locker(&m_mutex); - for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { - if (i->second) { - i->second->silence(); - i->second->discardEvents(); - } - } - - m_noteOffs.clear(); -} - -void -AudioGenerator::setTargetChannelCount(size_t targetChannelCount) -{ - if (m_targetChannelCount == targetChannelCount) return; - -// std::cerr << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << std::endl; - - QMutexLocker locker(&m_mutex); - m_targetChannelCount = targetChannelCount; - - for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) { - if (i->second) i->second->setIdealChannelCount(targetChannelCount); - } -} - -size_t -AudioGenerator::getBlockSize() const -{ - return m_pluginBlockSize; -} - -void -AudioGenerator::setSoloModelSet(std::set<Model *> s) -{ - QMutexLocker locker(&m_mutex); - - m_soloModelSet = s; - m_soloing = true; -} - -void -AudioGenerator::clearSoloModelSet() -{ - QMutexLocker locker(&m_mutex); - - m_soloModelSet.clear(); - m_soloing = false; -} - -size_t -AudioGenerator::mixModel(Model *model, size_t startFrame, size_t frameCount, - float **buffer, size_t fadeIn, size_t fadeOut) -{ - if (m_sourceSampleRate == 0) { - std::cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << std::endl; - return frameCount; - } - - QMutexLocker locker(&m_mutex); - - PlayParameters *parameters = - PlayParameterRepository::getInstance()->getPlayParameters(model); - if (!parameters) return frameCount; - - bool playing = !parameters->isPlayMuted(); - if (!playing) { -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "AudioGenerator::mixModel(" << model << "): muted" << std::endl; -#endif - return frameCount; - } - - if (m_soloing) { - if (m_soloModelSet.find(model) == m_soloModelSet.end()) { -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << std::endl; -#endif - return frameCount; - } - } - - float gain = parameters->getPlayGain(); - float pan = parameters->getPlayPan(); - - DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); - if (dtvm) { - return mixDenseTimeValueModel(dtvm, startFrame, frameCount, - buffer, gain, pan, fadeIn, fadeOut); - } - - SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> - (model); - if (sodm) { - return mixSparseOneDimensionalModel(sodm, startFrame, frameCount, - buffer, gain, pan, fadeIn, fadeOut); - } - - NoteModel *nm = dynamic_cast<NoteModel *>(model); - if (nm) { - return mixNoteModel(nm, startFrame, frameCount, - buffer, gain, pan, fadeIn, fadeOut); - } - - return frameCount; -} - -size_t -AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm, - size_t startFrame, size_t frames, - float **buffer, float gain, float pan, - size_t fadeIn, size_t fadeOut) -{ - static float *channelBuffer = 0; - static size_t channelBufSiz = 0; - - size_t totalFrames = frames + fadeIn/2 + fadeOut/2; - - if (channelBufSiz < totalFrames) { - delete[] channelBuffer; - channelBuffer = new float[totalFrames]; - channelBufSiz = totalFrames; - } - - size_t got = 0; - size_t prevChannel = 999; - - for (size_t c = 0; c < m_targetChannelCount; ++c) { - - size_t sourceChannel = (c % dtvm->getChannelCount()); - -// std::cerr << "mixing channel " << c << " from source channel " << sourceChannel << std::endl; - - float channelGain = gain; - if (pan != 0.0) { - if (c == 0) { - if (pan > 0.0) channelGain *= 1.0 - pan; - } else { - if (pan < 0.0) channelGain *= pan + 1.0; - } - } - - if (prevChannel != sourceChannel) { - if (startFrame >= fadeIn/2) { - got = dtvm->getData - (sourceChannel, - startFrame - fadeIn/2, - frames + fadeOut/2 + fadeIn/2, - channelBuffer); - } else { - size_t missing = fadeIn/2 - startFrame; - got = dtvm->getData - (sourceChannel, - startFrame, - frames + fadeOut/2, - channelBuffer + missing); - } - } - prevChannel = sourceChannel; - - for (size_t i = 0; i < fadeIn/2; ++i) { - float *back = buffer[c]; - back -= fadeIn/2; - back[i] += (channelGain * channelBuffer[i] * i) / fadeIn; - } - - for (size_t i = 0; i < frames + fadeOut/2; ++i) { - float mult = channelGain; - if (i < fadeIn/2) { - mult = (mult * i) / fadeIn; - } - if (i > frames - fadeOut/2) { - mult = (mult * ((frames + fadeOut/2) - i)) / fadeOut; - } - buffer[c][i] += mult * channelBuffer[i]; - } - } - - return got; -} - -size_t -AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm, - size_t startFrame, size_t frames, - float **buffer, float gain, float pan, - size_t /* fadeIn */, - size_t /* fadeOut */) -{ - RealTimePluginInstance *plugin = m_synthMap[sodm]; - if (!plugin) return 0; - - size_t latency = plugin->getLatency(); - size_t blocks = frames / m_pluginBlockSize; - - //!!! hang on -- the fact that the audio callback play source's - //buffer is a multiple of the plugin's buffer size doesn't mean - //that we always get called for a multiple of it here (because it - //also depends on the JACK block size). how should we ensure that - //all models write the same amount in to the mix, and that we - //always have a multiple of the plugin buffer size? I guess this - //class has to be queryable for the plugin buffer size & the - //callback play source has to use that as a multiple for all the - //calls to mixModel - - size_t got = blocks * m_pluginBlockSize; - -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "mixModel [sparse]: frames " << frames - << ", blocks " << blocks << std::endl; -#endif - - snd_seq_event_t onEv; - onEv.type = SND_SEQ_EVENT_NOTEON; - onEv.data.note.channel = 0; - onEv.data.note.note = 64; - onEv.data.note.velocity = 100; - - snd_seq_event_t offEv; - offEv.type = SND_SEQ_EVENT_NOTEOFF; - offEv.data.note.channel = 0; - offEv.data.note.velocity = 0; - - NoteOffSet ¬eOffs = m_noteOffs[sodm]; - - for (size_t i = 0; i < blocks; ++i) { - - size_t reqStart = startFrame + i * m_pluginBlockSize; - - SparseOneDimensionalModel::PointList points = - sodm->getPoints(reqStart + latency, - reqStart + latency + m_pluginBlockSize); - - Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime - (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); - - for (SparseOneDimensionalModel::PointList::iterator pli = - points.begin(); pli != points.end(); ++pli) { - - size_t pliFrame = pli->frame; - - if (pliFrame >= latency) pliFrame -= latency; - - if (pliFrame < reqStart || - pliFrame >= reqStart + m_pluginBlockSize) continue; - - while (noteOffs.begin() != noteOffs.end() && - noteOffs.begin()->frame <= pliFrame) { - - Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime - (noteOffs.begin()->frame, m_sourceSampleRate); - - offEv.data.note.note = noteOffs.begin()->pitch; - -#ifdef DEBUG_AUDIO_GENERATOR - std::cerr << "mixModel [sparse]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl; -#endif - - plugin->sendEvent(eventTime, &offEv); - noteOffs.erase(noteOffs.begin()); - } - - Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime - (pliFrame, m_sourceSampleRate); - - plugin->sendEvent(eventTime, &onEv); - -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; -#endif - - size_t duration = 7000; // frames [for now] - NoteOff noff; - noff.pitch = onEv.data.note.note; - noff.frame = pliFrame + duration; - noteOffs.insert(noff); - } - - while (noteOffs.begin() != noteOffs.end() && - noteOffs.begin()->frame <= - startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { - - Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime - (noteOffs.begin()->frame, m_sourceSampleRate); - - offEv.data.note.note = noteOffs.begin()->pitch; - -#ifdef DEBUG_AUDIO_GENERATOR - std::cerr << "mixModel [sparse]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl; -#endif - - plugin->sendEvent(eventTime, &offEv); - noteOffs.erase(noteOffs.begin()); - } - - plugin->run(blockTime); - float **outs = plugin->getAudioOutputBuffers(); - - for (size_t c = 0; c < m_targetChannelCount; ++c) { -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "mixModel [sparse]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; -#endif - - size_t sourceChannel = (c % plugin->getAudioOutputCount()); - - float channelGain = gain; - if (pan != 0.0) { - if (c == 0) { - if (pan > 0.0) channelGain *= 1.0 - pan; - } else { - if (pan < 0.0) channelGain *= pan + 1.0; - } - } - - for (size_t j = 0; j < m_pluginBlockSize; ++j) { - buffer[c][i * m_pluginBlockSize + j] += - channelGain * outs[sourceChannel][j]; - } - } - } - - return got; -} - - -//!!! mucho duplication with above -- refactor -size_t -AudioGenerator::mixNoteModel(NoteModel *nm, - size_t startFrame, size_t frames, - float **buffer, float gain, float pan, - size_t /* fadeIn */, - size_t /* fadeOut */) -{ - RealTimePluginInstance *plugin = m_synthMap[nm]; - if (!plugin) return 0; - - size_t latency = plugin->getLatency(); - size_t blocks = frames / m_pluginBlockSize; - - //!!! hang on -- the fact that the audio callback play source's - //buffer is a multiple of the plugin's buffer size doesn't mean - //that we always get called for a multiple of it here (because it - //also depends on the JACK block size). how should we ensure that - //all models write the same amount in to the mix, and that we - //always have a multiple of the plugin buffer size? I guess this - //class has to be queryable for the plugin buffer size & the - //callback play source has to use that as a multiple for all the - //calls to mixModel - - size_t got = blocks * m_pluginBlockSize; - -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "mixModel [note]: frames " << frames - << ", blocks " << blocks << std::endl; -#endif - - snd_seq_event_t onEv; - onEv.type = SND_SEQ_EVENT_NOTEON; - onEv.data.note.channel = 0; - onEv.data.note.note = 64; - onEv.data.note.velocity = 100; - - snd_seq_event_t offEv; - offEv.type = SND_SEQ_EVENT_NOTEOFF; - offEv.data.note.channel = 0; - offEv.data.note.velocity = 0; - - NoteOffSet ¬eOffs = m_noteOffs[nm]; - - for (size_t i = 0; i < blocks; ++i) { - - size_t reqStart = startFrame + i * m_pluginBlockSize; - - NoteModel::PointList points = - nm->getPoints(reqStart + latency, - reqStart + latency + m_pluginBlockSize); - - Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime - (startFrame + i * m_pluginBlockSize, m_sourceSampleRate); - - for (NoteModel::PointList::iterator pli = - points.begin(); pli != points.end(); ++pli) { - - size_t pliFrame = pli->frame; - - if (pliFrame >= latency) pliFrame -= latency; - - if (pliFrame < reqStart || - pliFrame >= reqStart + m_pluginBlockSize) continue; - - while (noteOffs.begin() != noteOffs.end() && - noteOffs.begin()->frame <= pliFrame) { - - Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime - (noteOffs.begin()->frame, m_sourceSampleRate); - - offEv.data.note.note = noteOffs.begin()->pitch; - -#ifdef DEBUG_AUDIO_GENERATOR - std::cerr << "mixModel [note]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl; -#endif - - plugin->sendEvent(eventTime, &offEv); - noteOffs.erase(noteOffs.begin()); - } - - Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime - (pliFrame, m_sourceSampleRate); - - if (nm->getScaleUnits() == "Hz") { - onEv.data.note.note = Pitch::getPitchForFrequency(pli->value); - } else { - onEv.data.note.note = lrintf(pli->value); - } - - plugin->sendEvent(eventTime, &onEv); - -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "mixModel [note]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl; -#endif - - size_t duration = pli->duration; - if (duration == 0 || duration == 1) { - duration = m_sourceSampleRate / 20; - } - NoteOff noff; - noff.pitch = onEv.data.note.note; - noff.frame = pliFrame + duration; - noteOffs.insert(noff); - } - - while (noteOffs.begin() != noteOffs.end() && - noteOffs.begin()->frame <= - startFrame + i * m_pluginBlockSize + m_pluginBlockSize) { - - Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime - (noteOffs.begin()->frame, m_sourceSampleRate); - - offEv.data.note.note = noteOffs.begin()->pitch; - -#ifdef DEBUG_AUDIO_GENERATOR - std::cerr << "mixModel [note]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl; -#endif - - plugin->sendEvent(eventTime, &offEv); - noteOffs.erase(noteOffs.begin()); - } - - plugin->run(blockTime); - float **outs = plugin->getAudioOutputBuffers(); - - for (size_t c = 0; c < m_targetChannelCount; ++c) { -#ifdef DEBUG_AUDIO_GENERATOR - std::cout << "mixModel [note]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl; -#endif - - size_t sourceChannel = (c % plugin->getAudioOutputCount()); - - float channelGain = gain; - if (pan != 0.0) { - if (c == 0) { - if (pan > 0.0) channelGain *= 1.0 - pan; - } else { - if (pan < 0.0) channelGain *= pan + 1.0; - } - } - - for (size_t j = 0; j < m_pluginBlockSize; ++j) { - buffer[c][i * m_pluginBlockSize + j] += - channelGain * outs[sourceChannel][j]; - } - } - } - - return got; -} -
--- a/audioio/AudioGenerator.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,158 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _AUDIO_GENERATOR_H_ -#define _AUDIO_GENERATOR_H_ - -class Model; -class NoteModel; -class DenseTimeValueModel; -class SparseOneDimensionalModel; -class RealTimePluginInstance; - -#include <QObject> -#include <QMutex> - -#include <set> -#include <map> - -class AudioGenerator : public QObject -{ - Q_OBJECT - -public: - AudioGenerator(); - virtual ~AudioGenerator(); - - /** - * Return true if the given model is of a type that we generally - * know how to play. This doesn't guarantee that a specific - * AudioGenerator will actually produce sounds for it (for - * example, it may turn out that a vital plugin is missing). - */ - static bool canPlay(const Model *model); - - static QString getDefaultPlayPluginId(const Model *model); - static QString getDefaultPlayPluginConfiguration(const Model *model); - - /** - * Add a data model to be played from and initialise any necessary - * audio generation code. Returns true if the model will be - * played. (The return value test here is stricter than that for - * canPlay, above.) The model will be added regardless of the - * return value. - */ - virtual bool addModel(Model *model); - - /** - * Remove a model. - */ - virtual void removeModel(Model *model); - - /** - * Remove all models. - */ - virtual void clearModels(); - - /** - * Reset playback, clearing plugins and the like. - */ - virtual void reset(); - - /** - * Set the target channel count. The buffer parameter to mixModel - * must always point to at least this number of arrays. - */ - virtual void setTargetChannelCount(size_t channelCount); - - /** - * Return the internal processing block size. The frameCount - * argument to all mixModel calls must be a multiple of this - * value. - */ - virtual size_t getBlockSize() const; - - /** - * Mix a single model into an output buffer. - */ - virtual size_t mixModel(Model *model, size_t startFrame, size_t frameCount, - float **buffer, size_t fadeIn = 0, size_t fadeOut = 0); - - /** - * Specify that only the given set of models should be played. - */ - virtual void setSoloModelSet(std::set<Model *>s); - - /** - * Specify that all models should be played as normal (if not - * muted). - */ - virtual void clearSoloModelSet(); - -protected slots: - void playPluginIdChanged(const Model *, QString); - void playPluginConfigurationChanged(const Model *, QString); - -protected: - size_t m_sourceSampleRate; - size_t m_targetChannelCount; - - bool m_soloing; - std::set<Model *> m_soloModelSet; - - struct NoteOff { - - int pitch; - size_t frame; - - struct Comparator { - bool operator()(const NoteOff &n1, const NoteOff &n2) const { - return n1.frame < n2.frame; - } - }; - }; - - typedef std::map<const Model *, RealTimePluginInstance *> PluginMap; - - typedef std::set<NoteOff, NoteOff::Comparator> NoteOffSet; - typedef std::map<const Model *, NoteOffSet> NoteOffMap; - - QMutex m_mutex; - PluginMap m_synthMap; - NoteOffMap m_noteOffs; - static QString m_sampleDir; - - virtual RealTimePluginInstance *loadPluginFor(const Model *model); - virtual RealTimePluginInstance *loadPlugin(QString id, QString program); - static QString getSampleDir(); - static void setSampleDir(RealTimePluginInstance *plugin); - - virtual size_t mixDenseTimeValueModel - (DenseTimeValueModel *model, size_t startFrame, size_t frameCount, - float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut); - - virtual size_t mixSparseOneDimensionalModel - (SparseOneDimensionalModel *model, size_t startFrame, size_t frameCount, - float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut); - - virtual size_t mixNoteModel - (NoteModel *model, size_t startFrame, size_t frameCount, - float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut); - - static const size_t m_pluginBlockSize; -}; - -#endif -
--- a/audioio/AudioJACKTarget.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,402 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifdef HAVE_JACK - -#include "AudioJACKTarget.h" -#include "AudioCallbackPlaySource.h" - -#include <iostream> -#include <cmath> - -//#define DEBUG_AUDIO_JACK_TARGET 1 - -#ifdef BUILD_STATIC -#ifdef Q_OS_LINUX - -// Some lunacy to enable JACK support in static builds. JACK isn't -// supposed to be linked statically, because it depends on a -// consistent shared memory layout between client library and daemon, -// so it's very fragile in the face of version mismatches. -// -// Therefore for static builds on Linux we avoid linking against JACK -// at all during the build, instead using dlopen and runtime symbol -// lookup to switch on JACK support at runtime. The following big -// mess (down to the #endifs) is the code that implements this. - -static void *symbol(const char *name) -{ - static bool attempted = false; - static void *library = 0; - static std::map<const char *, void *> symbols; - if (symbols.find(name) != symbols.end()) return symbols[name]; - if (!library) { - if (!attempted) { - library = ::dlopen("libjack.so.1", RTLD_NOW); - if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW); - if (!library) library = ::dlopen("libjack.so", RTLD_NOW); - if (!library) { - std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: " - << ::dlerror() << " (tried .so, .so.0, .so.1)" - << std::endl; - } - attempted = true; - } - if (!library) return 0; - } - void *symbol = ::dlsym(library, name); - if (!symbol) { - std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol " - << name << ": " << ::dlerror() << std::endl; - } - symbols[name] = symbol; - return symbol; -} - -static int dynamic_jack_set_process_callback(jack_client_t *client, - JackProcessCallback process_callback, - void *arg) -{ - typedef int (*func)(jack_client_t *client, - JackProcessCallback process_callback, - void *arg); - void *s = symbol("jack_set_process_callback"); - if (!s) return 1; - func f = (func)s; - return f(client, process_callback, arg); -} - -static int dynamic_jack_set_xrun_callback(jack_client_t *client, - JackXRunCallback xrun_callback, - void *arg) -{ - typedef int (*func)(jack_client_t *client, - JackXRunCallback xrun_callback, - void *arg); - void *s = symbol("jack_set_xrun_callback"); - if (!s) return 1; - func f = (func)s; - return f(client, xrun_callback, arg); -} - -static const char **dynamic_jack_get_ports(jack_client_t *client, - const char *port_name_pattern, - const char *type_name_pattern, - unsigned long flags) -{ - typedef const char **(*func)(jack_client_t *client, - const char *port_name_pattern, - const char *type_name_pattern, - unsigned long flags); - void *s = symbol("jack_get_ports"); - if (!s) return 0; - func f = (func)s; - return f(client, port_name_pattern, type_name_pattern, flags); -} - -static jack_port_t *dynamic_jack_port_register(jack_client_t *client, - const char *port_name, - const char *port_type, - unsigned long flags, - unsigned long buffer_size) -{ - typedef jack_port_t *(*func)(jack_client_t *client, - const char *port_name, - const char *port_type, - unsigned long flags, - unsigned long buffer_size); - void *s = symbol("jack_port_register"); - if (!s) return 0; - func f = (func)s; - return f(client, port_name, port_type, flags, buffer_size); -} - -static int dynamic_jack_connect(jack_client_t *client, - const char *source, - const char *dest) -{ - typedef int (*func)(jack_client_t *client, - const char *source, - const char *dest); - void *s = symbol("jack_connect"); - if (!s) return 1; - func f = (func)s; - return f(client, source, dest); -} - -static void *dynamic_jack_port_get_buffer(jack_port_t *port, - jack_nframes_t sz) -{ - typedef void *(*func)(jack_port_t *, jack_nframes_t); - void *s = symbol("jack_port_get_buffer"); - if (!s) return 0; - func f = (func)s; - return f(port, sz); -} - -static int dynamic_jack_port_unregister(jack_client_t *client, - jack_port_t *port) -{ - typedef int(*func)(jack_client_t *, jack_port_t *); - void *s = symbol("jack_port_unregister"); - if (!s) return 0; - func f = (func)s; - return f(client, port); -} - -#define dynamic1(rv, name, argtype, failval) \ - static rv dynamic_##name(argtype arg) { \ - typedef rv (*func) (argtype); \ - void *s = symbol(#name); \ - if (!s) return failval; \ - func f = (func) s; \ - return f(arg); \ - } - -dynamic1(jack_client_t *, jack_client_new, const char *, 0); -dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0); -dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0); -dynamic1(int, jack_activate, jack_client_t *, 1); -dynamic1(int, jack_deactivate, jack_client_t *, 1); -dynamic1(int, jack_client_close, jack_client_t *, 1); -dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0); -dynamic1(const char *, jack_port_name, const jack_port_t *, 0); - -#define jack_client_new dynamic_jack_client_new -#define jack_get_buffer_size dynamic_jack_get_buffer_size -#define jack_get_sample_rate dynamic_jack_get_sample_rate -#define jack_set_process_callback dynamic_jack_set_process_callback -#define jack_set_xrun_callback dynamic_jack_set_xrun_callback -#define jack_activate dynamic_jack_activate -#define jack_deactivate dynamic_jack_deactivate -#define jack_client_close dynamic_jack_client_close -#define jack_get_ports dynamic_jack_get_ports -#define jack_port_register dynamic_jack_port_register -#define jack_port_unregister dynamic_jack_port_unregister -#define jack_port_get_latency dynamic_jack_port_get_latency -#define jack_port_name dynamic_jack_port_name -#define jack_connect dynamic_jack_connect -#define jack_port_get_buffer dynamic_jack_port_get_buffer - -#endif -#endif - -AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) : - AudioCallbackPlayTarget(source), - m_client(0), - m_bufferSize(0), - m_sampleRate(0) -{ - char name[100]; - strcpy(name, "Sonic Visualiser"); - m_client = jack_client_new(name); - - if (!m_client) { - sprintf(name, "Sonic Visualiser (%d)", (int)getpid()); - m_client = jack_client_new(name); - if (!m_client) { - std::cerr - << "ERROR: AudioJACKTarget: Failed to connect to JACK server" - << std::endl; - } - } - - if (!m_client) return; - - m_bufferSize = jack_get_buffer_size(m_client); - m_sampleRate = jack_get_sample_rate(m_client); - - jack_set_xrun_callback(m_client, xrunStatic, this); - jack_set_process_callback(m_client, processStatic, this); - - if (jack_activate(m_client)) { - std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client" - << std::endl; - } - - if (m_source) { - sourceModelReplaced(); - } -} - -AudioJACKTarget::~AudioJACKTarget() -{ - std::cerr << "AudioJACKTarget::~AudioJACKTarget()" << std::endl; - if (m_client) { - jack_deactivate(m_client); - jack_client_close(m_client); - } - std::cerr << "AudioJACKTarget::~AudioJACKTarget() done" << std::endl; -} - -bool -AudioJACKTarget::isOK() const -{ - return (m_client != 0); -} - -int -AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg) -{ - return ((AudioJACKTarget *)arg)->process(nframes); -} - -int -AudioJACKTarget::xrunStatic(void *arg) -{ - return ((AudioJACKTarget *)arg)->xrun(); -} - -void -AudioJACKTarget::sourceModelReplaced() -{ - m_mutex.lock(); - - m_source->setTargetBlockSize(m_bufferSize); - m_source->setTargetSampleRate(m_sampleRate); - - size_t channels = m_source->getSourceChannelCount(); - - // Because we offer pan, we always want at least 2 channels - if (channels < 2) channels = 2; - - if (channels == m_outputs.size() || !m_client) { - m_mutex.unlock(); - return; - } - - const char **ports = - jack_get_ports(m_client, NULL, NULL, - JackPortIsPhysical | JackPortIsInput); - size_t physicalPortCount = 0; - while (ports[physicalPortCount]) ++physicalPortCount; - -#ifdef DEBUG_AUDIO_JACK_TARGET - std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl; -#endif - - while (m_outputs.size() < channels) { - - char name[20]; - jack_port_t *port; - - sprintf(name, "out %d", m_outputs.size() + 1); - - port = jack_port_register(m_client, - name, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, - 0); - - if (!port) { - std::cerr - << "ERROR: AudioJACKTarget: Failed to create JACK output port " - << m_outputs.size() << std::endl; - } else { - m_source->setTargetPlayLatency(jack_port_get_latency(port)); - } - - if (m_outputs.size() < physicalPortCount) { - jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]); - } - - m_outputs.push_back(port); - } - - while (m_outputs.size() > channels) { - std::vector<jack_port_t *>::iterator itr = m_outputs.end(); - --itr; - jack_port_t *port = *itr; - if (port) jack_port_unregister(m_client, port); - m_outputs.erase(itr); - } - - m_mutex.unlock(); -} - -int -AudioJACKTarget::process(jack_nframes_t nframes) -{ - if (!m_mutex.tryLock()) { - return 0; - } - - if (m_outputs.empty()) { - m_mutex.unlock(); - return 0; - } - -#ifdef DEBUG_AUDIO_JACK_TARGET - std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl; -#endif - -#ifdef DEBUG_AUDIO_JACK_TARGET - if (m_bufferSize != nframes) { - std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl; - } -#endif - - float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *)); - - for (size_t ch = 0; ch < m_outputs.size(); ++ch) { - buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes); - } - - size_t received = 0; - - if (m_source) { - received = m_source->getSourceSamples(nframes, buffers); - } - - for (size_t ch = 0; ch < m_outputs.size(); ++ch) { - for (size_t i = received; i < nframes; ++i) { - buffers[ch][i] = 0.0; - } - } - - float peakLeft = 0.0, peakRight = 0.0; - - for (size_t ch = 0; ch < m_outputs.size(); ++ch) { - - float peak = 0.0; - - for (size_t i = 0; i < nframes; ++i) { - buffers[ch][i] *= m_outputGain; - float sample = fabsf(buffers[ch][i]); - if (sample > peak) peak = sample; - } - - if (ch == 0) peakLeft = peak; - if (ch > 0 || m_outputs.size() == 1) peakRight = peak; - } - - if (m_source) { - m_source->setOutputLevels(peakLeft, peakRight); - } - - m_mutex.unlock(); - return 0; -} - -int -AudioJACKTarget::xrun() -{ - std::cerr << "AudioJACKTarget: xrun!" << std::endl; - if (m_source) m_source->audioProcessingOverload(); - return 0; -} - -#endif /* HAVE_JACK */ -
--- a/audioio/AudioJACKTarget.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _AUDIO_JACK_TARGET_H_ -#define _AUDIO_JACK_TARGET_H_ - -#ifdef HAVE_JACK - -#include <jack/jack.h> -#include <vector> - -#include "AudioCallbackPlayTarget.h" - -#include <QMutex> - -class AudioCallbackPlaySource; - -class AudioJACKTarget : public AudioCallbackPlayTarget -{ - Q_OBJECT - -public: - AudioJACKTarget(AudioCallbackPlaySource *source); - virtual ~AudioJACKTarget(); - - virtual bool isOK() const; - -public slots: - virtual void sourceModelReplaced(); - -protected: - int process(jack_nframes_t nframes); - int xrun(); - - static int processStatic(jack_nframes_t, void *); - static int xrunStatic(void *); - - jack_client_t *m_client; - std::vector<jack_port_t *> m_outputs; - jack_nframes_t m_bufferSize; - jack_nframes_t m_sampleRate; - QMutex m_mutex; -}; - -#endif /* HAVE_JACK */ - -#endif -
--- a/audioio/AudioPortAudioTarget.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,254 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifdef HAVE_PORTAUDIO - -#include "AudioPortAudioTarget.h" -#include "AudioCallbackPlaySource.h" - -#include <iostream> -#include <cassert> -#include <cmath> - -//#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1 - -AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) : - AudioCallbackPlayTarget(source), - m_stream(0), - m_bufferSize(0), - m_sampleRate(0), - m_latency(0) -{ - PaError err; - -#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET -#ifdef HAVE_PORTAUDIO_V18 - std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v18" << std::endl; -#else - std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl; -#endif -#endif - - err = Pa_Initialize(); - if (err != paNoError) { - std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl; - return; - } - - m_bufferSize = 1024; - m_sampleRate = 44100; - if (m_source && (m_source->getSourceSampleRate() != 0)) { - m_sampleRate = m_source->getSourceSampleRate(); - } - -#ifdef HAVE_PORTAUDIO_V18 - m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize; -#endif - -#ifdef HAVE_PORTAUDIO_V18 - err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32, - m_sampleRate, m_bufferSize, 0, - processStatic, this); -#else - err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32, - m_sampleRate, m_bufferSize, - processStatic, this); -#endif - - if (err != paNoError) { - std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl; - m_stream = 0; - Pa_Terminate(); - return; - } - -#ifndef HAVE_PORTAUDIO_V18 - const PaStreamInfo *info = Pa_GetStreamInfo(m_stream); - m_latency = int(info->outputLatency * m_sampleRate + 0.001); -#endif - - std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl; - - err = Pa_StartStream(m_stream); - - if (err != paNoError) { - std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl; - Pa_CloseStream(m_stream); - m_stream = 0; - Pa_Terminate(); - return; - } - - if (m_source) { - std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl; - m_source->setTargetBlockSize(m_bufferSize); - m_source->setTargetSampleRate(m_sampleRate); - m_source->setTargetPlayLatency(m_latency); - } - -#ifdef DEBUG_PORT_AUDIO_TARGET - std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl; -#endif -} - -AudioPortAudioTarget::~AudioPortAudioTarget() -{ - if (m_stream) { - PaError err; - err = Pa_CloseStream(m_stream); - if (err != paNoError) { - std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl; - } - err = Pa_Terminate(); - if (err != paNoError) { - std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl; - } - } -} - -bool -AudioPortAudioTarget::isOK() const -{ - return (m_stream != 0); -} - -#ifdef HAVE_PORTAUDIO_V18 -int -AudioPortAudioTarget::processStatic(void *input, void *output, - unsigned long nframes, - PaTimestamp outTime, void *data) -{ - return ((AudioPortAudioTarget *)data)->process(input, output, - nframes, outTime); -} -#else -int -AudioPortAudioTarget::processStatic(const void *input, void *output, - unsigned long nframes, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags flags, void *data) -{ - return ((AudioPortAudioTarget *)data)->process(input, output, - nframes, timeInfo, - flags); -} -#endif - -void -AudioPortAudioTarget::sourceModelReplaced() -{ - m_source->setTargetSampleRate(m_sampleRate); -} - -#ifdef HAVE_PORTAUDIO_V18 -int -AudioPortAudioTarget::process(void *inputBuffer, void *outputBuffer, - unsigned long nframes, - PaTimestamp) -#else -int -AudioPortAudioTarget::process(const void *, void *outputBuffer, - unsigned long nframes, - const PaStreamCallbackTimeInfo *, - PaStreamCallbackFlags) -#endif -{ -#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET - std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl; -#endif - - if (!m_source) return 0; - - float *output = (float *)outputBuffer; - - assert(nframes <= m_bufferSize); - - static float **tmpbuf = 0; - static size_t tmpbufch = 0; - static size_t tmpbufsz = 0; - - size_t sourceChannels = m_source->getSourceChannelCount(); - - // Because we offer pan, we always want at least 2 channels - if (sourceChannels < 2) sourceChannels = 2; - - if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) { - - if (tmpbuf) { - for (size_t i = 0; i < tmpbufch; ++i) { - delete[] tmpbuf[i]; - } - delete[] tmpbuf; - } - - tmpbufch = sourceChannels; - tmpbufsz = m_bufferSize; - tmpbuf = new float *[tmpbufch]; - - for (size_t i = 0; i < tmpbufch; ++i) { - tmpbuf[i] = new float[tmpbufsz]; - } - } - - size_t received = m_source->getSourceSamples(nframes, tmpbuf); - - float peakLeft = 0.0, peakRight = 0.0; - - for (size_t ch = 0; ch < 2; ++ch) { - - float peak = 0.0; - - if (ch < sourceChannels) { - - // PortAudio samples are interleaved - for (size_t i = 0; i < nframes; ++i) { - if (i < received) { - output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain; - float sample = fabsf(output[i * 2 + ch]); - if (sample > peak) peak = sample; - } else { - output[i * 2 + ch] = 0; - } - } - - } else if (ch == 1 && sourceChannels == 1) { - - for (size_t i = 0; i < nframes; ++i) { - if (i < received) { - output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain; - float sample = fabsf(output[i * 2 + ch]); - if (sample > peak) peak = sample; - } else { - output[i * 2 + ch] = 0; - } - } - - } else { - for (size_t i = 0; i < nframes; ++i) { - output[i * 2 + ch] = 0; - } - } - - if (ch == 0) peakLeft = peak; - if (ch > 0 || sourceChannels == 1) peakRight = peak; - } - - m_source->setOutputLevels(peakLeft, peakRight); - - return 0; -} - -#endif /* HAVE_PORTAUDIO */ -
--- a/audioio/AudioPortAudioTarget.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _AUDIO_PORT_AUDIO_TARGET_H_ -#define _AUDIO_PORT_AUDIO_TARGET_H_ - -#ifdef HAVE_PORTAUDIO - -// This code can be compiled for either PortAudio v18 or v19. -// PortAudio v19 is the default. If you want to use v18, define -// the preprocessor symbol HAVE_PORTAUDIO_v18. - -#include <portaudio.h> -#include <vector> - -#include "AudioCallbackPlayTarget.h" - -class AudioCallbackPlaySource; - -class AudioPortAudioTarget : public AudioCallbackPlayTarget -{ - Q_OBJECT - -public: - AudioPortAudioTarget(AudioCallbackPlaySource *source); - virtual ~AudioPortAudioTarget(); - - virtual bool isOK() const; - -public slots: - virtual void sourceModelReplaced(); - -protected: -#ifdef HAVE_PORTAUDIO_V18 - - int process(void *input, void *output, unsigned long frames, - PaTimestamp outTime); - - static int processStatic(void *, void *, unsigned long, - PaTimestamp, void *); - - PortAudioStream *m_stream; - -#else - - int process(const void *input, void *output, unsigned long frames, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags); - - static int processStatic(const void *, void *, unsigned long, - const PaStreamCallbackTimeInfo *, - PaStreamCallbackFlags, void *); - - PaStream *m_stream; - -#endif - - int m_bufferSize; - int m_sampleRate; - int m_latency; -}; - -#endif /* HAVE_PORTAUDIO */ - -#endif -
--- a/audioio/AudioTargetFactory.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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 "AudioTargetFactory.h" - -#include "AudioJACKTarget.h" -#include "AudioCoreAudioTarget.h" -#include "AudioPortAudioTarget.h" - -#include <iostream> - -AudioCallbackPlayTarget * -AudioTargetFactory::createCallbackTarget(AudioCallbackPlaySource *source) -{ - AudioCallbackPlayTarget *target = 0; - -#ifdef HAVE_JACK - target = new AudioJACKTarget(source); - if (target->isOK()) return target; - else { - std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open JACK target" << std::endl; - delete target; - } -#endif - -#ifdef HAVE_COREAUDIO - target = new AudioCoreAudioTarget(source); - if (target->isOK()) return target; - else { - std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open CoreAudio target" << std::endl; - delete target; - } -#endif - -#ifdef HAVE_DIRECTSOUND - target = new AudioDirectSoundTarget(source); - if (target->isOK()) return target; - else { - std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open DirectSound target" << std::endl; - delete target; - } -#endif - -#ifdef HAVE_PORTAUDIO - target = new AudioPortAudioTarget(source); - if (target->isOK()) return target; - else { - std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open PortAudio target" << std::endl; - delete target; - } -#endif - - std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: No suitable targets available" << std::endl; - return 0; -} - -
--- a/audioio/AudioTargetFactory.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _AUDIO_TARGET_FACTORY_H_ -#define _AUDIO_TARGET_FACTORY_H_ - -class AudioCallbackPlaySource; -class AudioCallbackPlayTarget; - -class AudioTargetFactory -{ -public: - static AudioCallbackPlayTarget *createCallbackTarget(AudioCallbackPlaySource *); -}; - -#endif -
--- a/audioio/PhaseVocoderTimeStretcher.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,626 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "PhaseVocoderTimeStretcher.h" - -#include <iostream> -#include <cassert> - -#include <QMutexLocker> - -//#define DEBUG_PHASE_VOCODER_TIME_STRETCHER 1 - -PhaseVocoderTimeStretcher::PhaseVocoderTimeStretcher(size_t sampleRate, - size_t channels, - float ratio, - bool sharpen, - size_t maxOutputBlockSize) : - m_sampleRate(sampleRate), - m_channels(channels), - m_maxOutputBlockSize(maxOutputBlockSize), - m_ratio(ratio), - m_sharpen(sharpen), - m_totalCount(0), - m_transientCount(0), - m_n2sum(0), - m_mutex(new QMutex()) -{ - initialise(); -} - -PhaseVocoderTimeStretcher::~PhaseVocoderTimeStretcher() -{ - std::cerr << "PhaseVocoderTimeStretcher::~PhaseVocoderTimeStretcher" << std::endl; - - cleanup(); - - delete m_mutex; -} - -void -PhaseVocoderTimeStretcher::initialise() -{ - std::cerr << "PhaseVocoderTimeStretcher::initialise" << std::endl; - - calculateParameters(); - - m_analysisWindow = new Window<float>(HanningWindow, m_wlen); - m_synthesisWindow = new Window<float>(HanningWindow, m_wlen); - - m_prevPhase = new float *[m_channels]; - m_prevAdjustedPhase = new float *[m_channels]; - - m_prevTransientMag = (float *)fftf_malloc(sizeof(float) * (m_wlen / 2 + 1)); - m_prevTransientScore = 0; - m_prevTransient = false; - - m_tempbuf = (float *)fftf_malloc(sizeof(float) * m_wlen); - - m_time = new float *[m_channels]; - m_freq = new fftf_complex *[m_channels]; - m_plan = new fftf_plan[m_channels]; - m_iplan = new fftf_plan[m_channels]; - - m_inbuf = new RingBuffer<float> *[m_channels]; - m_outbuf = new RingBuffer<float> *[m_channels]; - m_mashbuf = new float *[m_channels]; - - m_modulationbuf = (float *)fftf_malloc(sizeof(float) * m_wlen); - - for (size_t c = 0; c < m_channels; ++c) { - - m_prevPhase[c] = (float *)fftf_malloc(sizeof(float) * (m_wlen / 2 + 1)); - m_prevAdjustedPhase[c] = (float *)fftf_malloc(sizeof(float) * (m_wlen / 2 + 1)); - - m_time[c] = (float *)fftf_malloc(sizeof(float) * m_wlen); - m_freq[c] = (fftf_complex *)fftf_malloc(sizeof(fftf_complex) * - (m_wlen / 2 + 1)); - - m_plan[c] = fftf_plan_dft_r2c_1d(m_wlen, m_time[c], m_freq[c], FFTW_MEASURE); - m_iplan[c] = fftf_plan_dft_c2r_1d(m_wlen, m_freq[c], m_time[c], FFTW_MEASURE); - - m_outbuf[c] = new RingBuffer<float> - ((m_maxOutputBlockSize + m_wlen) * 2); - m_inbuf[c] = new RingBuffer<float> - (lrintf(m_outbuf[c]->getSize() / m_ratio) + m_wlen); - - std::cerr << "making inbuf size " << m_inbuf[c]->getSize() << " (outbuf size is " << m_outbuf[c]->getSize() << ", ratio " << m_ratio << ")" << std::endl; - - - m_mashbuf[c] = (float *)fftf_malloc(sizeof(float) * m_wlen); - - for (size_t i = 0; i < m_wlen; ++i) { - m_mashbuf[c][i] = 0.0; - } - - for (size_t i = 0; i <= m_wlen/2; ++i) { - m_prevPhase[c][i] = 0.0; - m_prevAdjustedPhase[c][i] = 0.0; - } - } - - for (size_t i = 0; i < m_wlen; ++i) { - m_modulationbuf[i] = 0.0; - } - - for (size_t i = 0; i <= m_wlen/2; ++i) { - m_prevTransientMag[i] = 0.0; - } -} - -void -PhaseVocoderTimeStretcher::calculateParameters() -{ - std::cerr << "PhaseVocoderTimeStretcher::calculateParameters" << std::endl; - - m_wlen = 1024; - - //!!! In transient sharpening mode, we need to pick the window - //length so as to be more or less fixed in audio duration (i.e. we - //need to exploit the sample rate) - - //!!! have to work out the relationship between wlen and transient - //threshold - - if (m_ratio < 1) { - if (m_ratio < 0.4) { - m_n1 = 1024; - m_wlen = 2048; - } else if (m_ratio < 0.8) { - m_n1 = 512; - } else { - m_n1 = 256; - } - if (shouldSharpen()) { - m_wlen = 2048; - } - m_n2 = lrintf(m_n1 * m_ratio); - } else { - if (m_ratio > 2) { - m_n2 = 512; - m_wlen = 4096; - } else if (m_ratio > 1.6) { - m_n2 = 384; - m_wlen = 2048; - } else { - m_n2 = 256; - } - if (shouldSharpen()) { - if (m_wlen < 2048) m_wlen = 2048; - } - m_n1 = lrintf(m_n2 / m_ratio); - if (m_n1 == 0) { - m_n1 = 1; - m_n2 = lrintf(m_ratio); - } - } - - m_transientThreshold = lrintf(m_wlen / 4.5); - - m_totalCount = 0; - m_transientCount = 0; - m_n2sum = 0; - - - std::cerr << "PhaseVocoderTimeStretcher: channels = " << m_channels - << ", ratio = " << m_ratio - << ", n1 = " << m_n1 << ", n2 = " << m_n2 << ", wlen = " - << m_wlen << ", max = " << m_maxOutputBlockSize << std::endl; -// << ", outbuflen = " << m_outbuf[0]->getSize() << std::endl; -} - -void -PhaseVocoderTimeStretcher::cleanup() -{ - std::cerr << "PhaseVocoderTimeStretcher::cleanup" << std::endl; - - for (size_t c = 0; c < m_channels; ++c) { - - fftf_destroy_plan(m_plan[c]); - fftf_destroy_plan(m_iplan[c]); - - fftf_free(m_time[c]); - fftf_free(m_freq[c]); - - fftf_free(m_mashbuf[c]); - fftf_free(m_prevPhase[c]); - fftf_free(m_prevAdjustedPhase[c]); - - delete m_inbuf[c]; - delete m_outbuf[c]; - } - - fftf_free(m_tempbuf); - fftf_free(m_modulationbuf); - fftf_free(m_prevTransientMag); - - delete[] m_prevPhase; - delete[] m_prevAdjustedPhase; - delete[] m_inbuf; - delete[] m_outbuf; - delete[] m_mashbuf; - delete[] m_time; - delete[] m_freq; - delete[] m_plan; - delete[] m_iplan; - - delete m_analysisWindow; - delete m_synthesisWindow; -} - -void -PhaseVocoderTimeStretcher::setRatio(float ratio) -{ - QMutexLocker locker(m_mutex); - - size_t formerWlen = m_wlen; - m_ratio = ratio; - - std::cerr << "PhaseVocoderTimeStretcher::setRatio: new ratio " << ratio - << std::endl; - - calculateParameters(); - - if (m_wlen == formerWlen) { - - // This is the only container whose size depends on m_ratio - - RingBuffer<float> **newin = new RingBuffer<float> *[m_channels]; - - size_t formerSize = m_inbuf[0]->getSize(); - size_t newSize = lrintf(m_outbuf[0]->getSize() / m_ratio) + m_wlen; - - std::cerr << "resizing inbuf from " << formerSize << " to " - << newSize << " (outbuf size is " << m_outbuf[0]->getSize() << ", ratio " << m_ratio << ")" << std::endl; - - if (formerSize != newSize) { - - size_t ready = m_inbuf[0]->getReadSpace(); - - for (size_t c = 0; c < m_channels; ++c) { - newin[c] = new RingBuffer<float>(newSize); - } - - if (ready > 0) { - - size_t copy = std::min(ready, newSize); - float *tmp = new float[ready]; - - for (size_t c = 0; c < m_channels; ++c) { - m_inbuf[c]->read(tmp, ready); - newin[c]->write(tmp + ready - copy, copy); - } - - delete[] tmp; - } - - for (size_t c = 0; c < m_channels; ++c) { - delete m_inbuf[c]; - } - - delete[] m_inbuf; - m_inbuf = newin; - } - - } else { - - std::cerr << "wlen changed" << std::endl; - cleanup(); - initialise(); - } -} - -size_t -PhaseVocoderTimeStretcher::getProcessingLatency() const -{ - return getWindowSize() - getInputIncrement(); -} - -size_t -PhaseVocoderTimeStretcher::getRequiredInputSamples() const -{ - QMutexLocker locker(m_mutex); - - if (m_inbuf[0]->getReadSpace() >= m_wlen) return 0; - return m_wlen - m_inbuf[0]->getReadSpace(); -} - -void -PhaseVocoderTimeStretcher::putInput(float **input, size_t samples) -{ - QMutexLocker locker(m_mutex); - - // We need to add samples from input to our internal buffer. When - // we have m_windowSize samples in the buffer, we can process it, - // move the samples back by m_n1 and write the output onto our - // internal output buffer. If we have (samples * ratio) samples - // in that, we can write m_n2 of them back to output and return - // (otherwise we have to write zeroes). - - // When we process, we write m_wlen to our fixed output buffer - // (m_mashbuf). We then pull out the first m_n2 samples from that - // buffer, push them into the output ring buffer, and shift - // m_mashbuf left by that amount. - - // The processing latency is then m_wlen - m_n2. - - size_t consumed = 0; - - while (consumed < samples) { - - size_t writable = m_inbuf[0]->getWriteSpace(); - writable = std::min(writable, samples - consumed); - - if (writable == 0) { -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "WARNING: PhaseVocoderTimeStretcher::putInput: writable == 0 (inbuf has " << m_inbuf[0]->getReadSpace() << " samples available for reading, space for " << m_inbuf[0]->getWriteSpace() << " more)" << std::endl; -#endif - if (m_inbuf[0]->getReadSpace() < m_wlen || - m_outbuf[0]->getWriteSpace() < m_n2) { - std::cerr << "WARNING: PhaseVocoderTimeStretcher::putInput: Inbuf has " << m_inbuf[0]->getReadSpace() << ", outbuf has space for " << m_outbuf[0]->getWriteSpace() << " (n2 = " << m_n2 << ", wlen = " << m_wlen << "), won't be able to process" << std::endl; - break; - } - } else { - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "writing " << writable << " from index " << consumed << " to inbuf, consumed will be " << consumed + writable << std::endl; -#endif - - for (size_t c = 0; c < m_channels; ++c) { - m_inbuf[c]->write(input[c] + consumed, writable); - } - consumed += writable; - } - - while (m_inbuf[0]->getReadSpace() >= m_wlen && - m_outbuf[0]->getWriteSpace() >= m_n2) { - - // We know we have at least m_wlen samples available - // in m_inbuf. We need to peek m_wlen of them for - // processing, and then read m_n1 to advance the read - // pointer. - - for (size_t c = 0; c < m_channels; ++c) { - - size_t got = m_inbuf[c]->peek(m_tempbuf, m_wlen); - assert(got == m_wlen); - - analyseBlock(c, m_tempbuf); - } - - bool transient = false; - if (shouldSharpen()) transient = isTransient(); - - size_t n2 = m_n2; - - if (transient) { - n2 = m_n1; - } - - ++m_totalCount; - if (transient) ++m_transientCount; - m_n2sum += n2; - -// std::cerr << "ratio for last 10: " <<last10num << "/" << (10 * m_n1) << " = " << float(last10num) / float(10 * m_n1) << " (should be " << m_ratio << ")" << std::endl; - - if (m_totalCount > 50 && m_transientCount < m_totalCount) { - - int fixed = lrintf(m_transientCount * m_n1); - - int idealTotal = lrintf(m_totalCount * m_n1 * m_ratio); - int idealSquashy = idealTotal - fixed; - - int squashyCount = m_totalCount - m_transientCount; - - n2 = lrintf(idealSquashy / squashyCount); - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - if (n2 != m_n2) { - std::cerr << m_n2 << " -> " << n2 << std::endl; - } -#endif - } - - for (size_t c = 0; c < m_channels; ++c) { - - synthesiseBlock(c, m_mashbuf[c], - c == 0 ? m_modulationbuf : 0, - m_prevTransient ? m_n1 : m_n2); - - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "writing first " << m_n2 << " from mashbuf, skipping " << m_n1 << " on inbuf " << std::endl; -#endif - m_inbuf[c]->skip(m_n1); - - for (size_t i = 0; i < n2; ++i) { - if (m_modulationbuf[i] > 0.f) { - m_mashbuf[c][i] /= m_modulationbuf[i]; - } - } - - m_outbuf[c]->write(m_mashbuf[c], n2); - - for (size_t i = 0; i < m_wlen - n2; ++i) { - m_mashbuf[c][i] = m_mashbuf[c][i + n2]; - } - - for (size_t i = m_wlen - n2; i < m_wlen; ++i) { - m_mashbuf[c][i] = 0.0f; - } - } - - m_prevTransient = transient; - - for (size_t i = 0; i < m_wlen - n2; ++i) { - m_modulationbuf[i] = m_modulationbuf[i + n2]; - } - - for (size_t i = m_wlen - n2; i < m_wlen; ++i) { - m_modulationbuf[i] = 0.0f; - } - - if (!transient) m_n2 = n2; - } - - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "loop ended: inbuf read space " << m_inbuf[0]->getReadSpace() << ", outbuf write space " << m_outbuf[0]->getWriteSpace() << std::endl; -#endif - } - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "PhaseVocoderTimeStretcher::putInput returning" << std::endl; -#endif - -// std::cerr << "ratio: nominal: " << getRatio() << " actual: " -// << m_total2 << "/" << m_total1 << " = " << float(m_total2) / float(m_total1) << " ideal: " << m_ratio << std::endl; -} - -size_t -PhaseVocoderTimeStretcher::getAvailableOutputSamples() const -{ - QMutexLocker locker(m_mutex); - - return m_outbuf[0]->getReadSpace(); -} - -void -PhaseVocoderTimeStretcher::getOutput(float **output, size_t samples) -{ - QMutexLocker locker(m_mutex); - - if (m_outbuf[0]->getReadSpace() < samples) { - std::cerr << "WARNING: PhaseVocoderTimeStretcher::getOutput: not enough data (yet?) (" << m_outbuf[0]->getReadSpace() << " < " << samples << ")" << std::endl; - size_t fill = samples - m_outbuf[0]->getReadSpace(); - for (size_t c = 0; c < m_channels; ++c) { - for (size_t i = 0; i < fill; ++i) { - output[c][i] = 0.0; - } - m_outbuf[c]->read(output[c] + fill, m_outbuf[c]->getReadSpace()); - } - } else { -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "enough data - writing " << samples << " from outbuf" << std::endl; -#endif - for (size_t c = 0; c < m_channels; ++c) { - m_outbuf[c]->read(output[c], samples); - } - } - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "PhaseVocoderTimeStretcher::getOutput returning" << std::endl; -#endif -} - -void -PhaseVocoderTimeStretcher::analyseBlock(size_t c, float *buf) -{ - size_t i; - - // buf contains m_wlen samples - -#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "PhaseVocoderTimeStretcher::analyseBlock (channel " << c << ")" << std::endl; -#endif - - m_analysisWindow->cut(buf); - - for (i = 0; i < m_wlen/2; ++i) { - float temp = buf[i]; - buf[i] = buf[i + m_wlen/2]; - buf[i + m_wlen/2] = temp; - } - - for (i = 0; i < m_wlen; ++i) { - m_time[c][i] = buf[i]; - } - - fftf_execute(m_plan[c]); // m_time -> m_freq -} - -bool -PhaseVocoderTimeStretcher::isTransient() -{ - int count = 0; - - for (size_t i = 0; i <= m_wlen/2; ++i) { - - float real = 0.f, imag = 0.f; - - for (size_t c = 0; c < m_channels; ++c) { - real += m_freq[c][i][0]; - imag += m_freq[c][i][1]; - } - - float sqrmag = (real * real + imag * imag); - - if (m_prevTransientMag[i] > 0.f) { - float diff = 10.f * log10f(sqrmag / m_prevTransientMag[i]); - if (diff > 3.f) ++count; - } - - m_prevTransientMag[i] = sqrmag; - } - - bool isTransient = false; - -// if (count > m_transientThreshold && -// count > m_prevTransientScore * 1.2) { - if (count > m_prevTransientScore && - count > m_transientThreshold && - count - m_prevTransientScore > int(m_wlen) / 20) { - isTransient = true; - - -// std::cerr << "isTransient (count = " << count << ", prev = " << m_prevTransientScore << ", diff = " << count - m_prevTransientScore << ", ratio = " << (m_totalCount > 0 ? (float (m_n2sum) / float(m_totalCount * m_n1)) : 1.f) << ", ideal = " << m_ratio << ")" << std::endl; -// } else { -// std::cerr << " !transient (count = " << count << ", prev = " << m_prevTransientScore << ", diff = " << count - m_prevTransientScore << ")" << std::endl; - } - - m_prevTransientScore = count; - - return isTransient; -} - -void -PhaseVocoderTimeStretcher::synthesiseBlock(size_t c, - float *out, - float *modulation, - size_t lastStep) -{ - bool unchanged = (lastStep == m_n1); - - for (size_t i = 0; i <= m_wlen/2; ++i) { - - float phase = princargf(atan2f(m_freq[c][i][1], m_freq[c][i][0])); - float adjustedPhase = phase; - - if (!unchanged) { - - float omega = (2 * M_PI * m_n1 * i) / m_wlen; - - float expectedPhase = m_prevPhase[c][i] + omega; - - float phaseError = princargf(phase - expectedPhase); - - float phaseIncrement = (omega + phaseError) / m_n1; - - adjustedPhase = m_prevAdjustedPhase[c][i] + - lastStep * phaseIncrement; - - float mag = sqrtf(m_freq[c][i][0] * m_freq[c][i][0] + - m_freq[c][i][1] * m_freq[c][i][1]); - - float real = mag * cosf(adjustedPhase); - float imag = mag * sinf(adjustedPhase); - m_freq[c][i][0] = real; - m_freq[c][i][1] = imag; - } - - m_prevPhase[c][i] = phase; - m_prevAdjustedPhase[c][i] = adjustedPhase; - } - - fftf_execute(m_iplan[c]); // m_freq -> m_time, inverse fft - - for (size_t i = 0; i < m_wlen/2; ++i) { - float temp = m_time[c][i]; - m_time[c][i] = m_time[c][i + m_wlen/2]; - m_time[c][i + m_wlen/2] = temp; - } - - for (size_t i = 0; i < m_wlen; ++i) { - m_time[c][i] = m_time[c][i] / m_wlen; - } - - m_synthesisWindow->cut(m_time[c]); - - for (size_t i = 0; i < m_wlen; ++i) { - out[i] += m_time[c][i]; - } - - if (modulation) { - - float area = m_analysisWindow->getArea(); - - for (size_t i = 0; i < m_wlen; ++i) { - float val = m_synthesisWindow->getValue(i); - modulation[i] += val * area; - } - } -} - -
--- a/audioio/PhaseVocoderTimeStretcher.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,187 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _PHASE_VOCODER_TIME_STRETCHER_H_ -#define _PHASE_VOCODER_TIME_STRETCHER_H_ - -#include "base/Window.h" -#include "base/RingBuffer.h" - -#include "data/fft/FFTapi.h" - -#include <QMutex> - -/** - * A time stretcher that alters the performance speed of audio, - * preserving pitch. - * - * This is based on the straightforward phase vocoder with phase - * unwrapping (as in e.g. the DAFX book pp275-), with optional - * percussive transient detection to avoid smearing percussive notes - * and resynchronise phases, and adding a stream API for real-time - * use. Principles and methods from Chris Duxbury, AES 2002 and 2004 - * thesis; Emmanuel Ravelli, DAFX 2005; Dan Barry, ISSC 2005 on - * percussion detection; code by Chris Cannam. - */ - -class PhaseVocoderTimeStretcher -{ -public: - PhaseVocoderTimeStretcher(size_t sampleRate, - size_t channels, - float ratio, - bool sharpen, - size_t maxOutputBlockSize); - virtual ~PhaseVocoderTimeStretcher(); - - /** - * Return the number of samples that would need to be added via - * putInput in order to provoke the time stretcher into doing some - * time stretching and making more output samples available. - * This will be an estimate, if transient sharpening is on; the - * caller may need to do the put/get/test cycle more than once. - */ - size_t getRequiredInputSamples() const; - - /** - * Put (and possibly process) a given number of input samples. - * Number should usually equal the value returned from - * getRequiredInputSamples(). - */ - void putInput(float **input, size_t samples); - - /** - * Get the number of processed samples ready for reading. - */ - size_t getAvailableOutputSamples() const; - - /** - * Get some processed samples. - */ - void getOutput(float **output, size_t samples); - - //!!! and reset? - - /** - * Change the time stretch ratio. - */ - void setRatio(float ratio); - - /** - * Get the hop size for input. - */ - size_t getInputIncrement() const { return m_n1; } - - /** - * Get the hop size for output. - */ - size_t getOutputIncrement() const { return m_n2; } - - /** - * Get the window size for FFT processing. - */ - size_t getWindowSize() const { return m_wlen; } - - /** - * Get the stretch ratio. - */ - float getRatio() const { return float(m_n2) / float(m_n1); } - - /** - * Return whether this time stretcher will attempt to sharpen transients. - */ - bool getSharpening() const { return m_sharpen; } - - /** - * Return the number of channels for this time stretcher. - */ - size_t getChannelCount() const { return m_channels; } - - /** - * Get the latency added by the time stretcher, in sample frames. - * This will be exact if transient sharpening is off, or approximate - * if it is on. - */ - size_t getProcessingLatency() const; - -protected: - /** - * Process a single phase vocoder frame from "in" into - * m_freq[channel]. - */ - void analyseBlock(size_t channel, float *in); // into m_freq[channel] - - /** - * Examine m_freq[0..m_channels-1] and return whether a percussive - * transient is found. - */ - bool isTransient(); - - /** - * Resynthesise from m_freq[channel] adding in to "out", - * adjusting phases on the basis of a prior step size of lastStep. - * Also add the window shape in to the modulation array (if - * present) -- for use in ensuring the output has the correct - * magnitude afterwards. - */ - void synthesiseBlock(size_t channel, float *out, float *modulation, - size_t lastStep); - - void initialise(); - void calculateParameters(); - void cleanup(); - - bool shouldSharpen() { - return m_sharpen && (m_ratio > 0.25); - } - - size_t m_sampleRate; - size_t m_channels; - size_t m_maxOutputBlockSize; - float m_ratio; - bool m_sharpen; - size_t m_n1; - size_t m_n2; - size_t m_wlen; - Window<float> *m_analysisWindow; - Window<float> *m_synthesisWindow; - - int m_totalCount; - int m_transientCount; - int m_n2sum; - - float **m_prevPhase; - float **m_prevAdjustedPhase; - - float *m_prevTransientMag; - int m_prevTransientScore; - int m_transientThreshold; - bool m_prevTransient; - - float *m_tempbuf; - float **m_time; - fftf_complex **m_freq; - fftf_plan *m_plan; - fftf_plan *m_iplan; - - RingBuffer<float> **m_inbuf; - RingBuffer<float> **m_outbuf; - float **m_mashbuf; - float *m_modulationbuf; - - QMutex *m_mutex; -}; - -#endif
--- a/audioio/PlaySpeedRangeMapper.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 QMUL. - - 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 "PlaySpeedRangeMapper.h" - -#include <iostream> -#include <cmath> - -PlaySpeedRangeMapper::PlaySpeedRangeMapper(int minpos, int maxpos) : - m_minpos(minpos), - m_maxpos(maxpos) -{ -} - -int -PlaySpeedRangeMapper::getPositionForValue(float value) const -{ - // value is percent - float factor = getFactorForValue(value); - int position = getPositionForFactor(factor); - return position; -} - -int -PlaySpeedRangeMapper::getPositionForFactor(float factor) const -{ - bool slow = (factor > 1.0); - - if (!slow) factor = 1.0 / factor; - - int half = (m_maxpos + m_minpos) / 2; - - factor = sqrtf((factor - 1.0) * 1000.f); - int position = lrintf(((factor * (half - m_minpos)) / 100.0) + m_minpos); - - if (slow) { - position = half - position; - } else { - position = position + half; - } - -// std::cerr << "value = " << value << " slow = " << slow << " factor = " << factor << " position = " << position << std::endl; - - return position; -} - -float -PlaySpeedRangeMapper::getValueForPosition(int position) const -{ - float factor = getFactorForPosition(position); - float pc = getValueForFactor(factor); - return pc; -} - -float -PlaySpeedRangeMapper::getValueForFactor(float factor) const -{ - float pc; - if (factor < 1.0) pc = ((1.0 / factor) - 1.0) * 100.0; - else pc = (1.0 - factor) * 100.0; -// std::cerr << "position = " << position << " percent = " << pc << std::endl; - return pc; -} - -float -PlaySpeedRangeMapper::getFactorForValue(float value) const -{ - // value is percent - - float factor; - - if (value <= 0) { - factor = 1.0 - (value / 100.0); - } else { - factor = 1.0 / (1.0 + (value / 100.0)); - } - -// std::cerr << "value = " << value << " factor = " << factor << std::endl; - return factor; -} - -float -PlaySpeedRangeMapper::getFactorForPosition(int position) const -{ - bool slow = false; - - if (position < m_minpos) position = m_minpos; - if (position > m_maxpos) position = m_maxpos; - - int half = (m_maxpos + m_minpos) / 2; - - if (position < half) { - slow = true; - position = half - position; - } else { - position = position - half; - } - - // position is between min and half (inclusive) - - float factor; - - if (position == m_minpos) { - factor = 1.0; - } else { - factor = ((position - m_minpos) * 100.0) / (half - m_minpos); - factor = 1.0 + (factor * factor) / 1000.f; - } - - if (!slow) factor = 1.0 / factor; - -// std::cerr << "position = " << position << " slow = " << slow << " factor = " << factor << std::endl; - - return factor; -} - -QString -PlaySpeedRangeMapper::getUnit() const -{ - return "%"; -}
--- a/audioio/PlaySpeedRangeMapper.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 QMUL. - - 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. -*/ - -#ifndef _PLAY_SPEED_RANGE_MAPPER_H_ -#define _PLAY_SPEED_RANGE_MAPPER_H_ - -#include "base/RangeMapper.h" - -class PlaySpeedRangeMapper : public RangeMapper -{ -public: - PlaySpeedRangeMapper(int minpos, int maxpos); - - virtual int getPositionForValue(float value) const; - virtual float getValueForPosition(int position) const; - - int getPositionForFactor(float factor) const; - float getValueForFactor(float factor) const; - - float getFactorForPosition(int position) const; - float getFactorForValue(float value) const; - - virtual QString getUnit() const; - -protected: - int m_minpos; - int m_maxpos; -}; - - -#endif
--- a/document/Document.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,949 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "Document.h" - -#include "data/model/WaveFileModel.h" -#include "data/model/WritableWaveFileModel.h" -#include "data/model/DenseThreeDimensionalModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "layer/Layer.h" -#include "base/CommandHistory.h" -#include "base/Command.h" -#include "view/View.h" -#include "base/PlayParameterRepository.h" -#include "base/PlayParameters.h" -#include "transform/TransformFactory.h" -#include <QApplication> -#include <QTextStream> -#include <iostream> - -// For alignment: -#include "data/model/AggregateWaveModel.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/AlignmentModel.h" - -//!!! still need to handle command history, documentRestored/documentModified - -Document::Document() : - m_mainModel(0) -{ - connect(this, SIGNAL(modelAboutToBeDeleted(Model *)), - TransformFactory::getInstance(), - SLOT(modelAboutToBeDeleted(Model *))); -} - -Document::~Document() -{ - //!!! Document should really own the command history. atm we - //still refer to it in various places that don't have access to - //the document, be nice to fix that - -// std::cerr << "\n\nDocument::~Document: about to clear command history" << std::endl; - CommandHistory::getInstance()->clear(); - -// std::cerr << "Document::~Document: about to delete layers" << std::endl; - while (!m_layers.empty()) { - deleteLayer(*m_layers.begin(), true); - } - - if (!m_models.empty()) { - std::cerr << "Document::~Document: WARNING: " - << m_models.size() << " model(s) still remain -- " - << "should have been garbage collected when deleting layers" - << std::endl; - while (!m_models.empty()) { - Model *model = m_models.begin()->first; - if (model == m_mainModel) { - // just in case! - std::cerr << "Document::~Document: WARNING: Main model is also" - << " in models list!" << std::endl; - } else if (model) { - emit modelAboutToBeDeleted(model); - model->aboutToDelete(); - delete model; - } - m_models.erase(m_models.begin()); - } - } - -// std::cerr << "Document::~Document: About to get rid of main model" -// << std::endl; - if (m_mainModel) { - emit modelAboutToBeDeleted(m_mainModel); - m_mainModel->aboutToDelete(); - } - - emit mainModelChanged(0); - delete m_mainModel; - -} - -Layer * -Document::createLayer(LayerFactory::LayerType type) -{ - Layer *newLayer = LayerFactory::getInstance()->createLayer(type); - if (!newLayer) return 0; - - newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); - - m_layers.insert(newLayer); - emit layerAdded(newLayer); - - return newLayer; -} - -Layer * -Document::createMainModelLayer(LayerFactory::LayerType type) -{ - Layer *newLayer = createLayer(type); - if (!newLayer) return 0; - setModel(newLayer, m_mainModel); - return newLayer; -} - -Layer * -Document::createImportedLayer(Model *model) -{ - LayerFactory::LayerTypeSet types = - LayerFactory::getInstance()->getValidLayerTypes(model); - - if (types.empty()) { - std::cerr << "WARNING: Document::importLayer: no valid display layer for model" << std::endl; - return 0; - } - - //!!! for now, just use the first suitable layer type - LayerFactory::LayerType type = *types.begin(); - - Layer *newLayer = LayerFactory::getInstance()->createLayer(type); - if (!newLayer) return 0; - - newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); - - addImportedModel(model); - setModel(newLayer, model); - - //!!! and all channels - setChannel(newLayer, -1); - - m_layers.insert(newLayer); - emit layerAdded(newLayer); - return newLayer; -} - -Layer * -Document::createEmptyLayer(LayerFactory::LayerType type) -{ - Model *newModel = - LayerFactory::getInstance()->createEmptyModel(type, m_mainModel); - if (!newModel) return 0; - - Layer *newLayer = createLayer(type); - if (!newLayer) { - delete newModel; - return 0; - } - - addImportedModel(newModel); - setModel(newLayer, newModel); - - return newLayer; -} - -Layer * -Document::createDerivedLayer(LayerFactory::LayerType type, - TransformId transform) -{ - Layer *newLayer = createLayer(type); - if (!newLayer) return 0; - - newLayer->setObjectName(getUniqueLayerName - (TransformFactory::getInstance()-> - getTransformFriendlyName(transform))); - - return newLayer; -} - -Layer * -Document::createDerivedLayer(TransformId transform, - Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml) -{ - Model *newModel = addDerivedModel(transform, inputModel, - context, configurationXml); - if (!newModel) { - // error already printed to stderr by addDerivedModel - emit modelGenerationFailed(transform); - return 0; - } - - LayerFactory::LayerTypeSet types = - LayerFactory::getInstance()->getValidLayerTypes(newModel); - - if (types.empty()) { - std::cerr << "WARNING: Document::createLayerForTransform: no valid display layer for output of transform " << transform.toStdString() << std::endl; - delete newModel; - return 0; - } - - //!!! for now, just use the first suitable layer type - - Layer *newLayer = createLayer(*types.begin()); - setModel(newLayer, newModel); - - //!!! We need to clone the model when adding the layer, so that it - //can be edited without affecting other layers that are based on - //the same model. Unfortunately we can't just clone it now, - //because it probably hasn't been completed yet -- the transform - //runs in the background. Maybe the transform has to handle - //cloning and cacheing models itself. - // - // Once we do clone models here, of course, we'll have to avoid - // leaking them too. - // - // We want the user to be able to add a model to a second layer - // _while it's still being calculated in the first_ and have it - // work quickly. That means we need to put the same physical - // model pointer in both layers, so they can't actually be cloned. - - if (newLayer) { - newLayer->setObjectName(getUniqueLayerName - (TransformFactory::getInstance()-> - getTransformFriendlyName(transform))); - } - - emit layerAdded(newLayer); - return newLayer; -} - -void -Document::setMainModel(WaveFileModel *model) -{ - Model *oldMainModel = m_mainModel; - m_mainModel = model; - - emit modelAdded(m_mainModel); - - std::vector<Layer *> obsoleteLayers; - std::set<QString> failedTransforms; - - // We need to ensure that no layer is left using oldMainModel or - // any of the old derived models as its model. Either replace the - // model, or delete the layer for each layer that is currently - // using one of these. Carry out this replacement before we - // delete any of the models. - - for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - - Layer *layer = *i; - Model *model = layer->getModel(); - -// std::cerr << "Document::setMainModel: inspecting model " -// << (model ? model->objectName().toStdString() : "(null)") << " in layer " -// << layer->objectName().toStdString() << std::endl; - - if (model == oldMainModel) { -// std::cerr << "... it uses the old main model, replacing" << std::endl; - LayerFactory::getInstance()->setModel(layer, m_mainModel); - continue; - } - - if (m_models.find(model) == m_models.end()) { - std::cerr << "WARNING: Document::setMainModel: Unknown model " - << model << " in layer " << layer << std::endl; - // get rid of this hideous degenerate - obsoleteLayers.push_back(layer); - continue; - } - - if (m_models[model].source == oldMainModel) { - -// std::cerr << "... it uses a model derived from the old main model, regenerating" << std::endl; - - // This model was derived from the previous main - // model: regenerate it. - - TransformId transform = m_models[model].transform; - PluginTransform::ExecutionContext context = m_models[model].context; - - Model *replacementModel = - addDerivedModel(transform, - m_mainModel, - context, - m_models[model].configurationXml); - - if (!replacementModel) { - std::cerr << "WARNING: Document::setMainModel: Failed to regenerate model for transform \"" - << transform.toStdString() << "\"" << " in layer " << layer << std::endl; - if (failedTransforms.find(transform) == failedTransforms.end()) { - emit modelRegenerationFailed(layer->objectName(), - transform); - failedTransforms.insert(transform); - } - obsoleteLayers.push_back(layer); - } else { - std::cerr << "Replacing model " << model << " (type " - << typeid(*model).name() << ") with model " - << replacementModel << " (type " - << typeid(*replacementModel).name() << ") in layer " - << layer << " (name " << layer->objectName().toStdString() << ")" - << std::endl; - RangeSummarisableTimeValueModel *rm = - dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel); - if (rm) { - std::cerr << "new model has " << rm->getChannelCount() << " channels " << std::endl; - } else { - std::cerr << "new model is not a RangeSummarisableTimeValueModel!" << std::endl; - } - setModel(layer, replacementModel); - } - } - } - - for (size_t k = 0; k < obsoleteLayers.size(); ++k) { - deleteLayer(obsoleteLayers[k], true); - } - - emit mainModelChanged(m_mainModel); - - // we already emitted modelAboutToBeDeleted for this - delete oldMainModel; -} - -void -Document::addDerivedModel(TransformId transform, - Model *inputModel, - const PluginTransform::ExecutionContext &context, - Model *outputModelToAdd, - QString configurationXml) -{ - if (m_models.find(outputModelToAdd) != m_models.end()) { - std::cerr << "WARNING: Document::addDerivedModel: Model already added" - << std::endl; - return; - } - -// std::cerr << "Document::addDerivedModel: source is " << inputModel << " \"" << inputModel->objectName().toStdString() << "\"" << std::endl; - - ModelRecord rec; - rec.source = inputModel; - rec.transform = transform; - rec.context = context; - rec.configurationXml = configurationXml; - rec.refcount = 0; - - outputModelToAdd->setSourceModel(inputModel); - - m_models[outputModelToAdd] = rec; - - emit modelAdded(outputModelToAdd); -} - - -void -Document::addImportedModel(Model *model) -{ - if (m_models.find(model) != m_models.end()) { - std::cerr << "WARNING: Document::addImportedModel: Model already added" - << std::endl; - return; - } - - ModelRecord rec; - rec.source = 0; - rec.transform = ""; - rec.refcount = 0; - - m_models[model] = rec; - - emit modelAdded(model); -} - -Model * -Document::addDerivedModel(TransformId transform, - Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml) -{ - Model *model = 0; - - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - if (i->second.transform == transform && - i->second.source == inputModel && - i->second.context == context && - i->second.configurationXml == configurationXml) { - return i->first; - } - } - - model = TransformFactory::getInstance()->transform - (transform, inputModel, context, configurationXml); - - if (!model) { - std::cerr << "WARNING: Document::addDerivedModel: no output model for transform " << transform.toStdString() << std::endl; - } else { - addDerivedModel(transform, inputModel, context, model, configurationXml); - } - - return model; -} - -void -Document::releaseModel(Model *model) // Will _not_ release main model! -{ - if (model == 0) { - return; - } - - if (model == m_mainModel) { - return; - } - - bool toDelete = false; - - if (m_models.find(model) != m_models.end()) { - - if (m_models[model].refcount == 0) { - std::cerr << "WARNING: Document::releaseModel: model " << model - << " reference count is zero already!" << std::endl; - } else { - if (--m_models[model].refcount == 0) { - toDelete = true; - } - } - } else { - std::cerr << "WARNING: Document::releaseModel: Unfound model " - << model << std::endl; - toDelete = true; - } - - if (toDelete) { - - int sourceCount = 0; - - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - if (i->second.source == model) { - ++sourceCount; - i->second.source = 0; - } - } - - if (sourceCount > 0) { - std::cerr << "Document::releaseModel: Deleting model " - << model << " even though it is source for " - << sourceCount << " other derived model(s) -- resetting " - << "their source fields appropriately" << std::endl; - } - - emit modelAboutToBeDeleted(model); - model->aboutToDelete(); - m_models.erase(model); - delete model; - } -} - -void -Document::deleteLayer(Layer *layer, bool force) -{ - if (m_layerViewMap.find(layer) != m_layerViewMap.end() && - m_layerViewMap[layer].size() > 0) { - - std::cerr << "WARNING: Document::deleteLayer: Layer " - << layer << " [" << layer->objectName().toStdString() << "]" - << " is still used in " << m_layerViewMap[layer].size() - << " views!" << std::endl; - - if (force) { - - std::cerr << "(force flag set -- deleting from all views)" << std::endl; - - for (std::set<View *>::iterator j = m_layerViewMap[layer].begin(); - j != m_layerViewMap[layer].end(); ++j) { - // don't use removeLayerFromView, as it issues a command - layer->setLayerDormant(*j, true); - (*j)->removeLayer(layer); - } - - m_layerViewMap.erase(layer); - - } else { - return; - } - } - - if (m_layers.find(layer) == m_layers.end()) { - std::cerr << "Document::deleteLayer: Layer " - << layer << " does not exist, or has already been deleted " - << "(this may not be as serious as it sounds)" << std::endl; - return; - } - - m_layers.erase(layer); - - releaseModel(layer->getModel()); - emit layerRemoved(layer); - emit layerAboutToBeDeleted(layer); - delete layer; -} - -void -Document::setModel(Layer *layer, Model *model) -{ - if (model && - model != m_mainModel && - m_models.find(model) == m_models.end()) { - std::cerr << "ERROR: Document::setModel: Layer " << layer - << " (\"" << layer->objectName().toStdString() - << "\") wants to use unregistered model " << model - << ": register the layer's model before setting it!" - << std::endl; - return; - } - - Model *previousModel = layer->getModel(); - - if (previousModel == model) { - std::cerr << "WARNING: Document::setModel: Layer " << layer << " (\"" - << layer->objectName().toStdString() - << "\") is already set to model " - << model << " (\"" - << (model ? model->objectName().toStdString() : "(null)") - << "\")" << std::endl; - return; - } - - if (model && model != m_mainModel) { - m_models[model].refcount ++; - } - - if (model && previousModel) { - PlayParameterRepository::getInstance()->copyParameters - (previousModel, model); - } - - LayerFactory::getInstance()->setModel(layer, model); - - if (previousModel) { - releaseModel(previousModel); - } -} - -void -Document::setChannel(Layer *layer, int channel) -{ - LayerFactory::getInstance()->setChannel(layer, channel); -} - -void -Document::addLayerToView(View *view, Layer *layer) -{ - Model *model = layer->getModel(); - if (!model) { -// std::cerr << "Document::addLayerToView: Layer (\"" -// << layer->objectName().toStdString() -// << "\") with no model being added to view: " -// << "normally you want to set the model first" << std::endl; - } else { - if (model != m_mainModel && - m_models.find(model) == m_models.end()) { - std::cerr << "ERROR: Document::addLayerToView: Layer " << layer - << " has unregistered model " << model - << " -- register the layer's model before adding the layer!" << std::endl; - return; - } - } - - CommandHistory::getInstance()->addCommand - (new Document::AddLayerCommand(this, view, layer)); -} - -void -Document::removeLayerFromView(View *view, Layer *layer) -{ - CommandHistory::getInstance()->addCommand - (new Document::RemoveLayerCommand(this, view, layer)); -} - -void -Document::addToLayerViewMap(Layer *layer, View *view) -{ - bool firstView = (m_layerViewMap.find(layer) == m_layerViewMap.end() || - m_layerViewMap[layer].empty()); - - if (m_layerViewMap[layer].find(view) != - m_layerViewMap[layer].end()) { - std::cerr << "WARNING: Document::addToLayerViewMap:" - << " Layer " << layer << " -> view " << view << " already in" - << " layer view map -- internal inconsistency" << std::endl; - } - - m_layerViewMap[layer].insert(view); - - if (firstView) emit layerInAView(layer, true); -} - -void -Document::removeFromLayerViewMap(Layer *layer, View *view) -{ - if (m_layerViewMap[layer].find(view) == - m_layerViewMap[layer].end()) { - std::cerr << "WARNING: Document::removeFromLayerViewMap:" - << " Layer " << layer << " -> view " << view << " not in" - << " layer view map -- internal inconsistency" << std::endl; - } - - m_layerViewMap[layer].erase(view); - - if (m_layerViewMap[layer].empty()) { - m_layerViewMap.erase(layer); - emit layerInAView(layer, false); - } -} - -QString -Document::getUniqueLayerName(QString candidate) -{ - for (int count = 1; ; ++count) { - - QString adjusted = - (count > 1 ? QString("%1 <%2>").arg(candidate).arg(count) : - candidate); - - bool duplicate = false; - - for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if ((*i)->objectName() == adjusted) { - duplicate = true; - break; - } - } - - if (!duplicate) return adjusted; - } -} - -std::vector<Model *> -Document::getTransformInputModels() -{ - std::vector<Model *> models; - - if (!m_mainModel) return models; - - models.push_back(m_mainModel); - - //!!! This will pick up all models, including those that aren't visible... - - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - - Model *model = i->first; - if (!model || model == m_mainModel) continue; - DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); - - if (dtvm) { - models.push_back(dtvm); - } - } - - return models; -} - -void -Document::alignModel(Model *model) -{ - if (!m_mainModel || model == m_mainModel) return; - - RangeSummarisableTimeValueModel *rm = - dynamic_cast<RangeSummarisableTimeValueModel *>(model); - if (!rm) return; - - // This involves creating three new models: - - // 1. an AggregateWaveModel to provide the mixdowns of the main - // model and the new model in its two channels, as input to the - // MATCH plugin - - // 2. a SparseTimeValueModel, which is the model automatically - // created by FeatureExtractionPluginTransform when running the - // MATCH plugin (thus containing the alignment path) - - // 3. an AlignmentModel, which stores the path model and carries - // out alignment lookups on it. - - // The first two of these are provided as arguments to the - // constructor for the third, which takes responsibility for - // deleting them. The AlignmentModel, meanwhile, is passed to the - // new model we are aligning, which also takes responsibility for - // it. We should not have to delete any of these new models here. - - AggregateWaveModel::ChannelSpecList components; - - components.push_back(AggregateWaveModel::ModelChannelSpec - (m_mainModel, -1)); - - components.push_back(AggregateWaveModel::ModelChannelSpec - (rm, -1)); - - Model *aggregate = new AggregateWaveModel(components); - - TransformId id = "vamp:match-vamp-plugin:match:path"; - - TransformFactory *factory = TransformFactory::getInstance(); - - Model *transformOutput = factory->transform - (id, aggregate, - factory->getDefaultContextForTransform(id, aggregate), - "<plugin param-serialise=\"1\"/>"); - - SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *> - (transformOutput); - - if (!path) { - std::cerr << "Document::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << std::endl; - delete transformOutput; - delete aggregate; - return; - } - - AlignmentModel *alignmentModel = new AlignmentModel - (m_mainModel, model, aggregate, path); - - rm->setAlignment(alignmentModel); -} - -void -Document::alignModels() -{ - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - alignModel(i->first); - } -} - -Document::AddLayerCommand::AddLayerCommand(Document *d, - View *view, - Layer *layer) : - m_d(d), - m_view(view), - m_layer(layer), - m_name(qApp->translate("AddLayerCommand", "Add %1 Layer").arg(layer->objectName())), - m_added(false) -{ -} - -Document::AddLayerCommand::~AddLayerCommand() -{ -// std::cerr << "Document::AddLayerCommand::~AddLayerCommand" << std::endl; - if (!m_added) { - m_d->deleteLayer(m_layer); - } -} - -void -Document::AddLayerCommand::execute() -{ - for (int i = 0; i < m_view->getLayerCount(); ++i) { - if (m_view->getLayer(i) == m_layer) { - // already there - m_layer->setLayerDormant(m_view, false); - m_added = true; - return; - } - } - - m_view->addLayer(m_layer); - m_layer->setLayerDormant(m_view, false); - - m_d->addToLayerViewMap(m_layer, m_view); - m_added = true; -} - -void -Document::AddLayerCommand::unexecute() -{ - m_view->removeLayer(m_layer); - m_layer->setLayerDormant(m_view, true); - - m_d->removeFromLayerViewMap(m_layer, m_view); - m_added = false; -} - -Document::RemoveLayerCommand::RemoveLayerCommand(Document *d, - View *view, - Layer *layer) : - m_d(d), - m_view(view), - m_layer(layer), - m_name(qApp->translate("RemoveLayerCommand", "Delete %1 Layer").arg(layer->objectName())), - m_added(true) -{ -} - -Document::RemoveLayerCommand::~RemoveLayerCommand() -{ -// std::cerr << "Document::RemoveLayerCommand::~RemoveLayerCommand" << std::endl; - if (!m_added) { - m_d->deleteLayer(m_layer); - } -} - -void -Document::RemoveLayerCommand::execute() -{ - bool have = false; - for (int i = 0; i < m_view->getLayerCount(); ++i) { - if (m_view->getLayer(i) == m_layer) { - have = true; - break; - } - } - - if (!have) { // not there! - m_layer->setLayerDormant(m_view, true); - m_added = false; - return; - } - - m_view->removeLayer(m_layer); - m_layer->setLayerDormant(m_view, true); - - m_d->removeFromLayerViewMap(m_layer, m_view); - m_added = false; -} - -void -Document::RemoveLayerCommand::unexecute() -{ - m_view->addLayer(m_layer); - m_layer->setLayerDormant(m_view, false); - - m_d->addToLayerViewMap(m_layer, m_view); - m_added = true; -} - -void -Document::toXml(QTextStream &out, QString indent, QString extraAttributes) const -{ - out << indent + QString("<data%1%2>\n") - .arg(extraAttributes == "" ? "" : " ").arg(extraAttributes); - - if (m_mainModel) { - m_mainModel->toXml(out, indent + " ", "mainModel=\"true\""); - } - - // Models that are not used in a layer that is in a view should - // not be written. Get our list of required models first. - - std::set<const Model *> used; - - for (LayerViewMap::const_iterator i = m_layerViewMap.begin(); - i != m_layerViewMap.end(); ++i) { - - if (i->first && !i->second.empty() && i->first->getModel()) { - used.insert(i->first->getModel()); - } - } - - for (ModelMap::const_iterator i = m_models.begin(); - i != m_models.end(); ++i) { - - const Model *model = i->first; - const ModelRecord &rec = i->second; - - if (used.find(model) == used.end()) continue; - - // We need an intelligent way to determine which models need - // to be streamed (i.e. have been edited, or are small) and - // which should not be (i.e. remain as generated by a - // transform, and are large). - // - // At the moment we can get away with deciding not to stream - // dense 3d models or writable wave file models, provided they - // were generated from a transform, because at the moment there - // is no way to edit those model types so it should be safe to - // regenerate them. That won't always work in future though. - // It would be particularly nice to be able to ask the user, - // as well as making an intelligent guess. - - bool writeModel = true; - bool haveDerivation = false; - - if (rec.source && rec.transform != "") { - haveDerivation = true; - } - - if (haveDerivation) { - if (dynamic_cast<const WritableWaveFileModel *>(model)) { - writeModel = false; - } else if (dynamic_cast<const DenseThreeDimensionalModel *>(model)) { - writeModel = false; - } - } - - if (writeModel) { - i->first->toXml(out, indent + " "); - } - - if (haveDerivation) { - - QString extentsAttributes; - if (rec.context.startFrame != 0 || - rec.context.duration != 0) { - extentsAttributes = QString("startFrame=\"%1\" duration=\"%2\" ") - .arg(rec.context.startFrame) - .arg(rec.context.duration); - } - - out << indent; - out << QString(" <derivation source=\"%1\" model=\"%2\" channel=\"%3\" domain=\"%4\" stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" transform=\"%9\"") - .arg(XmlExportable::getObjectExportId(rec.source)) - .arg(XmlExportable::getObjectExportId(i->first)) - .arg(rec.context.channel) - .arg(rec.context.domain) - .arg(rec.context.stepSize) - .arg(rec.context.blockSize) - .arg(extentsAttributes) - .arg(int(rec.context.windowType)) - .arg(XmlExportable::encodeEntities(rec.transform)); - - if (rec.configurationXml != "") { - out << ">\n " + indent + rec.configurationXml - + "\n" + indent + " </derivation>\n"; - } else { - out << "/>\n"; - } - } - - //!!! We should probably own the PlayParameterRepository - PlayParameters *playParameters = - PlayParameterRepository::getInstance()->getPlayParameters(i->first); - if (playParameters) { - playParameters->toXml - (out, indent + " ", - QString("model=\"%1\"") - .arg(XmlExportable::getObjectExportId(i->first))); - } - } - - for (LayerSet::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { - - (*i)->toXml(out, indent + " "); - } - - out << indent + "</data>\n"; -} - -
--- a/document/Document.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,316 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _DOCUMENT_H_ -#define _DOCUMENT_H_ - -#include "layer/LayerFactory.h" -#include "transform/Transform.h" -#include "transform/PluginTransform.h" -#include "base/Command.h" - -#include <map> -#include <set> - -class Model; -class Layer; -class View; -class WaveFileModel; - -/** - * A Sonic Visualiser document consists of a set of data models, and - * also the visualisation layers used to display them. Changes to the - * layers and their layout need to be stored and managed in much the - * same way as changes to the underlying data. - * - * The document manages: - * - * - A main data Model, which provides the underlying sample rate and - * such like. This must be a WaveFileModel. - * - * - Any number of imported Model objects, which contain data without any - * requirement to remember where the data came from or how to - * regenerate it. - * - * - Any number of Model objects that were generated by a Transform - * such as FeatureExtractionPluginTransform. For these, we also - * record the source model and the name of the transform used to - * generate the model so that we can regenerate it (potentially - * from a different source) on demand. - * - * - A flat list of Layer objects. Elsewhere, the GUI may distribute these - * across any number of View widgets. A layer may be viewable on more - * than one view at once, in principle. A layer refers to one model, - * but the same model can be in use in more than one layer. - * - * The document does *not* manage the existence or structure of Pane - * and other view widgets. However, it does provide convenience - * methods for reference-counted command-based management of the - * association between layers and views (addLayerToView, - * removeLayerFromView). - */ - -class Document : public QObject, - public XmlExportable -{ - Q_OBJECT - -public: - Document(); - virtual ~Document(); - - /** - * Create and return a new layer of the given type, associated - * with no model. The caller may set any model on this layer, but - * the model must also be registered with the document via the - * add-model methods below. - */ - Layer *createLayer(LayerFactory::LayerType); - - /** - * Create and return a new layer of the given type, associated - * with the current main model (if appropriate to the layer type). - */ - Layer *createMainModelLayer(LayerFactory::LayerType); - - /** - * Create and return a new layer associated with the given model, - * and register the model as an imported model. - */ - Layer *createImportedLayer(Model *); - - /** - * Create and return a new layer of the given type, with an - * appropriate empty model. If the given type is not one for - * which an empty model can meaningfully be created, return 0. - */ - Layer *createEmptyLayer(LayerFactory::LayerType); - - /** - * Create and return a new layer of the given type, associated - * with the given transform name. This method does not run the - * transform itself, nor create a model. The caller can safely - * add a model to the layer later, but note that all models used - * by a transform layer _must_ be registered with the document - * using addDerivedModel below. - */ - Layer *createDerivedLayer(LayerFactory::LayerType, TransformId); - - /** - * Create and return a suitable layer for the given transform, - * running the transform and associating the resulting model with - * the new layer. - */ - Layer *createDerivedLayer(TransformId, - Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml); - - /** - * Set the main model (the source for playback sample rate, etc) - * to the given wave file model. This will regenerate any derived - * models that were based on the previous main model. - */ - void setMainModel(WaveFileModel *); - - /** - * Get the main model (the source for playback sample rate, etc). - */ - WaveFileModel *getMainModel() { return m_mainModel; } - - /** - * Get the main model (the source for playback sample rate, etc). - */ - const WaveFileModel *getMainModel() const { return m_mainModel; } - - std::vector<Model *> getTransformInputModels(); - - /** - * Add a derived model associated with the given transform, - * running the transform and returning the resulting model. - */ - Model *addDerivedModel(TransformId transform, - Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml); - - /** - * Add a derived model associated with the given transform. This - * is necessary to register any derived model that was not created - * by the document using createDerivedModel or createDerivedLayer. - */ - void addDerivedModel(TransformId, - Model *inputModel, - const PluginTransform::ExecutionContext &context, - Model *outputModelToAdd, - QString configurationXml); - - /** - * Add an imported (non-derived, non-main) model. This is - * necessary to register any imported model that is associated - * with a layer. - */ - void addImportedModel(Model *); - - /** - * Associate the given model with the given layer. The model must - * have already been registered using one of the addXXModel - * methods above. - */ - void setModel(Layer *, Model *); - - /** - * Set the given layer to use the given channel of its model (-1 - * means all available channels). - */ - void setChannel(Layer *, int); - - /** - * Add the given layer to the given view. If the layer is - * intended to show a particular model, the model should normally - * be set using setModel before this method is called. - */ - void addLayerToView(View *, Layer *); - - /** - * Remove the given layer from the given view. - */ - void removeLayerFromView(View *, Layer *); - - void toXml(QTextStream &, QString indent, QString extraAttributes) const; -signals: - void layerAdded(Layer *); - void layerRemoved(Layer *); - void layerAboutToBeDeleted(Layer *); - - // Emitted when a layer is first added to a view, or when it is - // last removed from a view - void layerInAView(Layer *, bool); - - void modelAdded(Model *); - void mainModelChanged(WaveFileModel *); // emitted after modelAdded - void modelAboutToBeDeleted(Model *); - - void modelGenerationFailed(QString transformName); - void modelRegenerationFailed(QString layerName, QString transformName); - -protected: - void releaseModel(Model *model); - - /** - * Delete the given layer, and also its associated model if no - * longer used by any other layer. In general, this should be the - * only method used to delete layers -- doing so directly is a bit - * of a social gaffe. - */ - void deleteLayer(Layer *, bool force = false); - - /** - * If model is suitable for alignment, align it against the main - * model and store the alignment in the model. - */ - void alignModel(Model *); - - /** - * Realign all models if the main model has changed. Is this wise? - */ - void alignModels(); - - /* - * Every model that is in use by a layer in the document must be - * found in either m_mainModel or m_models. We own and control - * the lifespan of all of these models. - */ - - /** - * The model that provides the underlying sample rate, etc. This - * model is not reference counted for layers, and is not freed - * unless it is replaced or the document is deleted. - */ - WaveFileModel *m_mainModel; - - struct ModelRecord - { - // Information associated with a non-main model. If this - // model is derived from another, then source will be non-NULL - // and the transform name will be set appropriately. If the - // transform name is set but source is NULL, then there was a - // transform involved but the (target) model has been modified - // since being generated from it. - const Model *source; - TransformId transform; - PluginTransform::ExecutionContext context; - QString configurationXml; - - // Count of the number of layers using this model. - int refcount; - }; - - typedef std::map<Model *, ModelRecord> ModelMap; - ModelMap m_models; - - class AddLayerCommand : public Command - { - public: - AddLayerCommand(Document *d, View *view, Layer *layer); - virtual ~AddLayerCommand(); - - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const { return m_name; } - - protected: - Document *m_d; - View *m_view; // I don't own this - Layer *m_layer; // Document owns this, but I determine its lifespans - QString m_name; - bool m_added; - }; - - class RemoveLayerCommand : public Command - { - public: - RemoveLayerCommand(Document *d, View *view, Layer *layer); - virtual ~RemoveLayerCommand(); - - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const { return m_name; } - - protected: - Document *m_d; - View *m_view; // I don't own this - Layer *m_layer; // Document owns this, but I determine its lifespan - QString m_name; - bool m_added; - }; - - typedef std::map<Layer *, std::set<View *> > LayerViewMap; - LayerViewMap m_layerViewMap; - - void addToLayerViewMap(Layer *, View *); - void removeFromLayerViewMap(Layer *, View *); - - QString getUniqueLayerName(QString candidate); - - /** - * And these are the layers. We also control the lifespans of - * these (usually through the commands used to add and remove them). - */ - typedef std::set<Layer *> LayerSet; - LayerSet m_layers; -}; - -#endif
--- a/document/SVFileReader.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1167 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "SVFileReader.h" - -#include "layer/Layer.h" -#include "view/View.h" -#include "base/PlayParameters.h" -#include "base/PlayParameterRepository.h" - -#include "data/fileio/AudioFileReaderFactory.h" -#include "data/fileio/FileFinder.h" -#include "data/fileio/FileSource.h" - -#include "data/model/WaveFileModel.h" -#include "data/model/EditableDenseThreeDimensionalModel.h" -#include "data/model/SparseOneDimensionalModel.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/NoteModel.h" -#include "data/model/TextModel.h" -#include "data/model/ImageModel.h" - -#include "view/Pane.h" - -#include "Document.h" - -#include <QString> -#include <QMessageBox> -#include <QFileDialog> - -#include <iostream> - -SVFileReader::SVFileReader(Document *document, - SVFileReaderPaneCallback &callback, - QString location) : - m_document(document), - m_paneCallback(callback), - m_location(location), - m_currentPane(0), - m_currentDataset(0), - m_currentDerivedModel(0), - m_currentDerivedModelId(-1), - m_currentPlayParameters(0), - m_datasetSeparator(" "), - m_inRow(false), - m_inLayer(false), - m_inView(false), - m_rowNumber(0), - m_ok(false) -{ -} - -void -SVFileReader::parse(const QString &xmlData) -{ - QXmlInputSource inputSource; - inputSource.setData(xmlData); - parse(inputSource); -} - -void -SVFileReader::parse(QXmlInputSource &inputSource) -{ - QXmlSimpleReader reader; - reader.setContentHandler(this); - reader.setErrorHandler(this); - m_ok = reader.parse(inputSource); -} - -bool -SVFileReader::isOK() -{ - return m_ok; -} - -SVFileReader::~SVFileReader() -{ - if (!m_awaitingDatasets.empty()) { - std::cerr << "WARNING: SV-XML: File ended with " - << m_awaitingDatasets.size() << " unfilled model dataset(s)" - << std::endl; - } - - std::set<Model *> unaddedModels; - - for (std::map<int, Model *>::iterator i = m_models.begin(); - i != m_models.end(); ++i) { - if (m_addedModels.find(i->second) == m_addedModels.end()) { - unaddedModels.insert(i->second); - } - } - - if (!unaddedModels.empty()) { - std::cerr << "WARNING: SV-XML: File contained " - << unaddedModels.size() << " unused models" - << std::endl; - while (!unaddedModels.empty()) { - delete *unaddedModels.begin(); - unaddedModels.erase(unaddedModels.begin()); - } - } -} - -bool -SVFileReader::startElement(const QString &, const QString &, - const QString &qName, - const QXmlAttributes &attributes) -{ - QString name = qName.toLower(); - - bool ok = false; - - // Valid element names: - // - // sv - // data - // dataset - // display - // derivation - // playparameters - // layer - // model - // point - // row - // view - // window - - if (name == "sv") { - - // nothing needed - ok = true; - - } else if (name == "data") { - - // nothing needed - m_inData = true; - ok = true; - - } else if (name == "display") { - - // nothing needed - ok = true; - - } else if (name == "window") { - - ok = readWindow(attributes); - - } else if (name == "model") { - - ok = readModel(attributes); - - } else if (name == "dataset") { - - ok = readDatasetStart(attributes); - - } else if (name == "bin") { - - ok = addBinToDataset(attributes); - - } else if (name == "point") { - - ok = addPointToDataset(attributes); - - } else if (name == "row") { - - ok = addRowToDataset(attributes); - - } else if (name == "layer") { - - addUnaddedModels(); // all models must be specified before first layer - ok = readLayer(attributes); - - } else if (name == "view") { - - m_inView = true; - ok = readView(attributes); - - } else if (name == "derivation") { - - ok = readDerivation(attributes); - - } else if (name == "playparameters") { - - ok = readPlayParameters(attributes); - - } else if (name == "plugin") { - - ok = readPlugin(attributes); - - } else if (name == "selections") { - - m_inSelections = true; - ok = true; - - } else if (name == "selection") { - - ok = readSelection(attributes); - - } else if (name == "measurement") { - - ok = readMeasurement(attributes); - - } else { - std::cerr << "WARNING: SV-XML: Unexpected element \"" - << name.toLocal8Bit().data() << "\"" << std::endl; - } - - if (!ok) { - std::cerr << "WARNING: SV-XML: Failed to completely process element \"" - << name.toLocal8Bit().data() << "\"" << std::endl; - } - - return true; -} - -bool -SVFileReader::characters(const QString &text) -{ - bool ok = false; - - if (m_inRow) { - ok = readRowData(text); - if (!ok) { - std::cerr << "WARNING: SV-XML: Failed to read row data content for row " << m_rowNumber << std::endl; - } - } - - return true; -} - -bool -SVFileReader::endElement(const QString &, const QString &, - const QString &qName) -{ - QString name = qName.toLower(); - - if (name == "dataset") { - - if (m_currentDataset) { - - bool foundInAwaiting = false; - - for (std::map<int, int>::iterator i = m_awaitingDatasets.begin(); - i != m_awaitingDatasets.end(); ++i) { - if (haveModel(i->second) && - m_models[i->second] == m_currentDataset) { - m_awaitingDatasets.erase(i); - foundInAwaiting = true; - break; - } - } - - if (!foundInAwaiting) { - std::cerr << "WARNING: SV-XML: Dataset precedes model, or no model uses dataset" << std::endl; - } - } - - m_currentDataset = 0; - - } else if (name == "data") { - - addUnaddedModels(); - m_inData = false; - - } else if (name == "derivation") { - - if (!m_currentDerivedModel) { - if (m_currentDerivedModel < 0) { - std::cerr << "WARNING: SV-XML: Bad derivation output model id " - << m_currentDerivedModelId << std::endl; - } else if (haveModel(m_currentDerivedModelId)) { - std::cerr << "WARNING: SV-XML: Derivation has existing model " - << m_currentDerivedModelId - << " as target, not regenerating" << std::endl; - } else { - m_currentDerivedModel = m_models[m_currentDerivedModelId] = - m_document->addDerivedModel(m_currentTransform, - m_currentTransformSource, - m_currentTransformContext, - m_currentTransformConfiguration); - } - } else { - m_document->addDerivedModel(m_currentTransform, - m_currentTransformSource, - m_currentTransformContext, - m_currentDerivedModel, - m_currentTransformConfiguration); - } - - m_addedModels.insert(m_currentDerivedModel); - m_currentDerivedModel = 0; - m_currentDerivedModelId = -1; - m_currentTransform = ""; - m_currentTransformConfiguration = ""; - - } else if (name == "row") { - m_inRow = false; - } else if (name == "layer") { - m_inLayer = false; - } else if (name == "view") { - m_inView = false; - } else if (name == "selections") { - m_inSelections = false; - } else if (name == "playparameters") { - m_currentPlayParameters = 0; - } - - return true; -} - -bool -SVFileReader::error(const QXmlParseException &exception) -{ - m_errorString = - QString("ERROR: SV-XML: %1 at line %2, column %3") - .arg(exception.message()) - .arg(exception.lineNumber()) - .arg(exception.columnNumber()); - std::cerr << m_errorString.toLocal8Bit().data() << std::endl; - return QXmlDefaultHandler::error(exception); -} - -bool -SVFileReader::fatalError(const QXmlParseException &exception) -{ - m_errorString = - QString("FATAL ERROR: SV-XML: %1 at line %2, column %3") - .arg(exception.message()) - .arg(exception.lineNumber()) - .arg(exception.columnNumber()); - std::cerr << m_errorString.toLocal8Bit().data() << std::endl; - return QXmlDefaultHandler::fatalError(exception); -} - - -#define READ_MANDATORY(TYPE, NAME, CONVERSION) \ - TYPE NAME = attributes.value(#NAME).trimmed().CONVERSION(&ok); \ - if (!ok) { \ - std::cerr << "WARNING: SV-XML: Missing or invalid mandatory " #TYPE " attribute \"" #NAME "\"" << std::endl; \ - return false; \ - } - -bool -SVFileReader::readWindow(const QXmlAttributes &attributes) -{ - bool ok = false; - - READ_MANDATORY(int, width, toInt); - READ_MANDATORY(int, height, toInt); - - m_paneCallback.setWindowSize(width, height); - return true; -} - -void -SVFileReader::addUnaddedModels() -{ - std::set<Model *> unaddedModels; - - for (std::map<int, Model *>::iterator i = m_models.begin(); - i != m_models.end(); ++i) { - if (m_addedModels.find(i->second) == m_addedModels.end()) { - unaddedModels.insert(i->second); - } - } - - for (std::set<Model *>::iterator i = unaddedModels.begin(); - i != unaddedModels.end(); ++i) { - m_document->addImportedModel(*i); - m_addedModels.insert(*i); - } -} - -bool -SVFileReader::readModel(const QXmlAttributes &attributes) -{ - bool ok = false; - - READ_MANDATORY(int, id, toInt); - - if (haveModel(id)) { - std::cerr << "WARNING: SV-XML: Ignoring duplicate model id " << id - << std::endl; - return false; - } - - QString name = attributes.value("name"); - - std::cerr << "SVFileReader::readModel: model name \"" << name.toStdString() << "\"" << std::endl; - - READ_MANDATORY(int, sampleRate, toInt); - - QString type = attributes.value("type").trimmed(); - bool mainModel = (attributes.value("mainModel").trimmed() == "true"); - - if (type == "wavefile") { - - WaveFileModel *model = 0; - FileFinder *ff = FileFinder::getInstance(); - QString originalPath = attributes.value("file"); - QString path = ff->find(FileFinder::AudioFile, - originalPath, m_location); - - FileSource file(path); - file.waitForStatus(); - - if (!file.isOK()) { - std::cerr << "SVFileReader::readModel: Failed to retrieve file \"" << path.toStdString() << "\" for wave file model: " << file.getErrorString().toStdString() << std::endl; - } else if (!file.isAvailable()) { - std::cerr << "SVFileReader::readModel: Failed to retrieve file \"" << path.toStdString() << "\" for wave file model: Source unavailable" << std::endl; - } else { - - file.waitForData(); - model = new WaveFileModel(file); - if (!model->isOK()) { - delete model; - model = 0; - } - } - - if (!model) return false; - - model->setObjectName(name); - m_models[id] = model; - if (mainModel) { - m_document->setMainModel(model); - m_addedModels.insert(model); - } - // Derived models will be added when their derivation - // is found. - - return true; - - } else if (type == "dense") { - - READ_MANDATORY(int, dimensions, toInt); - - // Currently the only dense model we support here is the dense - // 3d model. Dense time-value models are always file-backed - // waveform data, at this point, and they come in as wavefile - // models. - - if (dimensions == 3) { - - READ_MANDATORY(int, windowSize, toInt); - READ_MANDATORY(int, yBinCount, toInt); - - EditableDenseThreeDimensionalModel *model = - new EditableDenseThreeDimensionalModel - (sampleRate, windowSize, yBinCount); - - float minimum = attributes.value("minimum").trimmed().toFloat(&ok); - if (ok) model->setMinimumLevel(minimum); - - float maximum = attributes.value("maximum").trimmed().toFloat(&ok); - if (ok) model->setMaximumLevel(maximum); - - int dataset = attributes.value("dataset").trimmed().toInt(&ok); - if (ok) m_awaitingDatasets[dataset] = id; - - model->setObjectName(name); - m_models[id] = model; - return true; - - } else { - - std::cerr << "WARNING: SV-XML: Unexpected dense model dimension (" - << dimensions << ")" << std::endl; - } - } else if (type == "sparse") { - - READ_MANDATORY(int, dimensions, toInt); - - if (dimensions == 1) { - - READ_MANDATORY(int, resolution, toInt); - - if (attributes.value("subtype") == "image") { - - bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true"); - ImageModel *model = new ImageModel(sampleRate, resolution, - notifyOnAdd); - model->setObjectName(name); - m_models[id] = model; - - } else { - - SparseOneDimensionalModel *model = new SparseOneDimensionalModel - (sampleRate, resolution); - model->setObjectName(name); - m_models[id] = model; - } - - int dataset = attributes.value("dataset").trimmed().toInt(&ok); - if (ok) m_awaitingDatasets[dataset] = id; - - return true; - - } else if (dimensions == 2 || dimensions == 3) { - - READ_MANDATORY(int, resolution, toInt); - - bool haveMinMax = true; - float minimum = attributes.value("minimum").trimmed().toFloat(&ok); - if (!ok) haveMinMax = false; - float maximum = attributes.value("maximum").trimmed().toFloat(&ok); - if (!ok) haveMinMax = false; - - float valueQuantization = - attributes.value("valueQuantization").trimmed().toFloat(&ok); - - bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true"); - - QString units = attributes.value("units"); - - if (dimensions == 2) { - if (attributes.value("subtype") == "text") { - TextModel *model = new TextModel - (sampleRate, resolution, notifyOnAdd); - model->setObjectName(name); - m_models[id] = model; - } else { - SparseTimeValueModel *model; - if (haveMinMax) { - model = new SparseTimeValueModel - (sampleRate, resolution, minimum, maximum, notifyOnAdd); - } else { - model = new SparseTimeValueModel - (sampleRate, resolution, notifyOnAdd); - } - model->setScaleUnits(units); - model->setObjectName(name); - m_models[id] = model; - } - } else { - NoteModel *model; - if (haveMinMax) { - model = new NoteModel - (sampleRate, resolution, minimum, maximum, notifyOnAdd); - } else { - model = new NoteModel - (sampleRate, resolution, notifyOnAdd); - } - model->setValueQuantization(valueQuantization); - model->setScaleUnits(units); - model->setObjectName(name); - m_models[id] = model; - } - - int dataset = attributes.value("dataset").trimmed().toInt(&ok); - if (ok) m_awaitingDatasets[dataset] = id; - - return true; - - } else { - - std::cerr << "WARNING: SV-XML: Unexpected sparse model dimension (" - << dimensions << ")" << std::endl; - } - } else { - - std::cerr << "WARNING: SV-XML: Unexpected model type \"" - << type.toLocal8Bit().data() << "\" for model id " << id << std::endl; - } - - return false; -} - -bool -SVFileReader::readView(const QXmlAttributes &attributes) -{ - QString type = attributes.value("type"); - m_currentPane = 0; - - if (type != "pane") { - std::cerr << "WARNING: SV-XML: Unexpected view type \"" - << type.toLocal8Bit().data() << "\"" << std::endl; - return false; - } - - m_currentPane = m_paneCallback.addPane(); - - if (!m_currentPane) { - std::cerr << "WARNING: SV-XML: Internal error: Failed to add pane!" - << std::endl; - return false; - } - - bool ok = false; - - View *view = m_currentPane; - - // The view properties first - - READ_MANDATORY(size_t, centre, toUInt); - READ_MANDATORY(size_t, zoom, toUInt); - READ_MANDATORY(int, followPan, toInt); - READ_MANDATORY(int, followZoom, toInt); - QString tracking = attributes.value("tracking"); - - // Specify the follow modes before we set the actual values - view->setFollowGlobalPan(followPan); - view->setFollowGlobalZoom(followZoom); - view->setPlaybackFollow(tracking == "scroll" ? PlaybackScrollContinuous : - tracking == "page" ? PlaybackScrollPage - : PlaybackIgnore); - - // Then set these values - view->setCentreFrame(centre); - view->setZoomLevel(zoom); - - // And pane properties - READ_MANDATORY(int, centreLineVisible, toInt); - m_currentPane->setCentreLineVisible(centreLineVisible); - - int height = attributes.value("height").toInt(&ok); - if (ok) { - m_currentPane->resize(m_currentPane->width(), height); - } - - return true; -} - -bool -SVFileReader::readLayer(const QXmlAttributes &attributes) -{ - QString type = attributes.value("type"); - - int id; - bool ok = false; - id = attributes.value("id").trimmed().toInt(&ok); - - if (!ok) { - std::cerr << "WARNING: SV-XML: No layer id for layer of type \"" - << type.toLocal8Bit().data() - << "\"" << std::endl; - return false; - } - - Layer *layer = 0; - bool isNewLayer = false; - - // Layers are expected to be defined in layer elements in the data - // section, and referred to in layer elements in the view - // sections. So if we're in the data section, we expect this - // layer not to exist already; if we're in the view section, we - // expect it to exist. - - if (m_inData) { - - if (m_layers.find(id) != m_layers.end()) { - std::cerr << "WARNING: SV-XML: Ignoring duplicate layer id " << id - << " in data section" << std::endl; - return false; - } - - layer = m_layers[id] = m_document->createLayer - (LayerFactory::getInstance()->getLayerTypeForName(type)); - - if (layer) { - m_layers[id] = layer; - isNewLayer = true; - } - - } else { - - if (!m_currentPane) { - std::cerr << "WARNING: SV-XML: No current pane for layer " << id - << " in view section" << std::endl; - return false; - } - - if (m_layers.find(id) != m_layers.end()) { - - layer = m_layers[id]; - - } else { - std::cerr << "WARNING: SV-XML: Layer id " << id - << " in view section has not been defined -- defining it here" - << std::endl; - - layer = m_document->createLayer - (LayerFactory::getInstance()->getLayerTypeForName(type)); - - if (layer) { - m_layers[id] = layer; - isNewLayer = true; - } - } - } - - if (!layer) { - std::cerr << "WARNING: SV-XML: Failed to add layer of type \"" - << type.toLocal8Bit().data() - << "\"" << std::endl; - return false; - } - - if (isNewLayer) { - - QString name = attributes.value("name"); - layer->setObjectName(name); - - int modelId; - bool modelOk = false; - modelId = attributes.value("model").trimmed().toInt(&modelOk); - - if (modelOk) { - if (haveModel(modelId)) { - Model *model = m_models[modelId]; - m_document->setModel(layer, model); - } else { - std::cerr << "WARNING: SV-XML: Unknown model id " << modelId - << " in layer definition" << std::endl; - } - } - - layer->setProperties(attributes); - } - - if (!m_inData && m_currentPane) { - - QString visible = attributes.value("visible"); - bool dormant = (visible == "false"); - - // We need to do this both before and after adding the layer - // to the view -- we need it to be dormant if appropriate - // before it's actually added to the view so that any property - // box gets the right state when it's added, but the add layer - // command sets dormant to false because it assumes it may be - // restoring a previously dormant layer, so we need to set it - // again afterwards too. Hm - layer->setLayerDormant(m_currentPane, dormant); - - m_document->addLayerToView(m_currentPane, layer); - - layer->setLayerDormant(m_currentPane, dormant); - } - - m_currentLayer = layer; - m_inLayer = true; - - return true; -} - -bool -SVFileReader::readDatasetStart(const QXmlAttributes &attributes) -{ - bool ok = false; - - READ_MANDATORY(int, id, toInt); - READ_MANDATORY(int, dimensions, toInt); - - if (m_awaitingDatasets.find(id) == m_awaitingDatasets.end()) { - std::cerr << "WARNING: SV-XML: Unwanted dataset " << id << std::endl; - return false; - } - - int modelId = m_awaitingDatasets[id]; - - Model *model = 0; - if (haveModel(modelId)) { - model = m_models[modelId]; - } else { - std::cerr << "WARNING: SV-XML: Internal error: Unknown model " << modelId - << " expecting dataset " << id << std::endl; - return false; - } - - bool good = false; - - switch (dimensions) { - case 1: - if (dynamic_cast<SparseOneDimensionalModel *>(model)) good = true; - else if (dynamic_cast<ImageModel *>(model)) good = true; - break; - - case 2: - if (dynamic_cast<SparseTimeValueModel *>(model)) good = true; - else if (dynamic_cast<TextModel *>(model)) good = true; - break; - - case 3: - if (dynamic_cast<NoteModel *>(model)) good = true; - else if (dynamic_cast<EditableDenseThreeDimensionalModel *>(model)) { - m_datasetSeparator = attributes.value("separator"); - good = true; - } - break; - } - - if (!good) { - std::cerr << "WARNING: SV-XML: Model id " << modelId << " has wrong number of dimensions or inappropriate type for " << dimensions << "-D dataset " << id << std::endl; - m_currentDataset = 0; - return false; - } - - m_currentDataset = model; - return true; -} - -bool -SVFileReader::addPointToDataset(const QXmlAttributes &attributes) -{ - bool ok = false; - - READ_MANDATORY(int, frame, toInt); - -// std::cerr << "SVFileReader::addPointToDataset: frame = " << frame << std::endl; - - SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> - (m_currentDataset); - - if (sodm) { -// std::cerr << "Current dataset is a sparse one dimensional model" << std::endl; - QString label = attributes.value("label"); - sodm->addPoint(SparseOneDimensionalModel::Point(frame, label)); - return true; - } - - SparseTimeValueModel *stvm = dynamic_cast<SparseTimeValueModel *> - (m_currentDataset); - - if (stvm) { -// std::cerr << "Current dataset is a sparse time-value model" << std::endl; - float value = 0.0; - value = attributes.value("value").trimmed().toFloat(&ok); - QString label = attributes.value("label"); - stvm->addPoint(SparseTimeValueModel::Point(frame, value, label)); - return ok; - } - - NoteModel *nm = dynamic_cast<NoteModel *>(m_currentDataset); - - if (nm) { -// std::cerr << "Current dataset is a note model" << std::endl; - float value = 0.0; - value = attributes.value("value").trimmed().toFloat(&ok); - size_t duration = 0; - duration = attributes.value("duration").trimmed().toUInt(&ok); - QString label = attributes.value("label"); - nm->addPoint(NoteModel::Point(frame, value, duration, label)); - return ok; - } - - TextModel *tm = dynamic_cast<TextModel *>(m_currentDataset); - - if (tm) { -// std::cerr << "Current dataset is a text model" << std::endl; - float height = 0.0; - height = attributes.value("height").trimmed().toFloat(&ok); - QString label = attributes.value("label"); -// std::cerr << "SVFileReader::addPointToDataset: TextModel: frame = " << frame << ", height = " << height << ", label = " << label.toStdString() << ", ok = " << ok << std::endl; - tm->addPoint(TextModel::Point(frame, height, label)); - return ok; - } - - ImageModel *im = dynamic_cast<ImageModel *>(m_currentDataset); - - if (im) { -// std::cerr << "Current dataset is an image model" << std::endl; - QString image = attributes.value("image"); - QString label = attributes.value("label"); -// std::cerr << "SVFileReader::addPointToDataset: ImageModel: frame = " << frame << ", image = " << image.toStdString() << ", label = " << label.toStdString() << ", ok = " << ok << std::endl; - im->addPoint(ImageModel::Point(frame, image, label)); - return ok; - } - - std::cerr << "WARNING: SV-XML: Point element found in non-point dataset" << std::endl; - - return false; -} - -bool -SVFileReader::addBinToDataset(const QXmlAttributes &attributes) -{ - EditableDenseThreeDimensionalModel *dtdm = - dynamic_cast<EditableDenseThreeDimensionalModel *> - (m_currentDataset); - - if (dtdm) { - - bool ok = false; - int n = attributes.value("number").trimmed().toInt(&ok); - if (!ok) { - std::cerr << "WARNING: SV-XML: Missing or invalid bin number" - << std::endl; - return false; - } - - QString name = attributes.value("name"); - - dtdm->setBinName(n, name); - return true; - } - - std::cerr << "WARNING: SV-XML: Bin definition found in incompatible dataset" << std::endl; - - return false; -} - - -bool -SVFileReader::addRowToDataset(const QXmlAttributes &attributes) -{ - m_inRow = false; - - bool ok = false; - m_rowNumber = attributes.value("n").trimmed().toInt(&ok); - if (!ok) { - std::cerr << "WARNING: SV-XML: Missing or invalid row number" - << std::endl; - return false; - } - - m_inRow = true; - -// std::cerr << "SV-XML: In row " << m_rowNumber << std::endl; - - return true; -} - -bool -SVFileReader::readRowData(const QString &text) -{ - EditableDenseThreeDimensionalModel *dtdm = - dynamic_cast<EditableDenseThreeDimensionalModel *> - (m_currentDataset); - - bool warned = false; - - if (dtdm) { - QStringList data = text.split(m_datasetSeparator); - - DenseThreeDimensionalModel::Column values; - - for (QStringList::iterator i = data.begin(); i != data.end(); ++i) { - - if (values.size() == dtdm->getHeight()) { - if (!warned) { - std::cerr << "WARNING: SV-XML: Too many y-bins in 3-D dataset row " - << m_rowNumber << std::endl; - warned = true; - } - } - - bool ok; - float value = i->toFloat(&ok); - if (!ok) { - std::cerr << "WARNING: SV-XML: Bad floating-point value " - << i->toLocal8Bit().data() - << " in row data" << std::endl; - } else { - values.push_back(value); - } - } - - dtdm->setColumn(m_rowNumber, values); - return true; - } - - std::cerr << "WARNING: SV-XML: Row data found in non-row dataset" << std::endl; - - return false; -} - -bool -SVFileReader::readDerivation(const QXmlAttributes &attributes) -{ - int modelId = 0; - bool modelOk = false; - modelId = attributes.value("model").trimmed().toInt(&modelOk); - - if (!modelOk) { - std::cerr << "WARNING: SV-XML: No model id specified for derivation" << std::endl; - return false; - } - - QString transform = attributes.value("transform"); - - if (haveModel(modelId)) { - m_currentDerivedModel = m_models[modelId]; - } else { - // we'll regenerate the model when the derivation element ends - m_currentDerivedModel = 0; - } - - m_currentDerivedModelId = modelId; - - int sourceId = 0; - bool sourceOk = false; - sourceId = attributes.value("source").trimmed().toInt(&sourceOk); - - if (sourceOk && haveModel(sourceId)) { - m_currentTransformSource = m_models[sourceId]; - } else { - m_currentTransformSource = m_document->getMainModel(); - } - - m_currentTransform = transform; - m_currentTransformConfiguration = ""; - - m_currentTransformContext = PluginTransform::ExecutionContext(); - - bool ok = false; - int channel = attributes.value("channel").trimmed().toInt(&ok); - if (ok) m_currentTransformContext.channel = channel; - - int domain = attributes.value("domain").trimmed().toInt(&ok); - if (ok) m_currentTransformContext.domain = Vamp::Plugin::InputDomain(domain); - - int stepSize = attributes.value("stepSize").trimmed().toInt(&ok); - if (ok) m_currentTransformContext.stepSize = stepSize; - - int blockSize = attributes.value("blockSize").trimmed().toInt(&ok); - if (ok) m_currentTransformContext.blockSize = blockSize; - - int windowType = attributes.value("windowType").trimmed().toInt(&ok); - if (ok) m_currentTransformContext.windowType = WindowType(windowType); - - QString startFrameStr = attributes.value("startFrame"); - QString durationStr = attributes.value("duration"); - - size_t startFrame = 0; - size_t duration = 0; - - if (startFrameStr != "") { - startFrame = startFrameStr.trimmed().toInt(&ok); - if (!ok) startFrame = 0; - } - if (durationStr != "") { - duration = durationStr.trimmed().toInt(&ok); - if (!ok) duration = 0; - } - - m_currentTransformContext.startFrame = startFrame; - m_currentTransformContext.duration = duration; - - return true; -} - -bool -SVFileReader::readPlayParameters(const QXmlAttributes &attributes) -{ - m_currentPlayParameters = 0; - - int modelId = 0; - bool modelOk = false; - modelId = attributes.value("model").trimmed().toInt(&modelOk); - - if (!modelOk) { - std::cerr << "WARNING: SV-XML: No model id specified for play parameters" << std::endl; - return false; - } - - if (haveModel(modelId)) { - - bool ok = false; - - PlayParameters *parameters = PlayParameterRepository::getInstance()-> - getPlayParameters(m_models[modelId]); - - if (!parameters) { - std::cerr << "WARNING: SV-XML: Play parameters for model " - << modelId - << " not found - has model been added to document?" - << std::endl; - return false; - } - - bool muted = (attributes.value("mute").trimmed() == "true"); - parameters->setPlayMuted(muted); - - float pan = attributes.value("pan").toFloat(&ok); - if (ok) parameters->setPlayPan(pan); - - float gain = attributes.value("gain").toFloat(&ok); - if (ok) parameters->setPlayGain(gain); - - QString pluginId = attributes.value("pluginId"); - if (pluginId != "") parameters->setPlayPluginId(pluginId); - - m_currentPlayParameters = parameters; - -// std::cerr << "Current play parameters for model: " << m_models[modelId] << ": " << m_currentPlayParameters << std::endl; - - } else { - - std::cerr << "WARNING: SV-XML: Unknown model " << modelId - << " for play parameters" << std::endl; - return false; - } - - return true; -} - -bool -SVFileReader::readPlugin(const QXmlAttributes &attributes) -{ - if (m_currentDerivedModelId < 0 && !m_currentPlayParameters) { - std::cerr << "WARNING: SV-XML: Plugin found outside derivation or play parameters" << std::endl; - return false; - } - - QString configurationXml = "<plugin"; - - for (int i = 0; i < attributes.length(); ++i) { - configurationXml += QString(" %1=\"%2\"") - .arg(attributes.qName(i)) - .arg(XmlExportable::encodeEntities(attributes.value(i))); - } - - configurationXml += "/>"; - - if (m_currentPlayParameters) { - m_currentPlayParameters->setPlayPluginConfiguration(configurationXml); - } else { - m_currentTransformConfiguration += configurationXml; - } - - return true; -} - -bool -SVFileReader::readSelection(const QXmlAttributes &attributes) -{ - bool ok; - - READ_MANDATORY(int, start, toInt); - READ_MANDATORY(int, end, toInt); - - m_paneCallback.addSelection(start, end); - - return true; -} - -bool -SVFileReader::readMeasurement(const QXmlAttributes &attributes) -{ - std::cerr << "SVFileReader::readMeasurement: inLayer " - << m_inLayer << ", layer " << m_currentLayer << std::endl; - - if (!m_inLayer) { - std::cerr << "WARNING: SV-XML: Measurement found outside layer" << std::endl; - return false; - } - - m_currentLayer->addMeasurementRect(attributes); - return true; -} - -SVFileReaderPaneCallback::~SVFileReaderPaneCallback() -{ -} -
--- a/document/SVFileReader.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,234 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _SV_FILE_READER_H_ -#define _SV_FILE_READER_H_ - -#include "layer/LayerFactory.h" -#include "transform/Transform.h" -#include "transform/PluginTransform.h" - -#include <QXmlDefaultHandler> - -#include <map> - -class Pane; -class Model; -class Document; -class PlayParameters; - -class SVFileReaderPaneCallback -{ -public: - virtual ~SVFileReaderPaneCallback(); - virtual Pane *addPane() = 0; - virtual void setWindowSize(int width, int height) = 0; - virtual void addSelection(int start, int end) = 0; -}; - -/** - SVFileReader loads Sonic Visualiser XML files. (The SV file - format is bzipped XML.) - - Some notes about the SV XML format follow. We're very lazy with - our XML: there's no schema or DTD, and we depend heavily on - elements being in a particular order. - -\verbatim - - <sv> - - <data> - - <!-- The data section contains definitions of both models and - visual layers. Layers are considered data in the document; - the structure of views that displays the layers is not. --> - - <!-- id numbers are unique within the data type (i.e. no two - models can have the same id, but a model can have the same - id as a layer, etc). SV generates its id numbers just for - the purpose of cross-referencing within the current file; - they don't necessarily have any meaning once the file has - been loaded. --> - - <model id="0" name="..." type="..." ... /> - <model id="1" name="..." type="..." ... /> - - <!-- Models that have data associated with them store it - in a neighbouring dataset element. The dataset must follow - the model and precede any derivation or layer elements that - refer to the model. --> - - <model id="2" name="..." type="..." dataset="0" ... /> - - <dataset id="0" type="..."> - <point frame="..." value="..." ... /> - </dataset> - - <!-- Where one model is derived from another via a transform, - it has an associated derivation element. This must follow - both the source and target model elements. The source and - model attributes give the source model id and target model - id respectively. A model can have both dataset and - derivation elements; if it does, dataset must appear first. - If the model's data are not stored, but instead the model - is to be regenerated completely from the transform when - the session is reloaded, then the model should have _only_ - a derivation element, and no model element should appear - for it at all. --> - - <derivation source="0" model="2" transform="..." ...> - <plugin id="..." ... /> - </derivation> - - <!-- The playparameters element lists playback settings for - a model. --> - - <playparameters mute="false" pan="0" gain="1" model="1" ... /> - - <!-- Layer elements. The models must have already been defined. - The same model may appear in more than one layer (of more - than one type). --> - - <layer id="1" type="..." name="..." model="0" ... /> - <layer id="2" type="..." name="..." model="1" ... /> - - </data> - - - <display> - - <!-- The display element contains visual structure for the - layers. It's simpler than the data section. --> - - <!-- Overall preferred window size for this session. --> - - <window width="..." height="..."/> - - <!-- List of view elements to stack up. Each one contains - a list of layers in stacking order, back to front. --> - - <view type="pane" ...> - <layer id="1"/> - <layer id="2"/> - </view> - - <!-- The layer elements just refer to layers defined in the - data section, so they don't have to have any attributes - other than the id. For sort-of-historical reasons SV - actually does repeat the other attributes here, but - it doesn't need to. --> - - <view type="pane" ...> - <layer id="2"/> - <view> - - </display> - - - <!-- List of selected regions by audio frame extents. --> - - <selections> - <selection start="..." end="..."/> - </selections> - - - </sv> - -\endverbatim - */ - - -class SVFileReader : public QXmlDefaultHandler -{ -public: - SVFileReader(Document *document, - SVFileReaderPaneCallback &callback, - QString location = ""); // for audio file locate mechanism - virtual ~SVFileReader(); - - void parse(const QString &xmlData); - void parse(QXmlInputSource &source); - - bool isOK(); - QString getErrorString() const { return m_errorString; } - - // For loading a single layer onto an existing pane - void setCurrentPane(Pane *pane) { m_currentPane = pane; } - - virtual bool startElement(const QString &namespaceURI, - const QString &localName, - const QString &qName, - const QXmlAttributes& atts); - - virtual bool characters(const QString &); - - virtual bool endElement(const QString &namespaceURI, - const QString &localName, - const QString &qName); - - bool error(const QXmlParseException &exception); - bool fatalError(const QXmlParseException &exception); - -protected: - bool readWindow(const QXmlAttributes &); - bool readModel(const QXmlAttributes &); - bool readView(const QXmlAttributes &); - bool readLayer(const QXmlAttributes &); - bool readDatasetStart(const QXmlAttributes &); - bool addBinToDataset(const QXmlAttributes &); - bool addPointToDataset(const QXmlAttributes &); - bool addRowToDataset(const QXmlAttributes &); - bool readRowData(const QString &); - bool readDerivation(const QXmlAttributes &); - bool readPlayParameters(const QXmlAttributes &); - bool readPlugin(const QXmlAttributes &); - bool readSelection(const QXmlAttributes &); - bool readMeasurement(const QXmlAttributes &); - void addUnaddedModels(); - - bool haveModel(int id) { - return (m_models.find(id) != m_models.end()) && m_models[id]; - } - - Document *m_document; - SVFileReaderPaneCallback &m_paneCallback; - QString m_location; - Pane *m_currentPane; - std::map<int, Layer *> m_layers; - std::map<int, Model *> m_models; - std::set<Model *> m_addedModels; - std::map<int, int> m_awaitingDatasets; // map dataset id -> model id - Layer *m_currentLayer; - Model *m_currentDataset; - Model *m_currentDerivedModel; - int m_currentDerivedModelId; - PlayParameters *m_currentPlayParameters; - QString m_currentTransform; - Model *m_currentTransformSource; - PluginTransform::ExecutionContext m_currentTransformContext; - QString m_currentTransformConfiguration; - QString m_datasetSeparator; - bool m_inRow; - bool m_inLayer; - bool m_inView; - bool m_inData; - bool m_inSelections; - int m_rowNumber; - QString m_errorString; - bool m_ok; -}; - -#endif
--- a/main/MainWindow.cpp Wed Oct 24 16:00:30 2007 +0000 +++ b/main/MainWindow.cpp Wed Oct 24 16:34:31 2007 +0000 @@ -60,7 +60,7 @@ #include "data/fileio/FileSource.h" #include "data/fft/FFTDataServer.h" #include "base/RecentFiles.h" -#include "transform/TransformFactory.h" +#include "plugin/transform/TransformFactory.h" #include "base/PlayParameterRepository.h" #include "base/XmlExportable.h" #include "base/CommandHistory.h" @@ -68,7 +68,7 @@ #include "base/Clipboard.h" #include "base/UnitDatabase.h" #include "base/ColourDatabase.h" -#include "osc/OSCQueue.h" +#include "data/osc/OSCQueue.h" // For version information #include "vamp/vamp.h"
--- a/main/MainWindow.h Wed Oct 24 16:00:30 2007 +0000 +++ b/main/MainWindow.h Wed Oct 24 16:34:31 2007 +0000 @@ -21,14 +21,13 @@ #include <QUrl> #include <QPointer> -#include "MainWindowBase.h" - +#include "document/MainWindowBase.h" #include "base/Command.h" #include "view/ViewManager.h" #include "base/PropertyContainer.h" #include "base/RecentFiles.h" #include "layer/LayerFactory.h" -#include "transform/Transform.h" +#include "plugin/transform/Transform.h" #include "document/SVFileReader.h" #include "data/fileio/FileFinder.h" #include "data/fileio/FileSource.h"
--- a/main/MainWindowBase.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2006 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2007 Chris Cannam and QMUL. - - 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 "MainWindowBase.h" -#include "document/Document.h" - - -#include "view/Pane.h" -#include "view/PaneStack.h" -#include "data/model/WaveFileModel.h" -#include "data/model/SparseOneDimensionalModel.h" -#include "data/model/NoteModel.h" -#include "data/model/Labeller.h" -#include "view/ViewManager.h" - -#include "layer/WaveformLayer.h" -#include "layer/TimeRulerLayer.h" -#include "layer/TimeInstantLayer.h" -#include "layer/TimeValueLayer.h" -#include "layer/Colour3DPlotLayer.h" -#include "layer/SliceLayer.h" -#include "layer/SliceableLayer.h" -#include "layer/ImageLayer.h" - -#include "widgets/ListInputDialog.h" - -#include "audioio/AudioCallbackPlaySource.h" -#include "audioio/AudioCallbackPlayTarget.h" -#include "audioio/AudioTargetFactory.h" -#include "audioio/PlaySpeedRangeMapper.h" -#include "data/fileio/DataFileReaderFactory.h" -#include "data/fileio/PlaylistFileReader.h" -#include "data/fileio/WavFileWriter.h" -#include "data/fileio/CSVFileWriter.h" -#include "data/fileio/MIDIFileWriter.h" -#include "data/fileio/BZipFileDevice.h" -#include "data/fileio/FileSource.h" - -#include "data/fft/FFTDataServer.h" - -#include "base/RecentFiles.h" - -#include "base/PlayParameterRepository.h" -#include "base/XmlExportable.h" -#include "base/CommandHistory.h" -#include "base/Profiler.h" -#include "base/Preferences.h" - -#include "osc/OSCQueue.h" - -#include <QApplication> -#include <QMessageBox> -#include <QGridLayout> -#include <QLabel> -#include <QAction> -#include <QMenuBar> -#include <QToolBar> -#include <QInputDialog> -#include <QStatusBar> -#include <QTreeView> -#include <QFile> -#include <QFileInfo> -#include <QDir> -#include <QTextStream> -#include <QProcess> -#include <QShortcut> -#include <QSettings> -#include <QDateTime> -#include <QProcess> -#include <QCheckBox> -#include <QRegExp> -#include <QScrollArea> - -#include <iostream> -#include <cstdio> -#include <errno.h> - -using std::cerr; -using std::endl; - -using std::vector; -using std::map; -using std::set; - - -MainWindowBase::MainWindowBase(bool withAudioOutput, bool withOSCSupport) : - m_document(0), - m_paneStack(0), - m_viewManager(0), - m_timeRulerLayer(0), - m_audioOutput(withAudioOutput), - m_playSource(0), - m_playTarget(0), - m_oscQueue(withOSCSupport ? new OSCQueue() : 0), - m_recentFiles("RecentFiles", 20), - m_recentTransforms("RecentTransforms", 20), - m_documentModified(false), - m_openingAudioFile(false), - m_abandoning(false), - m_labeller(0) -{ - connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), - this, SLOT(documentModified())); - connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), - this, SLOT(documentRestored())); - - m_viewManager = new ViewManager(); - connect(m_viewManager, SIGNAL(selectionChanged()), - this, SLOT(updateMenuStates())); - connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), - this, SLOT(inProgressSelectionChanged())); - - Preferences::BackgroundMode mode = - Preferences::getInstance()->getBackgroundMode(); - m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); - if (mode != Preferences::BackgroundFromTheme) { - m_viewManager->setGlobalDarkBackground - (mode == Preferences::DarkBackground); - } - - m_paneStack = new PaneStack(0, m_viewManager); - connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), - this, SLOT(currentPaneChanged(Pane *))); - connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), - this, SLOT(currentLayerChanged(Pane *, Layer *))); - connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), - this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); - connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), - this, SLOT(contextHelpChanged(const QString &))); - connect(m_paneStack, SIGNAL(paneAdded(Pane *)), - this, SLOT(paneAdded(Pane *))); - connect(m_paneStack, SIGNAL(paneHidden(Pane *)), - this, SLOT(paneHidden(Pane *))); - connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)), - this, SLOT(paneAboutToBeDeleted(Pane *))); - connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)), - this, SLOT(paneDropAccepted(Pane *, QStringList))); - connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)), - this, SLOT(paneDropAccepted(Pane *, QString))); - - m_playSource = new AudioCallbackPlaySource(m_viewManager); - - connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), - this, SLOT(sampleRateMismatch(size_t, size_t, bool))); - connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), - this, SLOT(audioOverloadPluginDisabled())); - - connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), - this, SLOT(outputLevelsChanged(float, float))); - - connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), - this, SLOT(playbackFrameChanged(unsigned long))); - - connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), - this, SLOT(globalCentreFrameChanged(unsigned long))); - - connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), - this, SLOT(viewCentreFrameChanged(View *, unsigned long))); - - connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), - this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); - - connect(Preferences::getInstance(), - SIGNAL(propertyChanged(PropertyContainer::PropertyName)), - this, - SLOT(preferenceChanged(PropertyContainer::PropertyName))); - - if (m_oscQueue && m_oscQueue->isOK()) { - connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); - QTimer *oscTimer = new QTimer(this); - connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); - oscTimer->start(1000); - } - - Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter; - QSettings settings; - settings.beginGroup("MainWindow"); - labellerType = (Labeller::ValueType) - settings.value("labellertype", (int)labellerType).toInt(); - int cycle = settings.value("labellercycle", 4).toInt(); - settings.endGroup(); - - m_labeller = new Labeller(labellerType); - m_labeller->setCounterCycleSize(cycle); -} - -MainWindowBase::~MainWindowBase() -{ - delete m_playTarget; - delete m_playSource; - delete m_viewManager; - delete m_oscQueue; - Profiles::getInstance()->dump(); -} - -QString -MainWindowBase::getOpenFileName(FileFinder::FileType type) -{ - FileFinder *ff = FileFinder::getInstance(); - switch (type) { - case FileFinder::SessionFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::AudioFile: - return ff->getOpenFileName(type, m_audioFile); - case FileFinder::LayerFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::LayerFileNoMidi: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::SessionOrAudioFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::ImageFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::AnyFile: - if (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0) { // can import a layer - return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); - } else { - return ff->getOpenFileName(FileFinder::SessionOrAudioFile, - m_sessionFile); - } - } - return ""; -} - -QString -MainWindowBase::getSaveFileName(FileFinder::FileType type) -{ - FileFinder *ff = FileFinder::getInstance(); - switch (type) { - case FileFinder::SessionFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::AudioFile: - return ff->getSaveFileName(type, m_audioFile); - case FileFinder::LayerFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::LayerFileNoMidi: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::SessionOrAudioFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::ImageFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::AnyFile: - return ff->getSaveFileName(type, m_sessionFile); - } - return ""; -} - -void -MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path) -{ - FileFinder *ff = FileFinder::getInstance(); - ff->registerLastOpenedFilePath(type, path); -} - -void -MainWindowBase::updateMenuStates() -{ - Pane *currentPane = 0; - Layer *currentLayer = 0; - - if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentLayer = currentPane->getSelectedLayer(); - - bool haveCurrentPane = - (currentPane != 0); - bool haveCurrentLayer = - (haveCurrentPane && - (currentLayer != 0)); - bool haveMainModel = - (getMainModel() != 0); - bool havePlayTarget = - (m_playTarget != 0); - bool haveSelection = - (m_viewManager && - !m_viewManager->getSelections().empty()); - bool haveCurrentEditableLayer = - (haveCurrentLayer && - currentLayer->isLayerEditable()); - bool haveCurrentTimeInstantsLayer = - (haveCurrentLayer && - dynamic_cast<TimeInstantLayer *>(currentLayer)); - bool haveCurrentColour3DPlot = - (haveCurrentLayer && - dynamic_cast<Colour3DPlotLayer *>(currentLayer)); - bool haveClipboardContents = - (m_viewManager && - !m_viewManager->getClipboard().empty()); - - emit canAddPane(haveMainModel); - emit canDeleteCurrentPane(haveCurrentPane); - emit canZoom(haveMainModel && haveCurrentPane); - emit canScroll(haveMainModel && haveCurrentPane); - emit canAddLayer(haveMainModel && haveCurrentPane); - emit canImportMoreAudio(haveMainModel); - emit canImportLayer(haveMainModel && haveCurrentPane); - emit canExportAudio(haveMainModel); - emit canExportLayer(haveMainModel && - (haveCurrentEditableLayer || haveCurrentColour3DPlot)); - emit canExportImage(haveMainModel && haveCurrentPane); - emit canDeleteCurrentLayer(haveCurrentLayer); - emit canRenameLayer(haveCurrentLayer); - emit canEditLayer(haveCurrentEditableLayer); - emit canMeasureLayer(haveCurrentLayer); - emit canSelect(haveMainModel && haveCurrentPane); - emit canPlay(havePlayTarget); - emit canFfwd(true); - emit canRewind(true); - emit canPaste(haveCurrentEditableLayer && haveClipboardContents); - emit canInsertInstant(haveCurrentPane); - emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); - emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); - emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); - emit canClearSelection(haveSelection); - emit canEditSelection(haveSelection && haveCurrentEditableLayer); - emit canSave(m_sessionFile != "" && m_documentModified); -} - -void -MainWindowBase::documentModified() -{ -// std::cerr << "MainWindowBase::documentModified" << std::endl; - - if (!m_documentModified) { - //!!! this in subclass implementation? - setWindowTitle(tr("%1 (modified)").arg(windowTitle())); - } - - m_documentModified = true; - updateMenuStates(); -} - -void -MainWindowBase::documentRestored() -{ -// std::cerr << "MainWindowBase::documentRestored" << std::endl; - - if (m_documentModified) { - //!!! this in subclass implementation? - QString wt(windowTitle()); - wt.replace(tr(" (modified)"), ""); - setWindowTitle(wt); - } - - m_documentModified = false; - updateMenuStates(); -} - -void -MainWindowBase::playLoopToggled() -{ - QAction *action = dynamic_cast<QAction *>(sender()); - - if (action) { - m_viewManager->setPlayLoopMode(action->isChecked()); - } else { - m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); - } -} - -void -MainWindowBase::playSelectionToggled() -{ - QAction *action = dynamic_cast<QAction *>(sender()); - - if (action) { - m_viewManager->setPlaySelectionMode(action->isChecked()); - } else { - m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); - } -} - -void -MainWindowBase::playSoloToggled() -{ - QAction *action = dynamic_cast<QAction *>(sender()); - - if (action) { - m_viewManager->setPlaySoloMode(action->isChecked()); - } else { - m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); - } - - if (m_viewManager->getPlaySoloMode()) { - currentPaneChanged(m_paneStack->getCurrentPane()); - } else { - m_viewManager->setPlaybackModel(0); - if (m_playSource) { - m_playSource->clearSoloModelSet(); - } - } -} - -void -MainWindowBase::currentPaneChanged(Pane *p) -{ - updateMenuStates(); - updateVisibleRangeDisplay(p); - - if (!p) return; - - if (!(m_viewManager && - m_playSource && - m_viewManager->getPlaySoloMode())) { - if (m_viewManager) m_viewManager->setPlaybackModel(0); - return; - } - - Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); - - View::ModelSet soloModels = p->getModels(); - - for (View::ModelSet::iterator mi = soloModels.begin(); - mi != soloModels.end(); ++mi) { - if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) { - m_viewManager->setPlaybackModel(*mi); - } - } - - RangeSummarisableTimeValueModel *a = - dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel); - RangeSummarisableTimeValueModel *b = - dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager-> - getPlaybackModel()); - - m_playSource->setSoloModelSet(soloModels); - - if (a && b && (a != b)) { - int frame = m_playSource->getCurrentPlayingFrame(); - //!!! I don't really believe that these functions are the right way around - int rframe = a->alignFromReference(frame); - int bframe = b->alignToReference(rframe); - if (m_playSource->isPlaying()) m_playSource->play(bframe); - } -} - -void -MainWindowBase::currentLayerChanged(Pane *p, Layer *) -{ - updateMenuStates(); - updateVisibleRangeDisplay(p); -} - -void -MainWindowBase::selectAll() -{ - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), - getMainModel()->getEndFrame())); -} - -void -MainWindowBase::selectToStart() -{ - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), - m_viewManager->getGlobalCentreFrame())); -} - -void -MainWindowBase::selectToEnd() -{ - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), - getMainModel()->getEndFrame())); -} - -void -MainWindowBase::selectVisible() -{ - Model *model = getMainModel(); - if (!model) return; - - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - size_t startFrame, endFrame; - - if (currentPane->getStartFrame() < 0) startFrame = 0; - else startFrame = currentPane->getStartFrame(); - - if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); - else endFrame = currentPane->getEndFrame(); - - m_viewManager->setSelection(Selection(startFrame, endFrame)); -} - -void -MainWindowBase::clearSelection() -{ - m_viewManager->clearSelections(); -} - -void -MainWindowBase::cut() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - Layer *layer = currentPane->getSelectedLayer(); - if (!layer) return; - - Clipboard &clipboard = m_viewManager->getClipboard(); - clipboard.clear(); - - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - - CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - layer->copy(*i, clipboard); - layer->deleteSelection(*i); - } - - CommandHistory::getInstance()->endCompoundOperation(); -} - -void -MainWindowBase::copy() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - Layer *layer = currentPane->getSelectedLayer(); - if (!layer) return; - - Clipboard &clipboard = m_viewManager->getClipboard(); - clipboard.clear(); - - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - layer->copy(*i, clipboard); - } -} - -void -MainWindowBase::paste() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - //!!! if we have no current layer, we should create one of the most - // appropriate type - - Layer *layer = currentPane->getSelectedLayer(); - if (!layer) return; - - Clipboard &clipboard = m_viewManager->getClipboard(); - Clipboard::PointList contents = clipboard.getPoints(); -/* - long minFrame = 0; - bool have = false; - for (int i = 0; i < contents.size(); ++i) { - if (!contents[i].haveFrame()) continue; - if (!have || contents[i].getFrame() < minFrame) { - minFrame = contents[i].getFrame(); - have = true; - } - } - - long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; - - layer->paste(clipboard, frameOffset); -*/ - layer->paste(clipboard, 0, true); -} - -void -MainWindowBase::deleteSelected() -{ - if (m_paneStack->getCurrentPane() && - m_paneStack->getCurrentPane()->getSelectedLayer()) { - - Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer(); - - if (m_viewManager && - (m_viewManager->getToolMode() == ViewManager::MeasureMode)) { - - layer->deleteCurrentMeasureRect(); - - } else { - - MultiSelection::SelectionList selections = - m_viewManager->getSelections(); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - layer->deleteSelection(*i); - } - } - } -} - -void -MainWindowBase::insertInstant() -{ - int frame = m_viewManager->getPlaybackFrame(); - insertInstantAt(frame); -} - -void -MainWindowBase::insertInstantsAtBoundaries() -{ - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - size_t start = i->getStartFrame(); - size_t end = i->getEndFrame(); - if (start != end) { - insertInstantAt(i->getStartFrame()); - insertInstantAt(i->getEndFrame()); - } - } -} - -void -MainWindowBase::insertInstantAt(size_t frame) -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) { - return; - } - - Layer *layer = dynamic_cast<TimeInstantLayer *> - (pane->getSelectedLayer()); - - if (!layer) { - for (int i = pane->getLayerCount(); i > 0; --i) { - layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); - if (layer) break; - } - - if (!layer) { - CommandHistory::getInstance()->startCompoundOperation - (tr("Add Point"), true); - layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); - if (layer) { - m_document->addLayerToView(pane, layer); - m_paneStack->setCurrentLayer(pane, layer); - } - CommandHistory::getInstance()->endCompoundOperation(); - } - } - - if (layer) { - - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> - (model); - - if (sodm) { - SparseOneDimensionalModel::Point point(frame, ""); - - SparseOneDimensionalModel::Point prevPoint(0); - bool havePrevPoint = false; - - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); - - if (m_labeller->actingOnPrevPoint()) { - - SparseOneDimensionalModel::PointList prevPoints = - sodm->getPreviousPoints(frame); - - if (!prevPoints.empty()) { - prevPoint = *prevPoints.begin(); - havePrevPoint = true; - } - } - - if (m_labeller) { - - m_labeller->setSampleRate(sodm->getSampleRate()); - - if (havePrevPoint) { - command->deletePoint(prevPoint); - } - - m_labeller->label<SparseOneDimensionalModel::Point> - (point, havePrevPoint ? &prevPoint : 0); - - if (havePrevPoint) { - command->addPoint(prevPoint); - } - } - - command->addPoint(point); - - command->setName(tr("Add Point at %1 s") - .arg(RealTime::frame2RealTime - (frame, - sodm->getSampleRate()) - .toText(false).c_str())); - - command->finish(); - } - } -} - -void -MainWindowBase::renumberInstants() -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) return; - - Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer()); - if (!layer) return; - - MultiSelection ms(m_viewManager->getSelection()); - - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> - (model); - if (!sodm) return; - - if (!m_labeller) return; - - Labeller labeller(*m_labeller); - labeller.setSampleRate(sodm->getSampleRate()); - - // This uses a command - - labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); -} - -MainWindowBase::FileOpenStatus -MainWindowBase::open(QString fileOrUrl, AudioFileOpenMode mode) -{ - return open(FileSource(fileOrUrl), mode); -} - -MainWindowBase::FileOpenStatus -MainWindowBase::open(FileSource source, AudioFileOpenMode mode) -{ - FileOpenStatus status; - - if (!source.isAvailable()) return FileOpenFailed; - source.waitForData(); - - bool canImportLayer = (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0); - - if ((status = openAudio(source, mode)) != FileOpenFailed) { - return status; - } else if ((status = openSession(source)) != FileOpenFailed) { - return status; - } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) { - return status; - } else if (!canImportLayer) { - return FileOpenWrongMode; - } else if ((status = openImage(source)) != FileOpenFailed) { - return status; - } else if ((status = openLayer(source)) != FileOpenFailed) { - return status; - } else { - return FileOpenFailed; - } -} - -MainWindowBase::FileOpenStatus -MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode) -{ - std::cerr << "MainWindowBase::openAudio(" << source.getLocation().toStdString() << ")" << std::endl; - - if (!source.isAvailable()) return FileOpenFailed; - source.waitForData(); - - m_openingAudioFile = true; - - size_t rate = 0; - - if (Preferences::getInstance()->getResampleOnLoad()) { - rate = m_playSource->getSourceSampleRate(); - } - - WaveFileModel *newModel = new WaveFileModel(source, rate); - - if (!newModel->isOK()) { - delete newModel; - m_openingAudioFile = false; - return FileOpenFailed; - } - - std::cerr << "mode = " << mode << std::endl; - - if (mode == AskUser) { - if (getMainModel()) { - - static bool prevSetAsMain = true; - bool setAsMain = true; - - QStringList items; - items << tr("Replace the existing main waveform") - << tr("Load this file into a new waveform pane"); - - bool ok = false; - QString item = ListInputDialog::getItem - (this, tr("Select target for import"), - tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), - items, prevSetAsMain ? 0 : 1, &ok); - - if (!ok || item.isEmpty()) { - delete newModel; - m_openingAudioFile = false; - return FileOpenCancelled; - } - - setAsMain = (item == items[0]); - prevSetAsMain = setAsMain; - - if (setAsMain) mode = ReplaceMainModel; - else mode = CreateAdditionalModel; - - } else { - mode = ReplaceMainModel; - } - } - - if (mode == ReplaceCurrentPane) { - - Pane *pane = m_paneStack->getCurrentPane(); - if (pane) { - if (getMainModel()) { - View::ModelSet models(pane->getModels()); - if (models.find(getMainModel()) != models.end()) { - mode = ReplaceMainModel; - } - } else { - mode = ReplaceMainModel; - } - } else { - mode = CreateAdditionalModel; - } - } - - if (mode == CreateAdditionalModel && !getMainModel()) { - mode = ReplaceMainModel; - } - - if (mode == ReplaceMainModel) { - - Model *prevMain = getMainModel(); - if (prevMain) { - m_playSource->removeModel(prevMain); - PlayParameterRepository::getInstance()->removeModel(prevMain); - } - PlayParameterRepository::getInstance()->addModel(newModel); - - m_document->setMainModel(newModel); - - setupMenus(); - - if (m_sessionFile == "") { - //!!! shouldn't be dealing directly with title from here -- call a method - setWindowTitle(tr("Sonic Visualiser: %1") - .arg(source.getLocation())); - CommandHistory::getInstance()->clear(); - CommandHistory::getInstance()->documentSaved(); - m_documentModified = false; - } else { - setWindowTitle(tr("Sonic Visualiser: %1 [%2]") - .arg(QFileInfo(m_sessionFile).fileName()) - .arg(source.getLocation())); - if (m_documentModified) { - m_documentModified = false; - documentModified(); // so as to restore "(modified)" window title - } - } - - if (!source.isRemote()) m_audioFile = source.getLocalFilename(); - - } else if (mode == CreateAdditionalModel) { - - CommandHistory::getInstance()->startCompoundOperation - (tr("Import \"%1\"").arg(source.getLocation()), true); - - m_document->addImportedModel(newModel); - - AddPaneCommand *command = new AddPaneCommand(this); - CommandHistory::getInstance()->addCommand(command); - - Pane *pane = command->getPane(); - - if (!m_timeRulerLayer) { - m_timeRulerLayer = m_document->createMainModelLayer - (LayerFactory::TimeRuler); - } - - m_document->addLayerToView(pane, m_timeRulerLayer); - - Layer *newLayer = m_document->createImportedLayer(newModel); - - if (newLayer) { - m_document->addLayerToView(pane, newLayer); - } - - CommandHistory::getInstance()->endCompoundOperation(); - - } else if (mode == ReplaceCurrentPane) { - - // We know there is a current pane, otherwise we would have - // reset the mode to CreateAdditionalModel above; and we know - // the current pane does not contain the main model, otherwise - // we would have reset it to ReplaceMainModel. But we don't - // know whether the pane contains a waveform model at all. - - Pane *pane = m_paneStack->getCurrentPane(); - Layer *replace = 0; - - for (int i = 0; i < pane->getLayerCount(); ++i) { - Layer *layer = pane->getLayer(i); - if (dynamic_cast<WaveformLayer *>(layer)) { - replace = layer; - break; - } - } - - CommandHistory::getInstance()->startCompoundOperation - (tr("Import \"%1\"").arg(source.getLocation()), true); - - m_document->addImportedModel(newModel); - - if (replace) { - m_document->removeLayerFromView(pane, replace); - } - - Layer *newLayer = m_document->createImportedLayer(newModel); - - if (newLayer) { - m_document->addLayerToView(pane, newLayer); - } - - CommandHistory::getInstance()->endCompoundOperation(); - } - - updateMenuStates(); - m_recentFiles.addFile(source.getLocation()); - if (!source.isRemote()) { - // for file dialog - registerLastOpenedFilePath(FileFinder::AudioFile, - source.getLocalFilename()); - } - m_openingAudioFile = false; - - currentPaneChanged(m_paneStack->getCurrentPane()); - - return FileOpenSucceeded; -} - -MainWindowBase::FileOpenStatus -MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode) -{ - std::set<QString> extensions; - PlaylistFileReader::getSupportedExtensions(extensions); - QString extension = source.getExtension(); - if (extensions.find(extension) == extensions.end()) return FileOpenFailed; - - if (!source.isAvailable()) return FileOpenFailed; - source.waitForData(); - - PlaylistFileReader reader(source.getLocalFilename()); - if (!reader.isOK()) return FileOpenFailed; - - PlaylistFileReader::Playlist playlist = reader.load(); - - bool someSuccess = false; - - for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); - i != playlist.end(); ++i) { - - FileOpenStatus status = openAudio(*i, mode); - - if (status == FileOpenCancelled) { - return FileOpenCancelled; - } - - if (status == FileOpenSucceeded) { - someSuccess = true; - mode = CreateAdditionalModel; - } - } - - if (someSuccess) return FileOpenSucceeded; - else return FileOpenFailed; -} - -MainWindowBase::FileOpenStatus -MainWindowBase::openLayer(FileSource source) -{ - Pane *pane = m_paneStack->getCurrentPane(); - - if (!pane) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindowBase::openLayer: no current pane" << std::endl; - return FileOpenWrongMode; - } - - if (!getMainModel()) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << std::endl; - return FileOpenWrongMode; - } - - if (!source.isAvailable()) return FileOpenFailed; - source.waitForData(); - - QString path = source.getLocalFilename(); - - if (source.getExtension() == "svl" || source.getExtension() == "xml") { - - PaneCallback callback(this); - QFile file(path); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - std::cerr << "ERROR: MainWindowBase::openLayer(" - << source.getLocation().toStdString() - << "): Failed to open file for reading" << std::endl; - return FileOpenFailed; - } - - SVFileReader reader(m_document, callback, source.getLocation()); - reader.setCurrentPane(pane); - - QXmlInputSource inputSource(&file); - reader.parse(inputSource); - - if (!reader.isOK()) { - std::cerr << "ERROR: MainWindowBase::openLayer(" - << source.getLocation().toStdString() - << "): Failed to read XML file: " - << reader.getErrorString().toStdString() << std::endl; - return FileOpenFailed; - } - - m_recentFiles.addFile(source.getLocation()); - - if (!source.isRemote()) { - registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog - } - - } else { - - try { - - Model *model = DataFileReaderFactory::load - (path, getMainModel()->getSampleRate()); - - if (model) { - - std::cerr << "MainWindowBase::openLayer: Have model" << std::endl; - - Layer *newLayer = m_document->createImportedLayer(model); - - if (newLayer) { - - m_document->addLayerToView(pane, newLayer); - m_recentFiles.addFile(source.getLocation()); - - if (!source.isRemote()) { - registerLastOpenedFilePath - (FileFinder::LayerFile, - path); // for file dialog - } - - return FileOpenSucceeded; - } - } - } catch (DataFileReaderFactory::Exception e) { - if (e == DataFileReaderFactory::ImportCancelled) { - return FileOpenCancelled; - } - } - } - - source.setLeaveLocalFile(true); - return FileOpenFailed; -} - -MainWindowBase::FileOpenStatus -MainWindowBase::openImage(FileSource source) -{ - Pane *pane = m_paneStack->getCurrentPane(); - - if (!pane) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindowBase::openImage: no current pane" << std::endl; - return FileOpenWrongMode; - } - - if (!m_document->getMainModel()) { - return FileOpenWrongMode; - } - - bool newLayer = false; - - ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer()); - if (!il) { - for (int i = pane->getLayerCount()-1; i >= 0; --i) { - il = dynamic_cast<ImageLayer *>(pane->getLayer(i)); - if (il) break; - } - } - if (!il) { - il = dynamic_cast<ImageLayer *> - (m_document->createEmptyLayer(LayerFactory::Image)); - if (!il) return FileOpenFailed; - newLayer = true; - } - - // We don't put the image file in Recent Files - - std::cerr << "openImage: trying location \"" << source.getLocation().toStdString() << "\" in image layer" << std::endl; - - if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) { - if (newLayer) { - m_document->setModel(il, 0); // releasing its model - delete il; - } - return FileOpenFailed; - } else { - if (newLayer) { - m_document->addLayerToView(pane, il); - } - m_paneStack->setCurrentLayer(pane, il); - } - - return FileOpenSucceeded; -} - -MainWindowBase::FileOpenStatus -MainWindowBase::openSessionFile(QString fileOrUrl) -{ - return openSession(FileSource(fileOrUrl)); -} - -MainWindowBase::FileOpenStatus -MainWindowBase::openSession(FileSource source) -{ - if (!source.isAvailable()) return FileOpenFailed; - if (source.getExtension() != "sv") return FileOpenFailed; - source.waitForData(); - - BZipFileDevice bzFile(source.getLocalFilename()); - if (!bzFile.open(QIODevice::ReadOnly)) return FileOpenFailed; - - if (!checkSaveModified()) return FileOpenCancelled; - - QString error; - closeSession(); - createDocument(); - - PaneCallback callback(this); - m_viewManager->clearSelections(); - - SVFileReader reader(m_document, callback, source.getLocation()); - QXmlInputSource inputSource(&bzFile); - reader.parse(inputSource); - - if (!reader.isOK()) { - error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); - } - - bzFile.close(); - - bool ok = (error == ""); - - if (ok) { - - setWindowTitle(tr("Sonic Visualiser: %1") - .arg(source.getLocation())); - - if (!source.isRemote()) m_sessionFile = source.getLocalFilename(); - - setupMenus(); - - CommandHistory::getInstance()->clear(); - CommandHistory::getInstance()->documentSaved(); - m_documentModified = false; - updateMenuStates(); - - m_recentFiles.addFile(source.getLocation()); - - if (!source.isRemote()) { - // for file dialog - registerLastOpenedFilePath(FileFinder::SessionFile, - source.getLocalFilename()); - } - - } else { - setWindowTitle(tr("Sonic Visualiser")); - } - - return ok ? FileOpenSucceeded : FileOpenFailed; -} - -void -MainWindowBase::createPlayTarget() -{ - if (m_playTarget) return; - - m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); - if (!m_playTarget) { - QMessageBox::warning - (this, tr("Couldn't open audio device"), - tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Audio playback will not be available during this session."), - QMessageBox::Ok); - } -} - -WaveFileModel * -MainWindowBase::getMainModel() -{ - if (!m_document) return 0; - return m_document->getMainModel(); -} - -const WaveFileModel * -MainWindowBase::getMainModel() const -{ - if (!m_document) return 0; - return m_document->getMainModel(); -} - -void -MainWindowBase::createDocument() -{ - m_document = new Document; - - connect(m_document, SIGNAL(layerAdded(Layer *)), - this, SLOT(layerAdded(Layer *))); - connect(m_document, SIGNAL(layerRemoved(Layer *)), - this, SLOT(layerRemoved(Layer *))); - connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), - this, SLOT(layerAboutToBeDeleted(Layer *))); - connect(m_document, SIGNAL(layerInAView(Layer *, bool)), - this, SLOT(layerInAView(Layer *, bool))); - - connect(m_document, SIGNAL(modelAdded(Model *)), - this, SLOT(modelAdded(Model *))); - connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), - this, SLOT(mainModelChanged(WaveFileModel *))); - connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), - this, SLOT(modelAboutToBeDeleted(Model *))); - - connect(m_document, SIGNAL(modelGenerationFailed(QString)), - this, SLOT(modelGenerationFailed(QString))); - connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)), - this, SLOT(modelRegenerationFailed(QString, QString))); -} - -bool -MainWindowBase::saveSessionFile(QString path) -{ - BZipFileDevice bzFile(path); - if (!bzFile.open(QIODevice::WriteOnly)) { - std::cerr << "Failed to open session file \"" << path.toStdString() - << "\" for writing: " - << bzFile.errorString().toStdString() << std::endl; - return false; - } - - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - - QTextStream out(&bzFile); - toXml(out); - out.flush(); - - QApplication::restoreOverrideCursor(); - - if (!bzFile.isOK()) { - QMessageBox::critical(this, tr("Failed to write file"), - tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") - .arg(path).arg(bzFile.errorString())); - bzFile.close(); - return false; - } - - bzFile.close(); - return true; -} - -void -MainWindowBase::toXml(QTextStream &out) -{ - QString indent(" "); - - out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; - out << "<!DOCTYPE sonic-visualiser>\n"; - out << "<sv>\n"; - - m_document->toXml(out, "", ""); - - out << "<display>\n"; - - out << QString(" <window width=\"%1\" height=\"%2\"/>\n") - .arg(width()).arg(height()); - - for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { - - Pane *pane = m_paneStack->getPane(i); - - if (pane) { - pane->toXml(out, indent); - } - } - - out << "</display>\n"; - - m_viewManager->getSelection().toXml(out); - - out << "</sv>\n"; -} - -Pane * -MainWindowBase::addPaneToStack() -{ - AddPaneCommand *command = new AddPaneCommand(this); - CommandHistory::getInstance()->addCommand(command); - return command->getPane(); -} - -void -MainWindowBase::zoomIn() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->zoom(true); -} - -void -MainWindowBase::zoomOut() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->zoom(false); -} - -void -MainWindowBase::zoomToFit() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - Model *model = getMainModel(); - if (!model) return; - - size_t start = model->getStartFrame(); - size_t end = model->getEndFrame(); - size_t pixels = currentPane->width(); - - size_t sw = currentPane->getVerticalScaleWidth(); - if (pixels > sw * 2) pixels -= sw * 2; - else pixels = 1; - if (pixels > 4) pixels -= 4; - - size_t zoomLevel = (end - start) / pixels; - - currentPane->setZoomLevel(zoomLevel); - currentPane->setCentreFrame((start + end) / 2); -} - -void -MainWindowBase::zoomDefault() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->setZoomLevel(1024); -} - -void -MainWindowBase::scrollLeft() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(false, false); -} - -void -MainWindowBase::jumpLeft() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(false, true); -} - -void -MainWindowBase::scrollRight() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(true, false); -} - -void -MainWindowBase::jumpRight() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(true, true); -} - -void -MainWindowBase::showNoOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::NoOverlays); -} - -void -MainWindowBase::showMinimalOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); -} - -void -MainWindowBase::showStandardOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::StandardOverlays); -} - -void -MainWindowBase::showAllOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::AllOverlays); -} - -void -MainWindowBase::toggleZoomWheels() -{ - if (m_viewManager->getZoomWheelsEnabled()) { - m_viewManager->setZoomWheelsEnabled(false); - } else { - m_viewManager->setZoomWheelsEnabled(true); - } -} - -void -MainWindowBase::togglePropertyBoxes() -{ - if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { - if (Preferences::getInstance()->getPropertyBoxLayout() == - Preferences::VerticallyStacked) { - m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); - } else { - m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); - } - } else { - m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); - } -} - -void -MainWindowBase::toggleStatusBar() -{ - QSettings settings; - settings.beginGroup("MainWindow"); - bool sb = settings.value("showstatusbar", true).toBool(); - - if (sb) { - statusBar()->hide(); - } else { - statusBar()->show(); - } - - settings.setValue("showstatusbar", !sb); - - settings.endGroup(); -} - -void -MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name) -{ - if (name == "Property Box Layout") { - if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { - if (Preferences::getInstance()->getPropertyBoxLayout() == - Preferences::VerticallyStacked) { - m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); - } else { - m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); - } - } - } else if (name == "Background Mode" && m_viewManager) { - Preferences::BackgroundMode mode = - Preferences::getInstance()->getBackgroundMode(); - if (mode == Preferences::BackgroundFromTheme) { - m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); - } else if (mode == Preferences::DarkBackground) { - m_viewManager->setGlobalDarkBackground(true); - } else { - m_viewManager->setGlobalDarkBackground(false); - } - } -} - -void -MainWindowBase::play() -{ - if (m_playSource->isPlaying()) { - stop(); - } else { - playbackFrameChanged(m_viewManager->getPlaybackFrame()); - m_playSource->play(m_viewManager->getPlaybackFrame()); - } -} - -void -MainWindowBase::ffwd() -{ - if (!getMainModel()) return; - - int frame = m_viewManager->getPlaybackFrame(); - ++frame; - - Layer *layer = getSnapLayer(); - size_t sr = getMainModel()->getSampleRate(); - - if (!layer) { - - frame = RealTime::realTime2Frame - (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr); - if (frame > int(getMainModel()->getEndFrame())) { - frame = getMainModel()->getEndFrame(); - } - - } else { - - size_t resolution = 0; - if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), - frame, resolution, Layer::SnapRight)) { - frame = getMainModel()->getEndFrame(); - } - } - - if (frame < 0) frame = 0; - - if (m_viewManager->getPlaySelectionMode()) { - frame = m_viewManager->constrainFrameToSelection(size_t(frame)); - } - - m_viewManager->setPlaybackFrame(frame); -} - -void -MainWindowBase::ffwdEnd() -{ - if (!getMainModel()) return; - - size_t frame = getMainModel()->getEndFrame(); - - if (m_viewManager->getPlaySelectionMode()) { - frame = m_viewManager->constrainFrameToSelection(frame); - } - - m_viewManager->setPlaybackFrame(frame); -} - -void -MainWindowBase::rewind() -{ - if (!getMainModel()) return; - - int frame = m_viewManager->getPlaybackFrame(); - if (frame > 0) --frame; - - Layer *layer = getSnapLayer(); - size_t sr = getMainModel()->getSampleRate(); - - // when rewinding during playback, we want to allow a period - // following a rewind target point at which the rewind will go to - // the prior point instead of the immediately neighbouring one - if (m_playSource && m_playSource->isPlaying()) { - RealTime ct = RealTime::frame2RealTime(frame, sr); - ct = ct - RealTime::fromSeconds(0.25); - if (ct < RealTime::zeroTime) ct = RealTime::zeroTime; -// std::cerr << "rewind: frame " << frame << " -> "; - frame = RealTime::realTime2Frame(ct, sr); -// std::cerr << frame << std::endl; - } - - if (!layer) { - - frame = RealTime::realTime2Frame - (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr); - if (frame < int(getMainModel()->getStartFrame())) { - frame = getMainModel()->getStartFrame(); - } - - } else { - - size_t resolution = 0; - if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(), - frame, resolution, Layer::SnapLeft)) { - frame = getMainModel()->getStartFrame(); - } - } - - if (frame < 0) frame = 0; - - if (m_viewManager->getPlaySelectionMode()) { - frame = m_viewManager->constrainFrameToSelection(size_t(frame)); - } - - m_viewManager->setPlaybackFrame(frame); -} - -void -MainWindowBase::rewindStart() -{ - if (!getMainModel()) return; - - size_t frame = getMainModel()->getStartFrame(); - - if (m_viewManager->getPlaySelectionMode()) { - frame = m_viewManager->constrainFrameToSelection(frame); - } - - m_viewManager->setPlaybackFrame(frame); -} - -Layer * -MainWindowBase::getSnapLayer() const -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) return 0; - - Layer *layer = pane->getSelectedLayer(); - - if (!dynamic_cast<TimeInstantLayer *>(layer) && - !dynamic_cast<TimeValueLayer *>(layer) && - !dynamic_cast<TimeRulerLayer *>(layer)) { - - layer = 0; - - for (int i = pane->getLayerCount(); i > 0; --i) { - Layer *l = pane->getLayer(i-1); - if (dynamic_cast<TimeRulerLayer *>(l)) { - layer = l; - break; - } - } - } - - return layer; -} - -void -MainWindowBase::stop() -{ - m_playSource->stop(); - - if (m_paneStack && m_paneStack->getCurrentPane()) { - updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); - } else { - m_myStatusMessage = ""; - statusBar()->showMessage(""); - } -} - -MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) : - m_mw(mw), - m_pane(0), - m_prevCurrentPane(0), - m_added(false) -{ -} - -MainWindowBase::AddPaneCommand::~AddPaneCommand() -{ - if (m_pane && !m_added) { - m_mw->m_paneStack->deletePane(m_pane); - } -} - -QString -MainWindowBase::AddPaneCommand::getName() const -{ - return tr("Add Pane"); -} - -void -MainWindowBase::AddPaneCommand::execute() -{ - if (!m_pane) { - m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); - m_pane = m_mw->m_paneStack->addPane(); - - connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), - m_mw, SLOT(contextHelpChanged(const QString &))); - } else { - m_mw->m_paneStack->showPane(m_pane); - } - - m_mw->m_paneStack->setCurrentPane(m_pane); - m_added = true; -} - -void -MainWindowBase::AddPaneCommand::unexecute() -{ - m_mw->m_paneStack->hidePane(m_pane); - m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); - m_added = false; -} - -MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) : - m_mw(mw), - m_pane(pane), - m_added(true) -{ -} - -MainWindowBase::RemovePaneCommand::~RemovePaneCommand() -{ - if (m_pane && !m_added) { - m_mw->m_paneStack->deletePane(m_pane); - } -} - -QString -MainWindowBase::RemovePaneCommand::getName() const -{ - return tr("Remove Pane"); -} - -void -MainWindowBase::RemovePaneCommand::execute() -{ - m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); - m_mw->m_paneStack->hidePane(m_pane); - m_added = false; -} - -void -MainWindowBase::RemovePaneCommand::unexecute() -{ - m_mw->m_paneStack->showPane(m_pane); - m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); - m_added = true; -} - -void -MainWindowBase::deleteCurrentPane() -{ - CommandHistory::getInstance()->startCompoundOperation - (tr("Delete Pane"), true); - - Pane *pane = m_paneStack->getCurrentPane(); - if (pane) { - while (pane->getLayerCount() > 0) { - Layer *layer = pane->getLayer(0); - if (layer) { - m_document->removeLayerFromView(pane, layer); - } else { - break; - } - } - - RemovePaneCommand *command = new RemovePaneCommand(this, pane); - CommandHistory::getInstance()->addCommand(command); - } - - CommandHistory::getInstance()->endCompoundOperation(); - - updateMenuStates(); -} - -void -MainWindowBase::deleteCurrentLayer() -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (pane) { - Layer *layer = pane->getSelectedLayer(); - if (layer) { - m_document->removeLayerFromView(pane, layer); - } - } - updateMenuStates(); -} - -void -MainWindowBase::playbackFrameChanged(unsigned long frame) -{ - if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - - RealTime now = RealTime::frame2RealTime - (frame, getMainModel()->getSampleRate()); - - if (now.sec == m_lastPlayStatusSec) return; - - RealTime then = RealTime::frame2RealTime - (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); - - QString nowStr; - QString thenStr; - QString remainingStr; - - if (then.sec > 10) { - nowStr = now.toSecText().c_str(); - thenStr = then.toSecText().c_str(); - remainingStr = (then - now).toSecText().c_str(); - m_lastPlayStatusSec = now.sec; - } else { - nowStr = now.toText(true).c_str(); - thenStr = then.toText(true).c_str(); - remainingStr = (then - now).toText(true).c_str(); - } - - m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") - .arg(nowStr).arg(thenStr).arg(remainingStr); - - statusBar()->showMessage(m_myStatusMessage); -} - -void -MainWindowBase::globalCentreFrameChanged(unsigned long ) -{ - if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - Pane *p = 0; - if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; - if (!p->getFollowGlobalPan()) return; - updateVisibleRangeDisplay(p); -} - -void -MainWindowBase::viewCentreFrameChanged(View *v, unsigned long ) -{ - if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - Pane *p = 0; - if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; - if (v == p) updateVisibleRangeDisplay(p); -} - -void -MainWindowBase::viewZoomLevelChanged(View *v, unsigned long , bool ) -{ - if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - Pane *p = 0; - if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; - if (v == p) updateVisibleRangeDisplay(p); -} - -void -MainWindowBase::layerAdded(Layer *) -{ -// std::cerr << "MainWindowBase::layerAdded(" << layer << ")" << std::endl; - updateMenuStates(); -} - -void -MainWindowBase::layerRemoved(Layer *) -{ -// std::cerr << "MainWindowBase::layerRemoved(" << layer << ")" << std::endl; - updateMenuStates(); -} - -void -MainWindowBase::layerAboutToBeDeleted(Layer *layer) -{ -// std::cerr << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << std::endl; - if (layer == m_timeRulerLayer) { -// std::cerr << "(this is the time ruler layer)" << std::endl; - m_timeRulerLayer = 0; - } -} - -void -MainWindowBase::layerInAView(Layer *layer, bool inAView) -{ -// std::cerr << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << std::endl; - - // Check whether we need to add or remove model from play source - Model *model = layer->getModel(); - if (model) { - if (inAView) { - m_playSource->addModel(model); - } else { - bool found = false; - for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { - Pane *pane = m_paneStack->getPane(i); - if (!pane) continue; - for (int j = 0; j < pane->getLayerCount(); ++j) { - Layer *pl = pane->getLayer(j); - if (pl && pl->getModel() == model) { - found = true; - break; - } - } - if (found) break; - } - if (!found) m_playSource->removeModel(model); - } - } - - updateMenuStates(); -} - -void -MainWindowBase::modelAdded(Model *model) -{ -// std::cerr << "MainWindowBase::modelAdded(" << model << ")" << std::endl; - m_playSource->addModel(model); -} - -void -MainWindowBase::mainModelChanged(WaveFileModel *model) -{ -// std::cerr << "MainWindowBase::mainModelChanged(" << model << ")" << std::endl; - updateDescriptionLabel(); - if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); - if (model && !m_playTarget && m_audioOutput) createPlayTarget(); -} - -void -MainWindowBase::modelAboutToBeDeleted(Model *model) -{ -// std::cerr << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << std::endl; - if (model == m_viewManager->getPlaybackModel()) { - m_viewManager->setPlaybackModel(0); - } - m_playSource->removeModel(model); - FFTDataServer::modelAboutToBeDeleted(model); -} - -void -MainWindowBase::pollOSC() -{ - if (!m_oscQueue || m_oscQueue->isEmpty()) return; - std::cerr << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; - - if (m_openingAudioFile) return; - - OSCMessage message = m_oscQueue->readMessage(); - - if (message.getTarget() != 0) { - return; //!!! for now -- this class is target 0, others not handled yet - } - - handleOSCMessage(message); -} - -void -MainWindowBase::inProgressSelectionChanged() -{ - Pane *currentPane = 0; - if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); - if (currentPane) updateVisibleRangeDisplay(currentPane); -} - -void -MainWindowBase::contextHelpChanged(const QString &s) -{ - if (s == "" && m_myStatusMessage != "") { - statusBar()->showMessage(m_myStatusMessage); - return; - } - statusBar()->showMessage(s); -} - -void -MainWindowBase::openHelpUrl(QString url) -{ - // This method mostly lifted from Qt Assistant source code - - QProcess *process = new QProcess(this); - connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); - - QStringList args; - -#ifdef Q_OS_MAC - args.append(url); - process->start("open", args); -#else -#ifdef Q_OS_WIN32 - - QString pf(getenv("ProgramFiles")); - QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); - - args.append(url); - process->start(command, args); - -#else -#ifdef Q_WS_X11 - if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { - args.append("exec"); - args.append(url); - process->start("kfmclient", args); - } else if (!qgetenv("BROWSER").isEmpty()) { - args.append(url); - process->start(qgetenv("BROWSER"), args); - } else { - args.append(url); - process->start("firefox", args); - } -#endif -#endif -#endif -} -
--- a/main/MainWindowBase.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2007 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _MAIN_WINDOW_BASE_H_ -#define _MAIN_WINDOW_BASE_H_ - -#include <QFrame> -#include <QString> -#include <QUrl> -#include <QMainWindow> -#include <QPointer> - -#include "base/Command.h" -#include "view/ViewManager.h" -#include "base/PropertyContainer.h" -#include "base/RecentFiles.h" -#include "layer/LayerFactory.h" -#include "transform/Transform.h" -#include "document/SVFileReader.h" -#include "data/fileio/FileFinder.h" -#include "data/fileio/FileSource.h" -#include <map> - -class Document; -class PaneStack; -class Pane; -class View; -class Fader; -class Overview; -class Layer; -class WaveformLayer; -class WaveFileModel; -class AudioCallbackPlaySource; -class AudioCallbackPlayTarget; -class CommandHistory; -class QMenu; -class AudioDial; -class QLabel; -class QCheckBox; -class PreferencesDialog; -class QTreeView; -class QPushButton; -class OSCQueue; -class OSCMessage; -class KeyReference; -class Labeller; - -/** - * The base class for the SV main window. This includes everything to - * do with general document and pane stack management, but nothing - * that involves user interaction -- this doesn't create the widget or - * menu structures or editing tools, and if a function needs to open a - * dialog, it shouldn't be in here. This permits "variations on SV" - * to use different subclasses retaining the same general structure. - */ - -class MainWindowBase : public QMainWindow -{ - Q_OBJECT - -public: - MainWindowBase(bool withAudioOutput, bool withOSCSupport); - virtual ~MainWindowBase(); - - enum AudioFileOpenMode { - ReplaceMainModel, - CreateAdditionalModel, - ReplaceCurrentPane, - AskUser - }; - - enum FileOpenStatus { - FileOpenSucceeded, - FileOpenFailed, - FileOpenCancelled, - FileOpenWrongMode // attempted to open layer when no main model present - }; - - virtual FileOpenStatus open(QString fileOrUrl, AudioFileOpenMode = AskUser); - virtual FileOpenStatus open(FileSource source, AudioFileOpenMode = AskUser); - - virtual FileOpenStatus openAudio(FileSource source, AudioFileOpenMode = AskUser); - virtual FileOpenStatus openPlaylist(FileSource source, AudioFileOpenMode = AskUser); - virtual FileOpenStatus openLayer(FileSource source); - virtual FileOpenStatus openImage(FileSource source); - - virtual FileOpenStatus openSessionFile(QString fileOrUrl); - virtual FileOpenStatus openSession(FileSource source); - - virtual bool saveSessionFile(QString path); - -signals: - // Used to toggle the availability of menu actions - void canAddPane(bool); - void canDeleteCurrentPane(bool); - void canAddLayer(bool); - void canImportMoreAudio(bool); - void canImportLayer(bool); - void canExportAudio(bool); - void canExportLayer(bool); - void canExportImage(bool); - void canRenameLayer(bool); - void canEditLayer(bool); - void canMeasureLayer(bool); - void canSelect(bool); - void canClearSelection(bool); - void canEditSelection(bool); - void canDeleteSelection(bool); - void canPaste(bool); - void canInsertInstant(bool); - void canInsertInstantsAtBoundaries(bool); - void canRenumberInstants(bool); - void canDeleteCurrentLayer(bool); - void canZoom(bool); - void canScroll(bool); - void canPlay(bool); - void canFfwd(bool); - void canRewind(bool); - void canPlaySelection(bool); - void canSpeedUpPlayback(bool); - void canSlowDownPlayback(bool); - void canChangePlaybackSpeed(bool); - void canSave(bool); - -public slots: - virtual void preferenceChanged(PropertyContainer::PropertyName); - -protected slots: - virtual void zoomIn(); - virtual void zoomOut(); - virtual void zoomToFit(); - virtual void zoomDefault(); - virtual void scrollLeft(); - virtual void scrollRight(); - virtual void jumpLeft(); - virtual void jumpRight(); - - virtual void showNoOverlays(); - virtual void showMinimalOverlays(); - virtual void showStandardOverlays(); - virtual void showAllOverlays(); - - virtual void toggleZoomWheels(); - virtual void togglePropertyBoxes(); - virtual void toggleStatusBar(); - - virtual void play(); - virtual void ffwd(); - virtual void ffwdEnd(); - virtual void rewind(); - virtual void rewindStart(); - virtual void stop(); - - virtual void deleteCurrentPane(); - virtual void deleteCurrentLayer(); - - virtual void playLoopToggled(); - virtual void playSelectionToggled(); - virtual void playSoloToggled(); - - virtual void sampleRateMismatch(size_t, size_t, bool) = 0; - virtual void audioOverloadPluginDisabled() = 0; - - virtual void playbackFrameChanged(unsigned long); - virtual void globalCentreFrameChanged(unsigned long); - virtual void viewCentreFrameChanged(View *, unsigned long); - virtual void viewZoomLevelChanged(View *, unsigned long, bool); - virtual void outputLevelsChanged(float, float) = 0; - - virtual void currentPaneChanged(Pane *); - virtual void currentLayerChanged(Pane *, Layer *); - - virtual void selectAll(); - virtual void selectToStart(); - virtual void selectToEnd(); - virtual void selectVisible(); - virtual void clearSelection(); - - virtual void cut(); - virtual void copy(); - virtual void paste(); - virtual void deleteSelected(); - - virtual void insertInstant(); - virtual void insertInstantAt(size_t); - virtual void insertInstantsAtBoundaries(); - virtual void renumberInstants(); - - virtual void documentModified(); - virtual void documentRestored(); - - virtual void layerAdded(Layer *); - virtual void layerRemoved(Layer *); - virtual void layerAboutToBeDeleted(Layer *); - virtual void layerInAView(Layer *, bool); - - virtual void mainModelChanged(WaveFileModel *); - virtual void modelAdded(Model *); - virtual void modelAboutToBeDeleted(Model *); - - virtual void updateMenuStates(); - virtual void updateDescriptionLabel() = 0; - - virtual void modelGenerationFailed(QString) = 0; - virtual void modelRegenerationFailed(QString, QString) = 0; - - virtual void rightButtonMenuRequested(Pane *, QPoint point) = 0; - - virtual void paneAdded(Pane *) = 0; - virtual void paneHidden(Pane *) = 0; - virtual void paneAboutToBeDeleted(Pane *) = 0; - virtual void paneDropAccepted(Pane *, QStringList) = 0; - virtual void paneDropAccepted(Pane *, QString) = 0; - - virtual void pollOSC(); - virtual void handleOSCMessage(const OSCMessage &) = 0; - - virtual void contextHelpChanged(const QString &); - virtual void inProgressSelectionChanged(); - - virtual void closeSession() = 0; - -protected: - QString m_sessionFile; - QString m_audioFile; - Document *m_document; - - QLabel *m_descriptionLabel; - PaneStack *m_paneStack; - ViewManager *m_viewManager; - Layer *m_timeRulerLayer; - - bool m_audioOutput; - AudioCallbackPlaySource *m_playSource; - AudioCallbackPlayTarget *m_playTarget; - - OSCQueue *m_oscQueue; - - RecentFiles m_recentFiles; - RecentFiles m_recentTransforms; - - bool m_documentModified; - bool m_openingAudioFile; - bool m_abandoning; - - Labeller *m_labeller; - - int m_lastPlayStatusSec; - mutable QString m_myStatusMessage; - - bool m_initialDarkBackground; - - WaveFileModel *getMainModel(); - const WaveFileModel *getMainModel() const; - void createDocument(); - - Pane *addPaneToStack(); - Layer *getSnapLayer() const; - - class PaneCallback : public SVFileReaderPaneCallback - { - public: - PaneCallback(MainWindowBase *mw) : m_mw(mw) { } - virtual Pane *addPane() { return m_mw->addPaneToStack(); } - virtual void setWindowSize(int width, int height) { - m_mw->resize(width, height); - } - virtual void addSelection(int start, int end) { - m_mw->m_viewManager->addSelection(Selection(start, end)); - } - protected: - MainWindowBase *m_mw; - }; - - class AddPaneCommand : public Command - { - public: - AddPaneCommand(MainWindowBase *mw); - virtual ~AddPaneCommand(); - - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const; - - Pane *getPane() { return m_pane; } - - protected: - MainWindowBase *m_mw; - Pane *m_pane; // Main window owns this, but I determine its lifespan - Pane *m_prevCurrentPane; // I don't own this - bool m_added; - }; - - class RemovePaneCommand : public Command - { - public: - RemovePaneCommand(MainWindowBase *mw, Pane *pane); - virtual ~RemovePaneCommand(); - - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const; - - protected: - MainWindowBase *m_mw; - Pane *m_pane; // Main window owns this, but I determine its lifespan - Pane *m_prevCurrentPane; // I don't own this - bool m_added; - }; - - virtual bool checkSaveModified() = 0; - - virtual QString getOpenFileName(FileFinder::FileType type); - virtual QString getSaveFileName(FileFinder::FileType type); - virtual void registerLastOpenedFilePath(FileFinder::FileType type, QString path); - - virtual void createPlayTarget(); - virtual void openHelpUrl(QString url); - - virtual void setupMenus() = 0; - virtual void updateVisibleRangeDisplay(Pane *p) const = 0; - - virtual void toXml(QTextStream &stream); -}; - - -#endif
--- a/osc/OSCMessage.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. -*/ - -/* - This is a modified version of a source file from the - Rosegarden MIDI and audio sequencer and notation editor. - This file copyright 2000-2006 Chris Cannam. -*/ - -#include "OSCMessage.h" - - -OSCMessage::~OSCMessage() -{ - clearArgs(); -} - -void -OSCMessage::clearArgs() -{ - m_args.clear(); -} - -void -OSCMessage::addArg(QVariant arg) -{ - m_args.push_back(arg); -} - -size_t -OSCMessage::getArgCount() const -{ - return m_args.size(); -} - -const QVariant & -OSCMessage::getArg(size_t i) const -{ - return m_args[i]; -} -
--- a/osc/OSCMessage.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. -*/ - -/* - This is a modified version of a source file from the - Rosegarden MIDI and audio sequencer and notation editor. - This file copyright 2000-2006 Chris Cannam. -*/ - -#ifndef _OSC_MESSAGE_H_ -#define _OSC_MESSAGE_H_ - -#include <QString> -#include <QVariant> - -#include <vector> -#include <map> - -class OSCMessage -{ -public: - OSCMessage() { } - ~OSCMessage(); - - void setTarget(const int &target) { m_target = target; } - int getTarget() const { return m_target; } - - void setTargetData(const int &targetData) { m_targetData = targetData; } - int getTargetData() const { return m_targetData; } - - void setMethod(QString method) { m_method = method; } - QString getMethod() const { return m_method; } - - void clearArgs(); - void addArg(QVariant arg); - - size_t getArgCount() const; - const QVariant &getArg(size_t i) const; - -private: - int m_target; - int m_targetData; - QString m_method; - std::vector<QVariant> m_args; -}; - -#endif
--- a/osc/OSCQueue.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,222 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. -*/ - -/* - This is a modified version of a source file from the - Rosegarden MIDI and audio sequencer and notation editor. - This file copyright 2000-2006 Chris Cannam and QMUL. -*/ - -#include "OSCQueue.h" - -#include <iostream> - -#define OSC_MESSAGE_QUEUE_SIZE 1023 - -#ifdef HAVE_LIBLO - -void -OSCQueue::oscError(int num, const char *msg, const char *path) -{ - std::cerr << "ERROR: OSCQueue::oscError: liblo server error " << num - << " in path " << path << ": " << msg << std::endl; -} - -int -OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv, - int argc, lo_message, void *user_data) -{ - OSCQueue *queue = static_cast<OSCQueue *>(user_data); - - int target; - int targetData; - QString method; - - if (!queue->parseOSCPath(path, target, targetData, method)) { - return 1; - } - - OSCMessage message; - message.setTarget(target); - message.setTargetData(targetData); - message.setMethod(method); - - int i = 0; - - while (types && i < argc && types[i]) { - - char type = types[i]; - lo_arg *arg = argv[i]; - - switch (type) { - case 'i': message.addArg(arg->i); break; - // This conversion fails to compile in 64-bit environments - // at present, and we don't use the h type anyway so we - // can safely omit it -// case 'h': message.addArg(arg->h); break; - case 'f': message.addArg(arg->f); break; - case 'd': message.addArg(arg->d); break; - case 'c': message.addArg(arg->c); break; - case 't': message.addArg(arg->i); break; - case 's': message.addArg(&arg->s); break; - default: std::cerr << "WARNING: OSCQueue::oscMessageHandler: " - << "Unsupported OSC type '" << type << "'" - << std::endl; - break; - } - - ++i; - } - - queue->postMessage(message); - return 0; -} - -#endif - -OSCQueue::OSCQueue() : -#ifdef HAVE_LIBLO - m_thread(0), -#endif - m_buffer(OSC_MESSAGE_QUEUE_SIZE) -{ -#ifdef HAVE_LIBLO - m_thread = lo_server_thread_new(NULL, oscError); - - lo_server_thread_add_method(m_thread, NULL, NULL, - oscMessageHandler, this); - - lo_server_thread_start(m_thread); - - std::cout << "OSCQueue::OSCQueue: Base OSC URL is " - << lo_server_thread_get_url(m_thread) << std::endl; -#endif -} - -OSCQueue::~OSCQueue() -{ -#ifdef HAVE_LIBLO - if (m_thread) { - lo_server_thread_stop(m_thread); - } -#endif - - while (m_buffer.getReadSpace() > 0) { - delete m_buffer.readOne(); - } -} - -bool -OSCQueue::isOK() const -{ -#ifdef HAVE_LIBLO - return (m_thread != 0); -#else - return false; -#endif -} - -QString -OSCQueue::getOSCURL() const -{ - QString url = ""; -#ifdef HAVE_LIBLO - url = lo_server_thread_get_url(m_thread); -#endif - return url; -} - -size_t -OSCQueue::getMessagesAvailable() const -{ - return m_buffer.getReadSpace(); -} - -OSCMessage -OSCQueue::readMessage() -{ - OSCMessage *message = m_buffer.readOne(); - OSCMessage rmessage = *message; - delete message; - return rmessage; -} - -void -OSCQueue::postMessage(OSCMessage message) -{ - int count = 0, max = 5; - while (m_buffer.getWriteSpace() == 0) { - if (count == max) { - std::cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << std::endl; - return; - } - std::cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << std::endl; - std::cerr << "Waiting for something to be processed" << std::endl; -#ifdef _WIN32 - Sleep(1); -#else - sleep(1); -#endif - count++; - } - - OSCMessage *mp = new OSCMessage(message); - m_buffer.write(&mp, 1); - std::cerr << "OSCQueue::postMessage: Posted OSC message: target " - << message.getTarget() << ", target data " << message.getTargetData() - << ", method " << message.getMethod().toStdString() << std::endl; - emit messagesAvailable(); -} - -bool -OSCQueue::parseOSCPath(QString path, int &target, int &targetData, - QString &method) -{ - while (path.startsWith("/")) { - path = path.right(path.length()-1); - } - - int i = 0; - - bool ok = false; - target = path.section('/', i, i).toInt(&ok); - - if (!ok) { - target = 0; - } else { - ++i; - targetData = path.section('/', i, i).toInt(&ok); - if (!ok) { - targetData = 0; - } else { - ++i; - } - } - - method = path.section('/', i, -1); - - if (method.contains('/')) { - std::cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \"" - << path.toStdString() << "\" (should be target/data/method or " - << "target/method or method, where target and data " - << "are numeric)" << std::endl; - return false; - } - - std::cerr << "OSCQueue::parseOSCPath: good path \"" << path.toStdString() - << "\"" << std::endl; - - return true; -} -
--- a/osc/OSCQueue.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - - 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. -*/ - -/* - This is a modified version of a source file from the - Rosegarden MIDI and audio sequencer and notation editor. - This file copyright 2000-2006 Chris Cannam and QMUL. -*/ - -#ifndef _OSC_QUEUE_H_ -#define _OSC_QUEUE_H_ - -#include "OSCMessage.h" - -#include "base/RingBuffer.h" - -#include <QObject> - -#ifdef HAVE_LIBLO -#include <lo/lo.h> -#endif - -class OSCQueue : public QObject -{ - Q_OBJECT - -public: - OSCQueue(); - virtual ~OSCQueue(); - - bool isOK() const; - - bool isEmpty() const { return getMessagesAvailable() == 0; } - size_t getMessagesAvailable() const; - OSCMessage readMessage(); - - QString getOSCURL() const; - -signals: - void messagesAvailable(); - -protected: -#ifdef HAVE_LIBLO - lo_server_thread m_thread; - - static void oscError(int, const char *, const char *); - static int oscMessageHandler(const char *, const char *, lo_arg **, - int, lo_message, void *); -#endif - - void postMessage(OSCMessage); - bool parseOSCPath(QString path, int &target, int &targetData, QString &method); - - RingBuffer<OSCMessage *> m_buffer; -}; - -#endif -
--- a/osc/demoscript.sh Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,541 +0,0 @@ -#!/bin/bash - -audio=/data/music -preferred=$audio/free -list=audiofiles.txt -used=audiofiles-used.txt - -df=vamp:vamp-aubio:aubioonset:detectionfunction -#df=vamp:qm-vamp-plugins:qm-tempotracker:detection_fn -onsets=vamp:vamp-aubio:aubioonset:onsets -#onsets=vamp:qm-vamp-plugins:qm-tempotracker:beats -beats=vamp:vamp-aubio:aubiotempo:beats -#beats=$onsets -#onsets=$beats -chromagram=vamp:qm-vamp-plugins:qm-chromagram:chromagram -notes=vamp:vamp-aubio:aubionotes:notes - -pid=`cat /tmp/demoscript.pid 2>/dev/null` -if [ -n "$pid" ]; then - kill "$pid" -fi -echo $$ > /tmp/demoscript.pid -trap "rm /tmp/demoscript.pid" 0 - -sv-command quit -sleep 1 -killall -9 sonic-visualiser -sleep 1 - -pick_file() -{ - file="" - count=`wc -l "$list" 2>/dev/null | awk '{ print $1 }'` - if [ ! -f "$list" ] || [ "$count" -eq "0" ] ; then - find "$audio" -name \*.ogg -print >> "$list" - find "$audio" -name \*.mp3 -print >> "$list" - find "$audio" -name \*.wav -print >> "$list" - find "$preferred" -name \*.ogg -print >> "$list" - find "$preferred" -name \*.mp3 -print >> "$list" - find "$preferred" -name \*.wav -print >> "$list" - count=`wc -l "$list" 2>/dev/null | awk '{ print $1 }'` - fi - while [ -z "$file" ]; do - index=$((RANDOM % $count)) - file=`tail +"$index" "$list" | head -1` - [ -f "$file" ] || continue - done - fgrep -v "$file" "$list" > "$list"_ && mv "$list"_ "$list" - echo "$file" -} - -load_a_file() -{ - file=`pick_file` - if ! sv-command open "$file"; then - pid="`pidof sonic-visualiser`" - if [ -z "$pid" ]; then - ( setsid sonic-visualiser -geometry 1000x500+10+100 & ) - sleep 2 - sudo renice +19 `pidof sonic-visualiser` - sudo renice +18 `pidof Xorg` - sv-command resize 1000 500 - load_a_file - else - echo "ERROR: Unable to contact sonic-visualiser pid $pid" 1>&2 - exit 1 - fi - fi -} - -show_stuff() -{ - sv-command set overlays 2 -# sv-command set zoomwheels 1 - sv-command set propertyboxes 1 -} - -hide_stuff() -{ - sv-command set overlays 0 -# sv-command set zoomwheels 0 - sv-command set propertyboxes 0 -} - -reset() -{ - for pane in 1 2 3 4 5; do - for layer in 1 2 3 4 5 6 7 8 9 10; do - sv-command delete layer - done - sv-command delete pane - done - sv-command zoom default - sv-command add waveform - show_stuff -} - -scroll_and_zoom() -{ - sv-command set overlays 0 - sv-command set zoomwheels 0 - sv-command set propertyboxes 0 -# sv-command setcurrent 1 1 -# sv-command delete layer -# sv-command setcurrent 1 1 - sv-command set layer Colour Red - sleep 1 - sv-command set pane Global-Zoom off - sv-command set pane Global-Scroll off - sv-command set pane Follow-Playback Scroll - for zoom in 950 900 850 800 750 700 650 600 550 512 450 400 350 300 256 192 160 128 96 64 48 32 24 16; do - sv-command zoom $zoom - sleep 0.1 - done -} - -play() -{ - sv-command play "$@" -} - -fade_in() -{ - sv-command set gain 0 - sleep 0.5 - play "$@" - for gain in 0.001 0.01 0.05 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1; do - sv-command set gain $gain - sleep 0.1 - done -} - -fade_out() -{ - for gain in 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0.05 0.01 0.001; do - sv-command set gain $gain - sleep 0.1 - done - stop - sv-command set gain 1 -} - -slow() -{ -# for speed in -1 -10 -20 -30 -40 -50 -60 -70 -80 -100 -140 -200 -250 -300 -400 -500 -700 -800 -900 -1000; do -# sv-command set speedup "$speed" -# sleep 1 -# done - for speed in -20 -100 -1000; do - sv-command set speedup "$speed" - sleep 10 - done -} - -stop() -{ - sv-command stop "$@" - sv-command set speedup 0 -} - -quit() -{ - sv-command quit -} - -add_melodic_range_spectrogram() -{ - sv-command set propertyboxes 1 - sv-command add spectrogram - sv-command set layer Window-Size 8192 -# sv-command set layer Window-Size 4096 - sv-command set layer Window-Overlap 4 -# sv-command set layer Window-Overlap 3 - sv-command set layer Frequency-Scale Log - sv-command set layer Colour-Scale Meter -} - -zoom_in_spectrogram() -{ - sv-command zoomvertical 43 8000 - for x in 1 2 3 4 5 6; do - max=$((8000 - 1000*$x)) - sv-command zoomvertical 43 "$max" - sleep 0.5 - done - for x in 1 2 3 4 5; do - max=$((2000 - 100 * $x)) - sv-command zoomvertical 43 "$max" - sleep 0.5 - done -} - -zoom_in_spectrogram_further() -{ - for x in 1 2 3 4 5; do - sv-command zoomvertical in - done -} - -playback_bits() -{ - sv-command setcurrent 1 - sv-command set pane Global-Zoom off - sv-command set pane Global-Scroll off - sv-command set pane Follow-Playback Scroll - sv-command jump 10 - sv-command setcurrent 1 1 - sv-command delete layer - sv-command setcurrent 1 1 -# sv-command setcurrent 1 2 - sv-command set layer Colour Blue - sleep 5 - hide_stuff - sv-command set overlays 0 - sv-command set zoomwheels 0 - sv-command set propertyboxes 0 - fade_in - sleep 10 -# sv-command set layer Colour Blue -# sleep 1 -# sv-command set layer Colour Orange -# sleep 1 -# sv-command set layer Colour Red -# sleep 1 -# sv-command set layer Colour Green -# sleep 1 -# sleep 1 - - -# scroll_and_zoom - -# sv-command set overlays 0 -# sv-command set zoomwheels 0 -# sv-command set propertyboxes 0 -# sv-command setcurrent 1 1 -# sv-command delete layer -# sv-command setcurrent 1 1 -# sv-command set layer Colour Red -# sleep 1 -# sv-command set pane Global-Zoom off -# sv-command set pane Global-Scroll off -# sv-command set pane Follow-Playback Scroll - sv-command set zoomwheels 1 - sleep 1 - for zoom in 950 900 850 800 750 700 650 600 550 512 450 400 350 300 256 192 160 128 96 64 48 32 24 16; do - sv-command zoom $zoom - sleep 0.1 - done - - sleep 1 - sv-command set zoomwheels 0 - sv-command zoom 16 - - sleep 10 - #slow - #sv-command set layer Normalize-Visible-Area on -# for zoom in 15 14 13 12 11 10 9 8 7 6 5 4 ; do -# sv-command zoom $zoom -# sleep 0.1 - # done - sleep 1 - sv-command set zoomwheels 0 - slow - sleep 7 - fade_out - sv-command setcurrent 1 - sv-command set pane Follow-Playback Page - sv-command set pane Global-Zoom on - sv-command set pane Global-Scroll on - done_playback_bits=1 -} - -spectrogram_bits() -{ - sv-command set pane Global-Zoom on - sv-command zoom 1024 - add_melodic_range_spectrogram - sv-command zoom 1024 - sleep 5 - sv-command jump 10 - sleep 20 - zoom_in_spectrogram - sleep 20 - - sv-command select 7.5 11 - fade_in selection - sleep 10 - sv-command set speedup -200 - sleep 10 - sv-command setcurrent 1 - sv-command delete pane - sv-command zoom in - sv-command setcurrent 1 2 - sv-command set layer Normalize-Columns off - sv-command set layer Normalize-Visible-Area on - sleep 20 - sv-command set speedup 0 - sleep 10 - sv-command select none -# fade_out - -# if [ -n "$done_playback_bits" ]; then -# sv-command setcurrent 1 -# sv-command zoom out -# sv-command zoom outvamp:qm-vamp-plugins:qm-chromagram:chromagram -# sv-command zoom out -# sv-command zoom out -# sv-command zoom out -# sv-command setcurrent 2 -# fi - -# hide_stuff -# fade_in - sleep 10 -# sv-command set layer Bin-Display Frequencies -# sv-command set layer Normalize-Columns on -# sleep 20 - sv-command set layer Bin-Display "All Bins" - sv-command set layer Normalize-Columns on - sv-command set layer Normalize-Visible-Area off - sv-command set layer Colour-Scale 0 - sv-command set layer Colour "Red on Blue" - sv-command zoomvertical 23 800 - sleep 20 - sv-command transform $onsets - sv-command set layer Colour Orange - sleep 20 - fade_out - sleep 1 -# sv-command jump 10 -# sv-command setcurrent 1 2 -# sv-command set layer Colour "Black on White" -# sv-command transform $notes -# sv-command set layer Colour Orange - sleep 10 -# sv-command setcurrent 1 3 -# sv-command delete layer - sv-command setcurrent 1 3 - sv-command delete layer - sv-command setcurrent 1 2 - sv-command set layer Colour Default - done_spectrogram_bits=1 - -# zoom_in_spectrogram_further -} - -onset_bits() -{ - show_stuff - sv-command set zoomwheels 0 - sv-command setcurrent 1 - sv-command set pane Global-Zoom on - sv-command set pane Global-Scroll on - sleep 0.5 - sv-command set layer Colour Blue - sleep 0.5 - sv-command set layer Colour Orange - sleep 0.5 - sv-command set layer Colour Red - sleep 0.5 - sv-command set layer Colour Green - sleep 1 -# sleep 1 -# if [ -n "$done_spectrogram_bits" ]; then -# sv-command setcurrent 2 -# sv-command delete pane -# fi -# sv-command zoom default -# sv-command zoom in -# sv-command zoom in -# sv-command zoom in - sv-command zoom 192 - sv-command zoom in - sv-command add timeruler - sv-command jump 0 - sv-command transform $df - sv-command set layer Colour Black - sleep 5 - sv-command set layer Plot-Type Curve - sleep 5 - sv-command jump 30 - sv-command setcurrent 1 - sv-command set pane Follow-Playback Page - sv-command transform $df - sv-command set layer Colour Red - sleep 5 - sv-command jump 30 - sleep 5 - if [ "$RANDOM" -lt 16384 ]; then - sv-command set layer Vertical-Scale "Log Scale" - fi - sv-command set layer Plot-Type Segmentation - sleep 5 -# hide_stuff - sleep 10 - sv-command set overlays 0 - sv-command set propertyboxes 0 -# sv-command setcurrent 1 1 -# sv-command set layer Colour Black -# sv-command setcurrent 1 2 - sleep 2 - fade_in - sleep 2 - sv-command transform $onsets - sv-command set layer Colour Black - sv-command setcurrent 2 - sv-command transform $onsets - sv-command set layer Colour Blue - sleep 20 -# sv-command setcurrent 2 -# sv-command transform vamp:qm-vamp-plugins:qm-tempotracker:beats -# sv-command transform $beats - sleep 20 -# fade_out -# show_stuff -} - -selection_bits() -{ -# reset - sv-command set overlays 1 - sv-command set zoomwheels 0 - sv-command resize 1000 500 - sv-command zoom default - sv-command setcurrent 2 - sv-command delete pane -# if [ -n "$done_playback_bits" ]; then - sv-command setcurrent 1 2 -# else -# sv-command setcurrent 1 3 -# fi - sv-command delete layer -# if [ -n "$done_playback_bits" ]; then - sv-command setcurrent 1 2 -# else -# sv-command setcurrent 1 3 -# fi - sv-command delete layer - sv-command setcurrent 1 2 - sv-command set layer Colour Orange -# sv-command transform vamp:qm-vamp-plugins:qm-tempotracker:beats - sv-command transform $beats -# sv-command setcurrent 1 2 - sv-command set layer Colour Black - sleep 20 - sv-command loop on - base=$((RANDOM % 100)) - sv-command select $base $base.3 -# fade_in selection - play selection - sleep 8 - base=$((base + 4)) - sv-command addselect $base $base.1 - #sleep 12 - base=$((base + 2)) - sv-command addselect $base $base.1 - #sleep 6 - base=$((base + 2)) - sv-command addselect $base $base.3 - #sleep 6 - base=$((base + 3)) - sv-command addselect $base $base.3 - #sleep 6 - base=$((base + 2)) - sv-command addselect $base $base.3 - sleep 4 - sv-command delete layer - sleep 16 - sv-command set speedup -50 - sleep 14 - sv-command set speedup 50 - sleep 8 - sv-command set speedup 100 - sleep 5 - sv-command set speedup 200 - fade_out -# sleep 10 - sv-command select none - sv-command set overlays 2 - sv-command set propertyboxes 1 -# sv-command setcurrent 1 3 -# sv-command delete layer - sv-command setcurrent 1 2 - sv-command set layer Colour Black -} - -chromagram_bits() -{ -# add_melodic_range_spectrogram -# sleep 10 - sv-command add timeruler - sleep 5 - sv-command jump 10 - sv-command zoom out - sleep 5 - sv-command transform $chromagram - sleep 40 - sv-command zoom out - fade_in - sleep 20 - fade_out -} - -while /bin/true; do - -sleep 2 -load_a_file -sv-command loop on - -sv-command resize 1000 500 -show_stuff -sleep 5 -sleep 20 -playback_bits - -#sleep 10 -sv-command resize 1000 700 -sv-command zoom default -show_stuff -onset_bits - -selection_bits - -#sv-command resize 1000 700 - -#sleep 10 -sv-command resize 1000 700 -#show_stuff -spectrogram_bits - -#sleep 10 -#sv-command jump 0 -#show_stuff -#chromagram_bits - -sleep 20 - -#reset -killall -9 sonic-visualiser - -done
--- a/osc/sv-command Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -#!/bin/sh -# -# A very simple command shell for Sonic Visualiser. -# -# This provides a wrapper for the sv-osc-send program, which is a -# generic OSC sending program (not specific to SV, despite its name). -# This script attempts to guess the OSC port number for an SV -# process running on the local host, and then composes a method name -# and arguments into a complete OSC call. -# -# You can either run this with the method and its arguments on the -# command line, e.g. "sv-command set layer Frequency-Scale Log", or -# you can provide a series of method + argument commands on stdin. -# -# Unless you use the -q option, this script will echo the OSC URL -# and arguments that it is sending for each command. -# -# Note that the method and arguments may not contain spaces. -# -# Chris Cannam, Nov 2006 - -quiet= -if [ "$1" = "-q" ]; then - quiet=true; shift; -fi - -# The yucky bit - -port=`lsof -c sonic- | \ - grep UDP | \ - sed -e 's/^.*[^0-9]\([0-9][0-9]*\) *$/\1/' | \ - grep -v ' ' | \ - head -1 ` - -host=127.0.0.1 -scheme=osc.udp - -if [ -z "$port" ]; then - echo "Sonic Visualiser OSC port not found" - exit 1 -fi - -if [ -n "$1" ]; then - command=$1; shift - [ -z "$quiet" ] && echo "$scheme://$host:$port/$command" "$@" - sv-osc-send "$scheme://$host:$port/$command" "$@" -else - while read command a1 a2 a3 a4 a5; do - [ -z "$command" ] && continue - [ -z "$quiet" ] && echo "$scheme://$host:$port/$command" $a1 $a2 $a3 $a4 $a5 - sv-osc-send "$scheme://$host:$port/$command" $a1 $a2 $a3 $a4 $a5 - done -fi - -exit 0
--- a/osc/sv-osc-send.c Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <lo/lo.h> - -void -usage(char *program_name) -{ - char *base_name = strrchr(program_name, '/'); - - if (base_name && *(base_name + 1) != 0) { - base_name += 1; - } else { - base_name = program_name; - } - - fprintf(stderr, "\nusage: %s <OSC URL> [<values>]\n\n", program_name); - fprintf(stderr, "example OSC URLs:\n\n" - " osc.udp://localhost:19383/path/test 1.0 4.2\n" - " osc.udp://my.host.org:10886/3/13/load file\n\n"); - fprintf(stderr, "numeric arguments will be treated as OSC 'f' floating point types.\n\n"); - exit(1); -} - -int main(int argc, char *argv[]) -{ - lo_address a; - char *url, *host, *port, *path; - lo_message message; - unsigned int i; - - if (argc < 2) { - usage(argv[0]); - /* does not return */ - } - url = argv[1]; - - host = lo_url_get_hostname(url); - port = lo_url_get_port(url); - path = lo_url_get_path(url); - a = lo_address_new(host, port); - - message = lo_message_new(); - - for (i = 0; i + 2 < argc; ++i) { - - int index = i + 2; - char *param; - - param = argv[index]; - if (!isdigit(param[0])) { - lo_message_add_string(message, argv[index]); - } else { - lo_message_add_float(message, atof(argv[index])); - } - } - - lo_send_message(a, path, message); - - if (lo_address_errno(a)) { - printf("liblo error: %s\n", lo_address_errstr(a)); - } - - free(host); - free(port); - free(path); - - return 0; -} -
--- a/sv.pro Wed Oct 24 16:00:30 2007 +0000 +++ b/sv.pro Wed Oct 24 16:34:31 2007 +0000 @@ -11,69 +11,33 @@ ICON = icons/sv-macicon.icns -DEPENDPATH += . .. audioio document i18n main osc transform -INCLUDEPATH += . .. audioio document transform osc main -LIBPATH = ../view ../layer ../data ../widgets ../plugin ../base ../system $$LIBPATH +DEPENDPATH += . .. i18n main transform +INCLUDEPATH += . .. transform main +LIBPATH = ../view ../layer ../data ../widgets ../plugin ../base ../system ../document ../audioio $$LIBPATH contains(DEFINES, BUILD_STATIC):LIBS -= -ljack -LIBS = -lsvview -lsvlayer -lsvdata -lsvwidgets -lsvplugin -lsvbase -lsvsystem $$LIBS +LIBS = -lsvdocument -lsvaudioio -lsvview -lsvlayer -lsvdata -lsvwidgets -lsvplugin -lsvbase -lsvsystem $$LIBS PRE_TARGETDEPS += ../view/libsvview.a \ ../layer/libsvlayer.a \ ../data/libsvdata.a \ + ../document/libsvdocument.a \ ../widgets/libsvwidgets.a \ ../plugin/libsvplugin.a \ ../base/libsvbase.a \ + ../audioio/libsvaudioio.a \ ../system/libsvsystem.a OBJECTS_DIR = tmp_obj MOC_DIR = tmp_moc # Input -HEADERS += audioio/AudioCallbackPlaySource.h \ - audioio/AudioCallbackPlayTarget.h \ - audioio/AudioCoreAudioTarget.h \ - audioio/AudioGenerator.h \ - audioio/AudioJACKTarget.h \ - audioio/AudioPortAudioTarget.h \ - audioio/AudioTargetFactory.h \ - audioio/PhaseVocoderTimeStretcher.h \ - audioio/PlaySpeedRangeMapper.h \ - document/Document.h \ - document/SVFileReader.h \ - main/MainWindow.h \ - main/MainWindowBase.h \ - main/PreferencesDialog.h \ - osc/OSCMessage.h \ - osc/OSCQueue.h \ - transform/FeatureExtractionPluginTransform.h \ - transform/PluginTransform.h \ - transform/RealTimePluginTransform.h \ - transform/Transform.h \ - transform/TransformFactory.h -SOURCES += audioio/AudioCallbackPlaySource.cpp \ - audioio/AudioCallbackPlayTarget.cpp \ - audioio/AudioCoreAudioTarget.cpp \ - audioio/AudioGenerator.cpp \ - audioio/AudioJACKTarget.cpp \ - audioio/AudioPortAudioTarget.cpp \ - audioio/AudioTargetFactory.cpp \ - audioio/PhaseVocoderTimeStretcher.cpp \ - audioio/PlaySpeedRangeMapper.cpp \ - document/Document.cpp \ - document/SVFileReader.cpp \ - main/main.cpp \ +HEADERS += main/MainWindow.h \ + main/PreferencesDialog.h +SOURCES += main/main.cpp \ main/MainWindow.cpp \ - main/MainWindowBase.cpp \ - main/PreferencesDialog.cpp \ - osc/OSCMessage.cpp \ - osc/OSCQueue.cpp \ - transform/FeatureExtractionPluginTransform.cpp \ - transform/PluginTransform.cpp \ - transform/RealTimePluginTransform.cpp \ - transform/Transform.cpp \ - transform/TransformFactory.cpp + main/PreferencesDialog.cpp RESOURCES += sonic-visualiser.qrc
--- a/transform/FeatureExtractionPluginTransform.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,553 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "FeatureExtractionPluginTransform.h" - -#include "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/PluginXml.h" -#include "vamp-sdk/Plugin.h" - -#include "data/model/Model.h" -#include "base/Window.h" -#include "data/model/SparseOneDimensionalModel.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/EditableDenseThreeDimensionalModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/NoteModel.h" -#include "data/model/FFTModel.h" -#include "data/model/WaveFileModel.h" - -#include <QMessageBox> - -#include <iostream> - -FeatureExtractionPluginTransform::FeatureExtractionPluginTransform(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString outputName) : - PluginTransform(inputModel, context), - m_plugin(0), - m_descriptor(0), - m_outputFeatureNo(0) -{ -// std::cerr << "FeatureExtractionPluginTransform::FeatureExtractionPluginTransform: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl; - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "FeatureExtractionPluginTransform: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate()); - - if (!m_plugin) { - std::cerr << "FeatureExtractionPluginTransform: Failed to instantiate plugin \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } - - DenseTimeValueModel *input = getInput(); - if (!input) return; - - size_t channelCount = input->getChannelCount(); - if (m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; - } - if (m_plugin->getMinChannelCount() > channelCount) { - std::cerr << "FeatureExtractionPluginTransform:: " - << "Can't provide enough channels to plugin (plugin min " - << m_plugin->getMinChannelCount() << ", max " - << m_plugin->getMaxChannelCount() << ", input model has " - << input->getChannelCount() << ")" << std::endl; - return; - } - - std::cerr << "Initialising feature extraction plugin with channels = " - << channelCount << ", step = " << m_context.stepSize - << ", block = " << m_context.blockSize << std::endl; - - if (!m_plugin->initialise(channelCount, - m_context.stepSize, - m_context.blockSize)) { - std::cerr << "FeatureExtractionPluginTransform: Plugin " - << m_plugin->getIdentifier() << " failed to initialise!" << std::endl; - return; - } - - Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); - - if (outputs.empty()) { - std::cerr << "FeatureExtractionPluginTransform: Plugin \"" - << pluginId.toStdString() << "\" has no outputs" << std::endl; - return; - } - - for (size_t i = 0; i < outputs.size(); ++i) { - if (outputName == "" || outputs[i].identifier == outputName.toStdString()) { - m_outputFeatureNo = i; - m_descriptor = new Vamp::Plugin::OutputDescriptor - (outputs[i]); - break; - } - } - - if (!m_descriptor) { - std::cerr << "FeatureExtractionPluginTransform: Plugin \"" - << pluginId.toStdString() << "\" has no output named \"" - << outputName.toStdString() << "\"" << std::endl; - return; - } - -// std::cerr << "FeatureExtractionPluginTransform: output sample type " -// << m_descriptor->sampleType << std::endl; - - int binCount = 1; - float minValue = 0.0, maxValue = 0.0; - bool haveExtents = false; - - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionPluginTransform: output bin count " -// << binCount << std::endl; - - if (binCount > 0 && m_descriptor->hasKnownExtents) { - minValue = m_descriptor->minValue; - maxValue = m_descriptor->maxValue; - haveExtents = true; - } - - size_t modelRate = m_input->getSampleRate(); - size_t modelResolution = 1; - - switch (m_descriptor->sampleType) { - - case Vamp::Plugin::OutputDescriptor::VariableSampleRate: - if (m_descriptor->sampleRate != 0.0) { - modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001); - } - break; - - case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: - modelResolution = m_context.stepSize; - break; - - case Vamp::Plugin::OutputDescriptor::FixedSampleRate: - modelRate = size_t(m_descriptor->sampleRate + 0.001); - break; - } - - if (binCount == 0) { - - m_output = new SparseOneDimensionalModel(modelRate, modelResolution, - false); - - } else if (binCount == 1) { - - SparseTimeValueModel *model; - if (haveExtents) { - model = new SparseTimeValueModel - (modelRate, modelResolution, minValue, maxValue, false); - } else { - model = new SparseTimeValueModel - (modelRate, modelResolution, false); - } - model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str()); - - m_output = model; - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - // We don't have a sparse 3D model, so interpret this as a - // note model. There's nothing to define which values to use - // as which parameters of the note -- for the moment let's - // treat the first as pitch, second as duration in frames, - // third (if present) as velocity. (Our note model doesn't - // yet store velocity.) - //!!! todo: ask the user! - - NoteModel *model; - if (haveExtents) { - model = new NoteModel - (modelRate, modelResolution, minValue, maxValue, false); - } else { - model = new NoteModel - (modelRate, modelResolution, false); - } - model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str()); - - m_output = model; - - } else { - - EditableDenseThreeDimensionalModel *model = - new EditableDenseThreeDimensionalModel - (modelRate, modelResolution, binCount, false); - - if (!m_descriptor->binNames.empty()) { - std::vector<QString> names; - for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) { - names.push_back(m_descriptor->binNames[i].c_str()); - } - model->setBinNames(names); - } - - m_output = model; - } -} - -FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform() -{ - std::cerr << "FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform()" << std::endl; - delete m_plugin; - delete m_descriptor; -} - -DenseTimeValueModel * -FeatureExtractionPluginTransform::getInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast<DenseTimeValueModel *>(getInputModel()); - if (!dtvm) { - std::cerr << "FeatureExtractionPluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -FeatureExtractionPluginTransform::run() -{ - DenseTimeValueModel *input = getInput(); - if (!input) return; - - if (!m_output) return; - - while (!input->isReady()) { -/* - if (dynamic_cast<WaveFileModel *>(input)) { - std::cerr << "FeatureExtractionPluginTransform::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl; - sleep(2); - break; // no need to wait - } -*/ - std::cerr << "FeatureExtractionPluginTransform::run: Waiting for input model to be ready..." << std::endl; - sleep(1); - } - - size_t sampleRate = m_input->getSampleRate(); - - size_t channelCount = input->getChannelCount(); - if (m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; - } - - float **buffers = new float*[channelCount]; - for (size_t ch = 0; ch < channelCount; ++ch) { - buffers[ch] = new float[m_context.blockSize + 2]; - } - - bool frequencyDomain = (m_plugin->getInputDomain() == - Vamp::Plugin::FrequencyDomain); - std::vector<FFTModel *> fftModels; - - if (frequencyDomain) { - for (size_t ch = 0; ch < channelCount; ++ch) { - FFTModel *model = new FFTModel - (getInput(), - channelCount == 1 ? m_context.channel : ch, - m_context.windowType, - m_context.blockSize, - m_context.stepSize, - m_context.blockSize, - false); - if (!model->isOK()) { - QMessageBox::critical - (0, tr("FFT cache failed"), - tr("Failed to create the FFT model for this transform.\n" - "There may be insufficient memory or disc space to continue.")); - delete model; - setCompletion(100); - return; - } - model->resume(); - fftModels.push_back(model); - } - } - - long startFrame = m_input->getStartFrame(); - long endFrame = m_input->getEndFrame(); - - long contextStart = m_context.startFrame; - long contextDuration = m_context.duration; - - if (contextStart == 0 || contextStart < startFrame) { - contextStart = startFrame; - } - - if (contextDuration == 0) { - contextDuration = endFrame - contextStart; - } - if (contextStart + contextDuration > endFrame) { - contextDuration = endFrame - contextStart; - } - - long blockFrame = contextStart; - - long prevCompletion = 0; - - setCompletion(0); - - while (!m_abandoned) { - - if (frequencyDomain) { - if (blockFrame - int(m_context.blockSize)/2 > - contextStart + contextDuration) break; - } else { - if (blockFrame >= - contextStart + contextDuration) break; - } - -// std::cerr << "FeatureExtractionPluginTransform::run: blockFrame " -// << blockFrame << ", endFrame " << endFrame << ", blockSize " -// << m_context.blockSize << std::endl; - - long completion = - (((blockFrame - contextStart) / m_context.stepSize) * 99) / - (contextDuration / m_context.stepSize); - - // channelCount is either m_input->channelCount or 1 - - for (size_t ch = 0; ch < channelCount; ++ch) { - if (frequencyDomain) { - int column = (blockFrame - startFrame) / m_context.stepSize; - for (size_t i = 0; i <= m_context.blockSize/2; ++i) { - fftModels[ch]->getValuesAt - (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); - } - } else { - getFrames(ch, channelCount, - blockFrame, m_context.blockSize, buffers[ch]); - } - } - - Vamp::Plugin::FeatureSet features = m_plugin->process - (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); - - for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { - Vamp::Plugin::Feature feature = - features[m_outputFeatureNo][fi]; - addFeature(blockFrame, feature); - } - - if (blockFrame == contextStart || completion > prevCompletion) { - setCompletion(completion); - prevCompletion = completion; - } - - blockFrame += m_context.stepSize; - } - - if (m_abandoned) return; - - Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); - - for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) { - Vamp::Plugin::Feature feature = - features[m_outputFeatureNo][fi]; - addFeature(blockFrame, feature); - } - - if (frequencyDomain) { - for (size_t ch = 0; ch < channelCount; ++ch) { - delete fftModels[ch]; - } - } - - setCompletion(100); -} - -void -FeatureExtractionPluginTransform::getFrames(int channel, int channelCount, - long startFrame, long size, - float *buffer) -{ - long offset = 0; - - if (startFrame < 0) { - for (int i = 0; i < size && startFrame + i < 0; ++i) { - buffer[i] = 0.0f; - } - offset = -startFrame; - size -= offset; - if (size <= 0) return; - startFrame = 0; - } - - long got = getInput()->getData - ((channelCount == 1 ? m_context.channel : channel), - startFrame, size, buffer + offset); - - while (got < size) { - buffer[offset + got] = 0.0; - ++got; - } - - if (m_context.channel == -1 && channelCount == 1 && - getInput()->getChannelCount() > 1) { - // use mean instead of sum, as plugin input - int cc = getInput()->getChannelCount(); - for (long i = 0; i < size; ++i) { - buffer[i] /= cc; - } - } -} - -void -FeatureExtractionPluginTransform::addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature) -{ - size_t inputRate = m_input->getSampleRate(); - -// std::cerr << "FeatureExtractionPluginTransform::addFeature(" -// << blockFrame << ")" << std::endl; - - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - - size_t frame = blockFrame; - - if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - if (!feature.hasTimestamp) { - std::cerr - << "WARNING: FeatureExtractionPluginTransform::addFeature: " - << "Feature has variable sample rate but no timestamp!" - << std::endl; - return; - } else { - frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate); - } - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::FixedSampleRate) { - - if (feature.hasTimestamp) { - //!!! warning: sampleRate may be non-integral - frame = Vamp::RealTime::realTime2Frame(feature.timestamp, - lrintf(m_descriptor->sampleRate)); - } else { - frame = m_output->getEndFrame(); - } - } - - if (binCount == 0) { - - SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>(); - if (!model) return; - model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); - - } else if (binCount == 1) { - - float value = 0.0; - if (feature.values.size() > 0) value = feature.values[0]; - - SparseTimeValueModel *model = getOutput<SparseTimeValueModel>(); - if (!model) return; - model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); -// std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl; - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - float pitch = 0.0; - if (feature.values.size() > 0) pitch = feature.values[0]; - - float duration = 1; - if (feature.values.size() > 1) duration = feature.values[1]; - - float velocity = 100; - if (feature.values.size() > 2) velocity = feature.values[2]; - - NoteModel *model = getOutput<NoteModel>(); - if (!model) return; - - model->addPoint(NoteModel::Point(frame, pitch, - lrintf(duration), - feature.label.c_str())); - - } else { - - DenseThreeDimensionalModel::Column values = feature.values; - - EditableDenseThreeDimensionalModel *model = - getOutput<EditableDenseThreeDimensionalModel>(); - if (!model) return; - - model->setColumn(frame / model->getResolution(), values); - } -} - -void -FeatureExtractionPluginTransform::setCompletion(int completion) -{ - int binCount = 1; - if (m_descriptor->hasFixedBinCount) { - binCount = m_descriptor->binCount; - } - -// std::cerr << "FeatureExtractionPluginTransform::setCompletion(" -// << completion << ")" << std::endl; - - if (binCount == 0) { - - SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>(); - if (!model) return; - model->setCompletion(completion); - - } else if (binCount == 1) { - - SparseTimeValueModel *model = getOutput<SparseTimeValueModel>(); - if (!model) return; - model->setCompletion(completion); - - } else if (m_descriptor->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - - NoteModel *model = getOutput<NoteModel>(); - if (!model) return; - model->setCompletion(completion); - - } else { - - EditableDenseThreeDimensionalModel *model = - getOutput<EditableDenseThreeDimensionalModel>(); - if (!model) return; - model->setCompletion(completion); - } -} -
--- a/transform/FeatureExtractionPluginTransform.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_ -#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_ - -#include "PluginTransform.h" - -class DenseTimeValueModel; - -class FeatureExtractionPluginTransform : public PluginTransform -{ - Q_OBJECT - -public: - FeatureExtractionPluginTransform(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString outputName = ""); - virtual ~FeatureExtractionPluginTransform(); - -protected: - virtual void run(); - - Vamp::Plugin *m_plugin; - Vamp::Plugin::OutputDescriptor *m_descriptor; - int m_outputFeatureNo; - - void addFeature(size_t blockFrame, - const Vamp::Plugin::Feature &feature); - - void setCompletion(int); - - void getFrames(int channel, int channelCount, - long startFrame, long size, float *buffer); - - // just casts - DenseTimeValueModel *getInput(); - template <typename ModelClass> ModelClass *getOutput() { - ModelClass *mc = dynamic_cast<ModelClass *>(m_output); - if (!mc) { - std::cerr << "FeatureExtractionPluginTransform::getOutput: Output model not conformable" << std::endl; - } - return mc; - } -}; - -#endif -
--- a/transform/PluginTransform.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 QMUL. - - 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 "PluginTransform.h" - -#include "vamp-sdk/PluginHostAdapter.h" -#include "vamp-sdk/hostext/PluginWrapper.h" - -PluginTransform::PluginTransform(Model *inputModel, - const ExecutionContext &context) : - Transform(inputModel), - m_context(context) -{ -} - -PluginTransform::ExecutionContext::ExecutionContext(int _c, size_t _bs) : - channel(_c), - domain(Vamp::Plugin::TimeDomain), - stepSize(_bs ? _bs : 1024), - blockSize(_bs ? _bs : 1024), - windowType(HanningWindow), - startFrame(0), - duration(0), - sampleRate(0) -{ -} - -PluginTransform::ExecutionContext::ExecutionContext(int _c, size_t _ss, - size_t _bs, WindowType _wt) : - channel(_c), - domain(Vamp::Plugin::FrequencyDomain), - stepSize(_ss ? _ss : (_bs ? _bs / 2 : 512)), - blockSize(_bs ? _bs : 1024), - windowType(_wt), - startFrame(0), - duration(0), - sampleRate(0) -{ -} - -PluginTransform::ExecutionContext::ExecutionContext(int _c, - const Vamp::PluginBase *_plugin) : - channel(_c), - domain(Vamp::Plugin::TimeDomain), - stepSize(0), - blockSize(0), - windowType(HanningWindow), - startFrame(0), - duration(0), - sampleRate(0) -{ - makeConsistentWithPlugin(_plugin); -} - -bool -PluginTransform::ExecutionContext::operator==(const ExecutionContext &c) -{ - return (c.channel == channel && - c.domain == domain && - c.stepSize == stepSize && - c.blockSize == blockSize && - c.windowType == windowType && - c.startFrame == startFrame && - c.duration == duration && - c.sampleRate == sampleRate); -} - -void -PluginTransform::ExecutionContext::makeConsistentWithPlugin(const Vamp::PluginBase *_plugin) -{ - const Vamp::Plugin *vp = dynamic_cast<const Vamp::Plugin *>(_plugin); - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; - vp = dynamic_cast<const Vamp::PluginHostAdapter *>(_plugin); //!!! why? -} - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; - vp = dynamic_cast<const Vamp::HostExt::PluginWrapper *>(_plugin); //!!! no, I mean really why? - } - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; - } - - if (!vp) { - domain = Vamp::Plugin::TimeDomain; - if (!stepSize) { - if (!blockSize) blockSize = 1024; - stepSize = blockSize; - } else { - if (!blockSize) blockSize = stepSize; - } - } else { - domain = vp->getInputDomain(); - if (!stepSize) stepSize = vp->getPreferredStepSize(); - if (!blockSize) blockSize = vp->getPreferredBlockSize(); - if (!blockSize) blockSize = 1024; - if (!stepSize) { - if (domain == Vamp::Plugin::FrequencyDomain) { -// std::cerr << "frequency domain, step = " << blockSize/2 << std::endl; - stepSize = blockSize/2; - } else { -// std::cerr << "time domain, step = " << blockSize/2 << std::endl; - stepSize = blockSize; - } - } - } -} - -
--- a/transform/PluginTransform.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 QMUL. - - 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. -*/ - -#ifndef _PLUGIN_TRANSFORM_H_ -#define _PLUGIN_TRANSFORM_H_ - -#include "Transform.h" - -#include "base/Window.h" - -#include "vamp-sdk/Plugin.h" - -//!!! should this just move back up to Transform? It is after all used -//directly in all sorts of generic places, like Document - -class PluginTransform : public Transform -{ -public: - class ExecutionContext { - public: - // Time domain: - ExecutionContext(int _c = -1, size_t _bs = 0); - - // Frequency domain: - ExecutionContext(int _c, size_t _ss, size_t _bs, WindowType _wt); - - // From plugin defaults: - ExecutionContext(int _c, const Vamp::PluginBase *_plugin); - - bool operator==(const ExecutionContext &); - - void makeConsistentWithPlugin(const Vamp::PluginBase *_plugin); - - int channel; - Vamp::Plugin::InputDomain domain; - size_t stepSize; - size_t blockSize; - WindowType windowType; - size_t startFrame; - size_t duration; // 0 -> whole thing - float sampleRate; // 0 -> model's rate - }; - -protected: - PluginTransform(Model *inputModel, - const ExecutionContext &context); - - ExecutionContext m_context; -}; - -#endif
--- a/transform/RealTimePluginTransform.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,274 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "RealTimePluginTransform.h" - -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginXml.h" - -#include "data/model/Model.h" -#include "data/model/SparseTimeValueModel.h" -#include "data/model/DenseTimeValueModel.h" -#include "data/model/WritableWaveFileModel.h" -#include "data/model/WaveFileModel.h" - -#include <iostream> - -RealTimePluginTransform::RealTimePluginTransform(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString units, - int output) : - PluginTransform(inputModel, context), - m_pluginId(pluginId), - m_configurationXml(configurationXml), - m_units(units), - m_plugin(0), - m_outputNo(output) -{ - if (!m_context.blockSize) m_context.blockSize = 1024; - -// std::cerr << "RealTimePluginTransform::RealTimePluginTransform: plugin " << pluginId.toStdString() << ", output " << output << std::endl; - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "RealTimePluginTransform: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - DenseTimeValueModel *input = getInput(); - if (!input) return; - - m_plugin = factory->instantiatePlugin(pluginId, 0, 0, - m_input->getSampleRate(), - m_context.blockSize, - input->getChannelCount()); - - if (!m_plugin) { - std::cerr << "RealTimePluginTransform: Failed to instantiate plugin \"" - << pluginId.toStdString() << "\"" << std::endl; - return; - } - - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } - - if (m_outputNo >= 0 && - m_outputNo >= int(m_plugin->getControlOutputCount())) { - std::cerr << "RealTimePluginTransform: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; - return; - } - - if (m_outputNo == -1) { - - size_t outputChannels = m_plugin->getAudioOutputCount(); - if (outputChannels > input->getChannelCount()) { - outputChannels = input->getChannelCount(); - } - - WritableWaveFileModel *model = new WritableWaveFileModel - (input->getSampleRate(), outputChannels); - - m_output = model; - - } else { - - SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); - - if (units != "") model->setScaleUnits(units); - - m_output = model; - } -} - -RealTimePluginTransform::~RealTimePluginTransform() -{ - delete m_plugin; -} - -DenseTimeValueModel * -RealTimePluginTransform::getInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast<DenseTimeValueModel *>(getInputModel()); - if (!dtvm) { - std::cerr << "RealTimePluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; - } - return dtvm; -} - -void -RealTimePluginTransform::run() -{ - DenseTimeValueModel *input = getInput(); - if (!input) return; - - while (!input->isReady()) { - if (dynamic_cast<WaveFileModel *>(input)) break; // no need to wait - std::cerr << "RealTimePluginTransform::run: Waiting for input model to be ready..." << std::endl; - sleep(1); - } - - SparseTimeValueModel *stvm = dynamic_cast<SparseTimeValueModel *>(m_output); - WritableWaveFileModel *wwfm = dynamic_cast<WritableWaveFileModel *>(m_output); - if (!stvm && !wwfm) return; - - if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; - - size_t sampleRate = input->getSampleRate(); - size_t channelCount = input->getChannelCount(); - if (!wwfm && m_context.channel != -1) channelCount = 1; - - long blockSize = m_plugin->getBufferSize(); - - float **inbufs = m_plugin->getAudioInputBuffers(); - - long startFrame = m_input->getStartFrame(); - long endFrame = m_input->getEndFrame(); - - long contextStart = m_context.startFrame; - long contextDuration = m_context.duration; - - if (contextStart == 0 || contextStart < startFrame) { - contextStart = startFrame; - } - - if (contextDuration == 0) { - contextDuration = endFrame - contextStart; - } - if (contextStart + contextDuration > endFrame) { - contextDuration = endFrame - contextStart; - } - - wwfm->setStartFrame(contextStart); - - long blockFrame = contextStart; - - long prevCompletion = 0; - - long latency = m_plugin->getLatency(); - - while (blockFrame < contextStart + contextDuration + latency && - !m_abandoned) { - - long completion = - (((blockFrame - contextStart) / blockSize) * 99) / - ((contextDuration) / blockSize); - - long got = 0; - - if (channelCount == 1) { - if (inbufs && inbufs[0]) { - got = input->getData - (m_context.channel, blockFrame, blockSize, inbufs[0]); - while (got < blockSize) { - inbufs[0][got++] = 0.0; - } - } - for (size_t ch = 1; ch < m_plugin->getAudioInputCount(); ++ch) { - for (long i = 0; i < blockSize; ++i) { - inbufs[ch][i] = inbufs[0][i]; - } - } - } else { - for (size_t ch = 0; ch < channelCount; ++ch) { - if (inbufs && inbufs[ch]) { - got = input->getData - (ch, blockFrame, blockSize, inbufs[ch]); - while (got < blockSize) { - inbufs[ch][got++] = 0.0; - } - } - } - for (size_t ch = channelCount; ch < m_plugin->getAudioInputCount(); ++ch) { - for (long i = 0; i < blockSize; ++i) { - inbufs[ch][i] = inbufs[ch % channelCount][i]; - } - } - } - -/* - std::cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< std::endl; - - for (size_t ch = 0; ch < m_plugin->getAudioInputCount(); ++ch) { - std::cerr << "Input channel " << ch << std::endl; - for (size_t i = 0; i < 100; ++i) { - std::cerr << inbufs[ch][i] << " "; - if (isnan(inbufs[ch][i])) { - std::cerr << "\n\nWARNING: NaN in audio input" << std::endl; - } - } - } -*/ - - m_plugin->run(Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); - - if (stvm) { - - float value = m_plugin->getControlOutputValue(m_outputNo); - - long pointFrame = blockFrame; - if (pointFrame > latency) pointFrame -= latency; - else pointFrame = 0; - - stvm->addPoint(SparseTimeValueModel::Point - (pointFrame, value, "")); - - } else if (wwfm) { - - float **outbufs = m_plugin->getAudioOutputBuffers(); - - if (outbufs) { - - if (blockFrame >= latency) { - long writeSize = std::min - (blockSize, - contextStart + contextDuration + latency - blockFrame); - wwfm->addSamples(outbufs, writeSize); - } else if (blockFrame + blockSize >= latency) { - long offset = latency - blockFrame; - long count = blockSize - offset; - float **tmp = new float *[channelCount]; - for (size_t c = 0; c < channelCount; ++c) { - tmp[c] = outbufs[c] + offset; - } - wwfm->addSamples(tmp, count); - delete[] tmp; - } - } - } - - if (blockFrame == contextStart || completion > prevCompletion) { - if (stvm) stvm->setCompletion(completion); - if (wwfm) wwfm->setCompletion(completion); - prevCompletion = completion; - } - - blockFrame += blockSize; - } - - if (m_abandoned) return; - - if (stvm) stvm->setCompletion(100); - if (wwfm) wwfm->setCompletion(100); -} -
--- a/transform/RealTimePluginTransform.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _REAL_TIME_PLUGIN_TRANSFORM_H_ -#define _REAL_TIME_PLUGIN_TRANSFORM_H_ - -#include "PluginTransform.h" -#include "plugin/RealTimePluginInstance.h" - -class DenseTimeValueModel; - -class RealTimePluginTransform : public PluginTransform -{ -public: - RealTimePluginTransform(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString units = "", - int output = -1); // -1 -> audio, 0+ -> data - virtual ~RealTimePluginTransform(); - -protected: - virtual void run(); - - QString m_pluginId; - QString m_configurationXml; - QString m_units; - - RealTimePluginInstance *m_plugin; - int m_outputNo; - - // just casts - DenseTimeValueModel *getInput(); -}; - -#endif -
--- a/transform/Transform.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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 "Transform.h" - -Transform::Transform(Model *m) : - m_input(m), - m_output(0), - m_detached(false), - m_abandoned(false) -{ -} - -Transform::~Transform() -{ - m_abandoned = true; - wait(); - if (!m_detached) delete m_output; -} -
--- a/transform/Transform.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - 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. -*/ - -#ifndef _TRANSFORM_H_ -#define _TRANSFORM_H_ - -#include "base/Thread.h" - -#include "data/model/Model.h" - -typedef QString TransformId; - -/** - * A Transform turns one data model into another. - * - * Typically in this application, a Transform might have a - * DenseTimeValueModel as its input (e.g. an audio waveform) and a - * SparseOneDimensionalModel (e.g. detected beats) as its output. - * - * The Transform typically runs in the background, as a separate - * thread populating the output model. The model is available to the - * user of the Transform immediately, but may be initially empty until - * the background thread has populated it. - */ - -class Transform : public Thread -{ -public: - virtual ~Transform(); - - // Just a hint to the processing thread that it should give up. - // Caller should still wait() and/or delete the transform before - // assuming its input and output models are no longer required. - void abandon() { m_abandoned = true; } - - Model *getInputModel() { return m_input; } - Model *getOutputModel() { return m_output; } - Model *detachOutputModel() { m_detached = true; return m_output; } - -protected: - Transform(Model *m); - - Model *m_input; // I don't own this - Model *m_output; // I own this, unless... - bool m_detached; // ... this is true. - bool m_abandoned; -}; - -#endif
--- a/transform/TransformFactory.cpp Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,861 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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 "TransformFactory.h" - -#include "FeatureExtractionPluginTransform.h" -#include "RealTimePluginTransform.h" - -#include "plugin/FeatureExtractionPluginFactory.h" -#include "plugin/RealTimePluginFactory.h" -#include "plugin/PluginXml.h" - -#include "widgets/PluginParameterDialog.h" - -#include "data/model/DenseTimeValueModel.h" - -#include "vamp-sdk/PluginHostAdapter.h" - -#include "sv/audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here - -#include <iostream> -#include <set> - -#include <QRegExp> - -TransformFactory * -TransformFactory::m_instance = new TransformFactory; - -TransformFactory * -TransformFactory::getInstance() -{ - return m_instance; -} - -TransformFactory::~TransformFactory() -{ -} - -TransformFactory::TransformList -TransformFactory::getAllTransforms() -{ - if (m_transforms.empty()) populateTransforms(); - - std::set<TransformDesc> dset; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - dset.insert(i->second); - } - - TransformList list; - for (std::set<TransformDesc>::const_iterator i = dset.begin(); - i != dset.end(); ++i) { - list.push_back(*i); - } - - return list; -} - -std::vector<QString> -TransformFactory::getAllTransformTypes() -{ - if (m_transforms.empty()) populateTransforms(); - - std::set<QString> types; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - types.insert(i->second.type); - } - - std::vector<QString> rv; - for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) { - rv.push_back(*i); - } - - return rv; -} - -std::vector<QString> -TransformFactory::getTransformCategories(QString transformType) -{ - if (m_transforms.empty()) populateTransforms(); - - std::set<QString> categories; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - if (i->second.type == transformType) { - categories.insert(i->second.category); - } - } - - bool haveEmpty = false; - - std::vector<QString> rv; - for (std::set<QString>::iterator i = categories.begin(); - i != categories.end(); ++i) { - if (*i != "") rv.push_back(*i); - else haveEmpty = true; - } - - if (haveEmpty) rv.push_back(""); // make sure empty category sorts last - - return rv; -} - -std::vector<QString> -TransformFactory::getTransformMakers(QString transformType) -{ - if (m_transforms.empty()) populateTransforms(); - - std::set<QString> makers; - for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { - if (i->second.type == transformType) { - makers.insert(i->second.maker); - } - } - - bool haveEmpty = false; - - std::vector<QString> rv; - for (std::set<QString>::iterator i = makers.begin(); - i != makers.end(); ++i) { - if (*i != "") rv.push_back(*i); - else haveEmpty = true; - } - - if (haveEmpty) rv.push_back(""); // make sure empty category sorts last - - return rv; -} - -void -TransformFactory::populateTransforms() -{ - TransformDescriptionMap transforms; - - populateFeatureExtractionPlugins(transforms); - populateRealTimePlugins(transforms); - - // disambiguate plugins with similar names - - std::map<QString, int> names; - std::map<QString, QString> pluginSources; - std::map<QString, QString> pluginMakers; - - for (TransformDescriptionMap::iterator i = transforms.begin(); - i != transforms.end(); ++i) { - - TransformDesc desc = i->second; - - QString td = desc.name; - QString tn = td.section(": ", 0, 0); - QString pn = desc.identifier.section(":", 1, 1); - - if (pluginSources.find(tn) != pluginSources.end()) { - if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) { - ++names[tn]; - } - } else { - ++names[tn]; - pluginSources[tn] = pn; - pluginMakers[tn] = desc.maker; - } - } - - std::map<QString, int> counts; - m_transforms.clear(); - - for (TransformDescriptionMap::iterator i = transforms.begin(); - i != transforms.end(); ++i) { - - TransformDesc desc = i->second; - QString identifier = desc.identifier; - QString maker = desc.maker; - - QString td = desc.name; - QString tn = td.section(": ", 0, 0); - QString to = td.section(": ", 1); - - if (names[tn] > 1) { - maker.replace(QRegExp(tr(" [\\(<].*$")), ""); - tn = QString("%1 [%2]").arg(tn).arg(maker); - } - - if (to != "") { - desc.name = QString("%1: %2").arg(tn).arg(to); - } else { - desc.name = tn; - } - - m_transforms[identifier] = desc; - } -} - -void -TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) -{ - std::vector<QString> plugs = - FeatureExtractionPluginFactory::getAllPluginIdentifiers(); - - for (size_t i = 0; i < plugs.size(); ++i) { - - QString pluginId = plugs[i]; - - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - - Vamp::Plugin *plugin = - factory->instantiatePlugin(pluginId, 48000); - - if (!plugin) { - std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - - QString pluginName = plugin->getName().c_str(); - QString category = factory->getPluginCategory(pluginId); - - Vamp::Plugin::OutputList outputs = - plugin->getOutputDescriptors(); - - for (size_t j = 0; j < outputs.size(); ++j) { - - QString transformId = QString("%1:%2") - .arg(pluginId).arg(outputs[j].identifier.c_str()); - - QString userName; - QString friendlyName; - QString units = outputs[j].unit.c_str(); - QString description = plugin->getDescription().c_str(); - QString maker = plugin->getMaker().c_str(); - if (maker == "") maker = tr("<unknown maker>"); - - if (description == "") { - if (outputs.size() == 1) { - description = tr("Extract features using \"%1\" plugin (from %2)") - .arg(pluginName).arg(maker); - } else { - description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)") - .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); - } - } else { - if (outputs.size() == 1) { - description = tr("%1 using \"%2\" plugin (from %3)") - .arg(description).arg(pluginName).arg(maker); - } else { - description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)") - .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker); - } - } - - if (outputs.size() == 1) { - userName = pluginName; - friendlyName = pluginName; - } else { - userName = QString("%1: %2") - .arg(pluginName) - .arg(outputs[j].name.c_str()); - friendlyName = outputs[j].name.c_str(); - } - - bool configurable = (!plugin->getPrograms().empty() || - !plugin->getParameterDescriptors().empty()); - -// std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl; - - transforms[transformId] = - TransformDesc(tr("Analysis"), - category, - transformId, - userName, - friendlyName, - description, - maker, - units, - configurable); - } - - delete plugin; - } -} - -void -TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms) -{ - std::vector<QString> plugs = - RealTimePluginFactory::getAllPluginIdentifiers(); - - static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); - - for (size_t i = 0; i < plugs.size(); ++i) { - - QString pluginId = plugs[i]; - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - if (!factory) { - std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - - const RealTimePluginDescriptor *descriptor = - factory->getPluginDescriptor(pluginId); - - if (!descriptor) { - std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl; - continue; - } - -//!!! if (descriptor->controlOutputPortCount == 0 || -// descriptor->audioInputPortCount == 0) continue; - -// std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl; - - QString pluginName = descriptor->name.c_str(); - QString category = factory->getPluginCategory(pluginId); - bool configurable = (descriptor->parameterCount > 0); - QString maker = descriptor->maker.c_str(); - if (maker == "") maker = tr("<unknown maker>"); - - if (descriptor->audioInputPortCount > 0) { - - for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) { - - QString transformId = QString("%1:%2").arg(pluginId).arg(j); - QString userName; - QString units; - QString portName; - - if (j < descriptor->controlOutputPortNames.size() && - descriptor->controlOutputPortNames[j] != "") { - - portName = descriptor->controlOutputPortNames[j].c_str(); - - userName = tr("%1: %2") - .arg(pluginName) - .arg(portName); - - if (unitRE.indexIn(portName) >= 0) { - units = unitRE.cap(1); - } - - } else if (descriptor->controlOutputPortCount > 1) { - - userName = tr("%1: Output %2") - .arg(pluginName) - .arg(j + 1); - - } else { - - userName = pluginName; - } - - QString description; - - if (portName != "") { - description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)") - .arg(portName) - .arg(pluginName) - .arg(maker); - } else { - description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)") - .arg(j + 1) - .arg(pluginName) - .arg(maker); - } - - transforms[transformId] = - TransformDesc(tr("Effects Data"), - category, - transformId, - userName, - userName, - description, - maker, - units, - configurable); - } - } - - if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) { - - if (descriptor->audioOutputPortCount > 0) { - - QString transformId = QString("%1:A").arg(pluginId); - QString type = tr("Effects"); - - QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)") - .arg(pluginName) - .arg(maker); - - if (descriptor->audioInputPortCount == 0) { - type = tr("Generators"); - QString description = tr("Generate audio signal using \"%1\" plugin (from %2)") - .arg(pluginName) - .arg(maker); - } - - transforms[transformId] = - TransformDesc(type, - category, - transformId, - pluginName, - pluginName, - description, - maker, - "", - configurable); - } - } - } -} - -QString -TransformFactory::getTransformName(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].name; - } else return ""; -} - -QString -TransformFactory::getTransformFriendlyName(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].friendlyName; - } else return ""; -} - -QString -TransformFactory::getTransformUnits(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].units; - } else return ""; -} - -bool -TransformFactory::isTransformConfigurable(TransformId identifier) -{ - if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].configurable; - } else return false; -} - -bool -TransformFactory::getTransformChannelRange(TransformId identifier, - int &min, int &max) -{ - QString id = identifier.section(':', 0, 2); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - Vamp::Plugin *plugin = - FeatureExtractionPluginFactory::instanceFor(id)-> - instantiatePlugin(id, 48000); - if (!plugin) return false; - - min = plugin->getMinChannelCount(); - max = plugin->getMaxChannelCount(); - delete plugin; - - return true; - - } else if (RealTimePluginFactory::instanceFor(id)) { - - const RealTimePluginDescriptor *descriptor = - RealTimePluginFactory::instanceFor(id)-> - getPluginDescriptor(id); - if (!descriptor) return false; - - min = descriptor->audioInputPortCount; - max = descriptor->audioInputPortCount; - - return true; - } - - return false; -} - -bool -TransformFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin, - int &minChannels, int &maxChannels) -{ - Vamp::Plugin *vp = 0; - if ((vp = dynamic_cast<Vamp::Plugin *>(plugin)) || - (vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin))) { - minChannels = vp->getMinChannelCount(); - maxChannels = vp->getMaxChannelCount(); - return true; - } else { - return getTransformChannelRange(identifier, minChannels, maxChannels); - } -} - -Model * -TransformFactory::getConfigurationForTransform(TransformId identifier, - const std::vector<Model *> &candidateInputModels, - PluginTransform::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source, - size_t startFrame, - size_t duration) -{ - if (candidateInputModels.empty()) return 0; - - //!!! This will need revision -- we'll have to have a callback - //from the dialog for when the candidate input model is changed, - //as we'll need to reinitialise the channel settings in the dialog - Model *inputModel = candidateInputModels[0]; //!!! for now - QStringList candidateModelNames; - std::map<QString, Model *> modelMap; - for (size_t i = 0; i < candidateInputModels.size(); ++i) { - QString modelName = candidateInputModels[i]->objectName(); - QString origModelName = modelName; - int dupcount = 1; - while (modelMap.find(modelName) != modelMap.end()) { - modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount); - } - modelMap[modelName] = candidateInputModels[i]; - candidateModelNames.push_back(modelName); - } - - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); - QString outputLabel = ""; - QString outputDescription = ""; - - bool ok = false; - configurationXml = m_lastConfigurations[identifier]; - -// std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; - - Vamp::PluginBase *plugin = 0; - - bool frequency = false; - bool effect = false; - bool generator = false; - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl; - - Vamp::Plugin *vp = - FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin - (id, inputModel->getSampleRate()); - - if (vp) { - - plugin = vp; - frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain); - - std::vector<Vamp::Plugin::OutputDescriptor> od = - vp->getOutputDescriptors(); - if (od.size() > 1) { - for (size_t i = 0; i < od.size(); ++i) { - if (od[i].identifier == output.toStdString()) { - outputLabel = od[i].name.c_str(); - outputDescription = od[i].description.c_str(); - break; - } - } - } - } - - } else if (RealTimePluginFactory::instanceFor(id)) { - - RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); - const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id); - - if (desc->audioInputPortCount > 0 && - desc->audioOutputPortCount > 0 && - !desc->isSynth) { - effect = true; - } - - if (desc->audioInputPortCount == 0) { - generator = true; - } - - if (output != "A") { - int outputNo = output.toInt(); - if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) { - outputLabel = desc->controlOutputPortNames[outputNo].c_str(); - } - } - - size_t sampleRate = inputModel->getSampleRate(); - size_t blockSize = 1024; - size_t channels = 1; - if (effect && source) { - sampleRate = source->getTargetSampleRate(); - blockSize = source->getTargetBlockSize(); - channels = source->getTargetChannelCount(); - } - - RealTimePluginInstance *rtp = factory->instantiatePlugin - (id, 0, 0, sampleRate, blockSize, channels); - - plugin = rtp; - - if (effect && source && rtp) { - source->setAuditioningPlugin(rtp); - } - } - - if (plugin) { - - context = PluginTransform::ExecutionContext(context.channel, plugin); - - if (configurationXml != "") { - PluginXml(plugin).setParametersFromXml(configurationXml); - } - - int sourceChannels = 1; - if (dynamic_cast<DenseTimeValueModel *>(inputModel)) { - sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel) - ->getChannelCount(); - } - - int minChannels = 1, maxChannels = sourceChannels; - getChannelRange(identifier, plugin, minChannels, maxChannels); - - int targetChannels = sourceChannels; - if (!effect) { - if (sourceChannels < minChannels) targetChannels = minChannels; - if (sourceChannels > maxChannels) targetChannels = maxChannels; - } - - int defaultChannel = context.channel; - - PluginParameterDialog *dialog = new PluginParameterDialog(plugin); - - if (candidateModelNames.size() > 1 && !generator) { - dialog->setCandidateInputModels(candidateModelNames); - } - - if (startFrame != 0 || duration != 0) { - dialog->setShowSelectionOnlyOption(true); - } - - if (targetChannels > 0) { - dialog->setChannelArrangement(sourceChannels, targetChannels, - defaultChannel); - } - - dialog->setOutputLabel(outputLabel, outputDescription); - - dialog->setShowProcessingOptions(true, frequency); - - if (dialog->exec() == QDialog::Accepted) { - ok = true; - } - - QString selectedInput = dialog->getInputModel(); - if (selectedInput != "") { - if (modelMap.find(selectedInput) != modelMap.end()) { - inputModel = modelMap[selectedInput]; - std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl; - } else { - std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl; - } - } else { - std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl; - } - - configurationXml = PluginXml(plugin).toXmlString(); - context.channel = dialog->getChannel(); - - if (startFrame != 0 || duration != 0) { - if (dialog->getSelectionOnly()) { - context.startFrame = startFrame; - context.duration = duration; - } - } - - dialog->getProcessingParameters(context.stepSize, - context.blockSize, - context.windowType); - - context.makeConsistentWithPlugin(plugin); - - delete dialog; - - if (effect && source) { - source->setAuditioningPlugin(0); // will delete our plugin - } else { - delete plugin; - } - } - - if (ok) m_lastConfigurations[identifier] = configurationXml; - - return ok ? inputModel : 0; -} - -PluginTransform::ExecutionContext -TransformFactory::getDefaultContextForTransform(TransformId identifier, - Model *inputModel) -{ - PluginTransform::ExecutionContext context(-1); - - QString id = identifier.section(':', 0, 2); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - Vamp::Plugin *vp = - FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin - (id, inputModel ? inputModel->getSampleRate() : 48000); - - if (vp) { - context = PluginTransform::ExecutionContext(-1, vp); - delete vp; - } - } - - return context; -} - -Transform * -TransformFactory::createTransform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml) -{ - Transform *transform = 0; - - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); - - if (FeatureExtractionPluginFactory::instanceFor(id)) { - transform = new FeatureExtractionPluginTransform(inputModel, - id, - context, - configurationXml, - output); - } else if (RealTimePluginFactory::instanceFor(id)) { - transform = new RealTimePluginTransform(inputModel, - id, - context, - configurationXml, - getTransformUnits(identifier), - output == "A" ? -1 : - output.toInt()); - } else { - std::cerr << "TransformFactory::createTransform: Unknown transform \"" - << identifier.toStdString() << "\"" << std::endl; - return transform; - } - - if (transform) transform->setObjectName(identifier); - return transform; -} - -Model * -TransformFactory::transform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml) -{ - Transform *t = createTransform(identifier, inputModel, context, - configurationXml); - - if (!t) return 0; - - connect(t, SIGNAL(finished()), this, SLOT(transformFinished())); - - m_runningTransforms.insert(t); - - t->start(); - Model *model = t->detachOutputModel(); - - if (model) { - QString imn = inputModel->objectName(); - QString trn = getTransformFriendlyName(identifier); - if (imn != "") { - if (trn != "") { - model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); - } else { - model->setObjectName(imn); - } - } else if (trn != "") { - model->setObjectName(trn); - } - } else { - t->wait(); - } - - return model; -} - -void -TransformFactory::transformFinished() -{ - QObject *s = sender(); - Transform *transform = dynamic_cast<Transform *>(s); - - std::cerr << "TransformFactory::transformFinished(" << transform << ")" << std::endl; - - if (!transform) { - std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl; - return; - } - - if (m_runningTransforms.find(transform) == m_runningTransforms.end()) { - std::cerr << "WARNING: TransformFactory::transformFinished(" - << transform - << "): I have no record of this transform running!" - << std::endl; - } - - m_runningTransforms.erase(transform); - - transform->wait(); // unnecessary but reassuring - delete transform; -} - -void -TransformFactory::modelAboutToBeDeleted(Model *m) -{ - TransformSet affected; - - for (TransformSet::iterator i = m_runningTransforms.begin(); - i != m_runningTransforms.end(); ++i) { - - Transform *t = *i; - - if (t->getInputModel() == m || t->getOutputModel() == m) { - affected.insert(t); - } - } - - for (TransformSet::iterator i = affected.begin(); - i != affected.end(); ++i) { - - Transform *t = *i; - - t->abandon(); - - t->wait(); // this should eventually call back on - // transformFinished, which will remove from - // m_runningTransforms and delete. - } -} -
--- a/transform/TransformFactory.h Wed Oct 24 16:00:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - 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. -*/ - -#ifndef _TRANSFORM_FACTORY_H_ -#define _TRANSFORM_FACTORY_H_ - -#include "Transform.h" -#include "PluginTransform.h" - -#include <map> -#include <set> - -namespace Vamp { class PluginBase; } - -class AudioCallbackPlaySource; - -class TransformFactory : public QObject -{ - Q_OBJECT - -public: - virtual ~TransformFactory(); - - static TransformFactory *getInstance(); - - // The identifier is intended to be computer-referenceable, and - // unique within the application. The name is intended to be - // human readable. In principle it doesn't have to be unique, but - // the factory will add suffixes to ensure that it is, all the - // same (just to avoid user confusion). The friendly name is a - // shorter version of the name. The type is also intended to be - // user-readable, for use in menus. - - struct TransformDesc { - - TransformDesc() { } - TransformDesc(QString _type, QString _category, - TransformId _identifier, QString _name, - QString _friendlyName, QString _description, - QString _maker, QString _units, bool _configurable) : - type(_type), category(_category), - identifier(_identifier), name(_name), - friendlyName(_friendlyName), description(_description), - maker(_maker), units(_units), configurable(_configurable) { } - - QString type; // e.g. feature extraction plugin - QString category; // e.g. time > onsets - TransformId identifier; // e.g. vamp:vamp-aubio:aubioonset - QString name; // plugin's name if 1 output, else "name: output" - QString friendlyName; // short text for layer name - QString description; // sentence describing transform - QString maker; - QString units; - bool configurable; - - bool operator<(const TransformDesc &od) const { - return (name < od.name); - }; - }; - typedef std::vector<TransformDesc> TransformList; - - TransformList getAllTransforms(); - - std::vector<QString> getAllTransformTypes(); - - std::vector<QString> getTransformCategories(QString transformType); - std::vector<QString> getTransformMakers(QString transformType); - - /** - * Get a configuration XML string for the given transform (by - * asking the user, most likely). Returns the selected input - * model if the transform is acceptable, 0 if the operation should - * be cancelled. Audio callback play source may be used to - * audition effects plugins, if provided. - */ - Model *getConfigurationForTransform(TransformId identifier, - const std::vector<Model *> &candidateInputModels, - PluginTransform::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source = 0, - size_t startFrame = 0, - size_t duration = 0); - - /** - * Get the default execution context for the given transform - * and input model (if known). - */ - PluginTransform::ExecutionContext getDefaultContextForTransform(TransformId identifier, - Model *inputModel = 0); - - /** - * Return the output model resulting from applying the named - * transform to the given input model. The transform may still be - * working in the background when the model is returned; check the - * output model's isReady completion status for more details. - * - * If the transform is unknown or the input model is not an - * appropriate type for the given transform, or if some other - * problem occurs, return 0. - * - * The returned model is owned by the caller and must be deleted - * when no longer needed. - */ - Model *transform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml = ""); - - /** - * Full name of a transform, suitable for putting on a menu. - */ - QString getTransformName(TransformId identifier); - - /** - * Brief but friendly name of a transform, suitable for use - * as the name of the output layer. - */ - QString getTransformFriendlyName(TransformId identifier); - - QString getTransformUnits(TransformId identifier); - - /** - * Return true if the transform has any configurable parameters, - * i.e. if getConfigurationForTransform can ever return a non-trivial - * (not equivalent to empty) configuration string. - */ - bool isTransformConfigurable(TransformId identifier); - - /** - * If the transform has a prescribed number or range of channel - * inputs, return true and set minChannels and maxChannels to the - * minimum and maximum number of channel inputs the transform can - * accept. Return false if it doesn't care. - */ - bool getTransformChannelRange(TransformId identifier, - int &minChannels, int &maxChannels); - -protected slots: - void transformFinished(); - - void modelAboutToBeDeleted(Model *); - -protected: - Transform *createTransform(TransformId identifier, Model *inputModel, - const PluginTransform::ExecutionContext &context, - QString configurationXml); - - struct TransformIdent - { - TransformId identifier; - QString configurationXml; - }; - - typedef std::map<TransformId, QString> TransformConfigurationMap; - TransformConfigurationMap m_lastConfigurations; - - typedef std::map<TransformId, TransformDesc> TransformDescriptionMap; - TransformDescriptionMap m_transforms; - - typedef std::set<Transform *> TransformSet; - TransformSet m_runningTransforms; - - void populateTransforms(); - void populateFeatureExtractionPlugins(TransformDescriptionMap &); - void populateRealTimePlugins(TransformDescriptionMap &); - - bool getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, int &min, int &max); - - static TransformFactory *m_instance; -}; - - -#endif