# HG changeset patch # User Chris Cannam # Date 1154346277 0 # Node ID 3a13b0d4934e2dcb04beadba28076a9b01f5cfe4 # Parent f90fad823cea2a44628c8b45956adce91d8bbf6b * Reorganising code base. This revision will not compile. diff -r f90fad823cea -r 3a13b0d4934e base/Layer.cpp --- a/base/Layer.cpp Fri Jul 28 11:09:36 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +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 "Layer.h" -#include "View.h" -#include "Model.h" - -#include - -#include "layer/LayerFactory.h" //!!! shouldn't be including this here -- does that suggest we need to move this into layer/ ? -#include "PlayParameterRepository.h" - -Layer::Layer() -{ -} - -Layer::~Layer() -{ -// std::cerr << "Layer::~Layer(" << this << ")" << std::endl; -} - -QString -Layer::getPropertyContainerIconName() const -{ - return LayerFactory::getInstance()->getLayerIconName - (LayerFactory::getInstance()->getLayerType(this)); -} - -QString -Layer::getLayerPresentationName() const -{ - QString layerName = objectName(); - QString modelName; - if (getModel()) modelName = getModel()->objectName(); - - QString text; - if (modelName != "") { - text = QString("%1: %2").arg(modelName).arg(layerName); - } else { - text = layerName; - } - - return text; -} - -void -Layer::setObjectName(const QString &name) -{ - QObject::setObjectName(name); - emit layerNameChanged(); -} - -QString -Layer::toXmlString(QString indent, QString extraAttributes) const -{ - QString s; - - s += indent; - - s += QString("\n") - .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName - (LayerFactory::getInstance()->getLayerType(this)))) - .arg(getObjectExportId(this)) - .arg(encodeEntities(objectName())) - .arg(getObjectExportId(getModel())) - .arg(extraAttributes); - - return s; -} - -PlayParameters * -Layer::getPlayParameters() -{ -// std::cerr << "Layer (" << this << ", " << objectName().toStdString() << ")::getPlayParameters: model is "<< getModel() << std::endl; - const Model *model = getModel(); - if (model) { - return PlayParameterRepository::getInstance()->getPlayParameters(model); - } - return 0; -} - -void -Layer::showLayer(View *view, bool show) -{ - setLayerDormant(view, !show); - emit layerParametersChanged(); -} - - -#ifdef INCLUDE_MOCFILES -#include "Layer.moc.cpp" -#endif - diff -r f90fad823cea -r 3a13b0d4934e base/Layer.h --- a/base/Layer.h Fri Jul 28 11:09:36 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,301 +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 _LAYER_H_ -#define _LAYER_H_ - -#include "PropertyContainer.h" -#include "XmlExportable.h" -#include "Selection.h" - -#include -#include -#include - -#include - -class ZoomConstraint; -class Model; -class QPainter; -class View; -class QMouseEvent; -class Clipboard; - -/** - * The base class for visual representations of the data found in a - * Model. Layers are expected to be able to draw themselves onto a - * View, and may also be editable. - */ - -class Layer : public PropertyContainer, - public XmlExportable -{ - Q_OBJECT - -public: - Layer(); - virtual ~Layer(); - - virtual const Model *getModel() const = 0; - virtual Model *getModel() { - return const_cast(const_cast(this)->getModel()); - } - - virtual const ZoomConstraint *getZoomConstraint() const { return 0; } - virtual void paint(View *, QPainter &, QRect) const = 0; - - enum VerticalPosition { - PositionTop, PositionMiddle, PositionBottom - }; - virtual VerticalPosition getPreferredTimeRulerPosition() const { - return PositionMiddle; - } - virtual VerticalPosition getPreferredFrameCountPosition() const { - return PositionBottom; - } - - virtual QString getPropertyContainerIconName() const; - - virtual QString getPropertyContainerName() const { - return objectName(); - } - - virtual QString getLayerPresentationName() const; - - virtual int getVerticalScaleWidth(View *, QPainter &) const { return 0; } - virtual void paintVerticalScale(View *, QPainter &, QRect) const { } - - virtual bool getCrosshairExtents(View *, QPainter &, QPoint /* cursorPos */, - std::vector &) const { - return false; - } - virtual void paintCrosshairs(View *, QPainter &, QPoint) const { } - - virtual QString getFeatureDescription(View *, QPoint &) const { - return ""; - } - - enum SnapType { - SnapLeft, - SnapRight, - SnapNearest, - SnapNeighbouring - }; - - /** - * Adjust the given frame to snap to the nearest feature, if - * possible. - * - * If snap is SnapLeft or SnapRight, adjust the frame to match - * that of the nearest feature in the given direction regardless - * of how far away it is. If snap is SnapNearest, adjust the - * frame to that of the nearest feature in either direction. If - * snap is SnapNeighbouring, adjust the frame to that of the - * nearest feature if it is close, and leave it alone (returning - * false) otherwise. SnapNeighbouring should always choose the - * same feature that would be used in an editing operation through - * calls to editStart etc. - * - * Return true if a suitable feature was found and frame adjusted - * accordingly. Return false if no suitable feature was - * available. Also return the resolution of the model in this - * layer in sample frames. - */ - virtual bool snapToFeatureFrame(View * /* v */, - int & /* frame */, - size_t &resolution, - SnapType /* snap */) const { - resolution = 1; - return false; - } - - // Draw and edit modes: - // - // Layer needs to get actual mouse events, I guess. Draw mode is - // probably the easier. - - virtual void drawStart(View *, QMouseEvent *) { } - virtual void drawDrag(View *, QMouseEvent *) { } - virtual void drawEnd(View *, QMouseEvent *) { } - - virtual void editStart(View *, QMouseEvent *) { } - virtual void editDrag(View *, QMouseEvent *) { } - virtual void editEnd(View *, QMouseEvent *) { } - - virtual void editOpen(View *, QMouseEvent *) { } // on double-click - - virtual void moveSelection(Selection, size_t /* newStartFrame */) { } - virtual void resizeSelection(Selection, Selection /* newSize */) { } - virtual void deleteSelection(Selection) { } - - virtual void copy(Selection, Clipboard & /* to */) { } - - /** - * Paste from the given clipboard onto the layer at the given - * frame offset. If interactive is true, the layer may ask the - * user about paste options through a dialog if desired, and may - * return false if the user cancelled the paste operation. This - * function should return true if a paste actually occurred. - */ - virtual bool paste(const Clipboard & /* from */, - int /* frameOffset */, - bool /* interactive */) { return false; } - - // Text mode: - // - // Label nearest feature. We need to get the feature coordinates - // and current label from the layer, and then the pane can pop up - // a little text entry dialog at the right location. Or we edit - // in place? Probably the dialog is easier. - - /** - * This should return true if the layer can safely be scrolled - * automatically by a given view (simply copying the existing data - * and then refreshing the exposed area) without altering its - * meaning. For the view widget as a whole this is usually not - * possible because of invariant (non-scrolling) material - * displayed over the top, but the widget may be able to optimise - * scrolling better if it is known that individual views can be - * scrolled safely in this way. - */ - virtual bool isLayerScrollable(const View *) const { return true; } - - /** - * This should return true if the layer completely obscures any - * underlying layers. It's used to determine whether the view can - * safely draw any selection rectangles under the layer instead of - * over it, in the case where the layer is not scrollable and - * therefore needs to be redrawn each time (so that the selection - * rectangle can be cached). - */ - virtual bool isLayerOpaque() const { return false; } - - /** - * This should return true if the layer can be edited by the user. - * If this is the case, the appropriate edit tools may be made - * available by the application and the layer's drawStart/Drag/End - * and editStart/Drag/End methods should be implemented. - */ - virtual bool isLayerEditable() const { return false; } - - /** - * Return the proportion of background work complete in drawing - * this view, as a percentage -- in most cases this will be the - * value returned by pointer from a call to the underlying model's - * isReady(int *) call. The widget may choose to show a progress - * meter if it finds that this returns < 100 at any given moment. - */ - virtual int getCompletion(View *) const { return 100; } - - virtual void setObjectName(const QString &name); - - /** - * Convert the layer's data (though not those of the model it - * refers to) into an XML string for file output. This class - * implements the basic name/type/model-id output; subclasses will - * typically call this superclass implementation with extra - * attributes describing their particular properties. - */ - virtual QString toXmlString(QString indent = "", - QString extraAttributes = "") const; - - /** - * Set the particular properties of a layer (those specific to the - * subclass) from a set of XML attributes. This is the effective - * inverse of the toXmlString method. - */ - virtual void setProperties(const QXmlAttributes &) = 0; - - /** - * Indicate that a layer is not currently visible in the given - * view and is not expected to become visible in the near future - * (for example because the user has explicitly removed or hidden - * it). The layer may respond by (for example) freeing any cache - * memory it is using, until next time its paint method is called, - * when it should set itself un-dormant again. - */ - virtual void setLayerDormant(const View *v, bool dormant) { - m_dormancy[v] = dormant; - } - - /** - * Return whether the layer is dormant (i.e. hidden) in the given - * view. - */ - virtual bool isLayerDormant(const View *v) const { - if (m_dormancy.find(v) == m_dormancy.end()) return false; - return m_dormancy.find(v)->second; - } - - virtual PlayParameters *getPlayParameters(); - - virtual bool needsTextLabelHeight() const { return false; } - - /** - * Return the minimum and maximum values for the y axis of the - * model in this layer, as well as whether the layer is configured - * to use a logarithmic y axis display. Also return the unit for - * these values if known. - * - * This function returns the "normal" extents for the layer, not - * necessarily the extents actually in use in the display. - */ - virtual bool getValueExtents(float &min, float &max, - bool &logarithmic, QString &unit) const = 0; - - /** - * Return the minimum and maximum values within the displayed - * range for the y axis, if only a subset of the whole range of - * the model (returned by getValueExtents) is being displayed. - * Return false if the layer is not imposing a particular display - * extent (using the normal layer extents or deferring to whatever - * is in use for the same units elsewhere in the view). - */ - virtual bool getDisplayExtents(float & /* min */, - float & /* max */) const { - return false; - } - - /** - * Set the displayed minimum and maximum values for the y axis to - * the given range, if supported. Return false if not supported - * on this layer (and set nothing). In most cases, layers that - * return false for getDisplayExtents should also return false for - * this function. - */ - virtual bool setDisplayExtents(float /* min */, - float /* max */) { - return false; - } - -public slots: - void showLayer(View *, bool show); - -signals: - void modelChanged(); - void modelCompletionChanged(); - void modelChanged(size_t startFrame, size_t endFrame); - void modelReplaced(); - - void layerParametersChanged(); - void layerNameChanged(); - -protected: - mutable std::map m_dormancy; -}; - -#endif - diff -r f90fad823cea -r 3a13b0d4934e base/View.cpp --- a/base/View.cpp Fri Jul 28 11:09:36 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1524 +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 "base/View.h" -#include "base/Layer.h" -#include "base/Model.h" -#include "base/ZoomConstraint.h" -#include "base/Profiler.h" - -#include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here -#include "model/PowerOfSqrtTwoZoomConstraint.h" //!!! likewise - -#include -#include -#include -#include - -#include -#include -#include - -//#define DEBUG_VIEW_WIDGET_PAINT 1 - -using std::cerr; -using std::endl; - -View::View(QWidget *w, bool showProgress) : - QFrame(w), - m_centreFrame(0), - m_zoomLevel(1024), - m_followPan(true), - m_followZoom(true), - m_followPlay(PlaybackScrollPage), - m_lightBackground(true), - m_showProgress(showProgress), - m_cache(0), - m_cacheCentreFrame(0), - m_cacheZoomLevel(1024), - m_selectionCached(false), - m_deleting(false), - m_haveSelectedLayer(false), - m_manager(0), - m_propertyContainer(new ViewPropertyContainer(this)) -{ -// QWidget::setAttribute(Qt::WA_PaintOnScreen); -} - -View::~View() -{ -// std::cerr << "View::~View(" << this << ")" << std::endl; - - m_deleting = true; - delete m_propertyContainer; -} - -PropertyContainer::PropertyList -View::getProperties() const -{ - PropertyContainer::PropertyList list; - list.push_back("Global Scroll"); - list.push_back("Global Zoom"); - list.push_back("Follow Playback"); - return list; -} - -QString -View::getPropertyLabel(const PropertyName &pn) const -{ - if (pn == "Global Scroll") return tr("Global Scroll"); - if (pn == "Global Zoom") return tr("Global Zoom"); - if (pn == "Follow Playback") return tr("Follow Playback"); - return ""; -} - -PropertyContainer::PropertyType -View::getPropertyType(const PropertyContainer::PropertyName &name) const -{ - if (name == "Global Scroll") return PropertyContainer::ToggleProperty; - if (name == "Global Zoom") return PropertyContainer::ToggleProperty; - if (name == "Follow Playback") return PropertyContainer::ValueProperty; - return PropertyContainer::InvalidProperty; -} - -int -View::getPropertyRangeAndValue(const PropertyContainer::PropertyName &name, - int *min, int *max) const -{ - if (name == "Global Scroll") return m_followPan; - if (name == "Global Zoom") return m_followZoom; - if (name == "Follow Playback") { - if (min) *min = 0; - if (max) *max = 2; - return int(m_followPlay); - } - if (min) *min = 0; - if (max) *max = 0; - return 0; -} - -QString -View::getPropertyValueLabel(const PropertyContainer::PropertyName &name, - int value) const -{ - if (name == "Follow Playback") { - switch (value) { - default: - case 0: return tr("Scroll"); - case 1: return tr("Page"); - case 2: return tr("Off"); - } - } - return tr(""); -} - -void -View::setProperty(const PropertyContainer::PropertyName &name, int value) -{ - if (name == "Global Scroll") { - setFollowGlobalPan(value != 0); - } else if (name == "Global Zoom") { - setFollowGlobalZoom(value != 0); - } else if (name == "Follow Playback") { - switch (value) { - default: - case 0: setPlaybackFollow(PlaybackScrollContinuous); break; - case 1: setPlaybackFollow(PlaybackScrollPage); break; - case 2: setPlaybackFollow(PlaybackIgnore); break; - } - } -} - -size_t -View::getPropertyContainerCount() const -{ - return m_layers.size() + 1; // the 1 is for me -} - -const PropertyContainer * -View::getPropertyContainer(size_t i) const -{ - return (const PropertyContainer *)(((View *)this)-> - getPropertyContainer(i)); -} - -PropertyContainer * -View::getPropertyContainer(size_t i) -{ - if (i == 0) return m_propertyContainer; - return m_layers[i-1]; -} - -bool -View::getValueExtents(QString unit, float &min, float &max, bool &log) const -{ - bool have = false; - - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { - - QString layerUnit; - float layerMin = 0.0, layerMax = 0.0; - float displayMin = 0.0, displayMax = 0.0; - bool layerLog = false; - - if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) && - layerUnit.toLower() == unit.toLower()) { - - if ((*i)->getDisplayExtents(displayMin, displayMax)) { - - min = displayMin; - max = displayMax; - log = layerLog; - have = true; - break; - - } else { - - if (!have || layerMin < min) min = layerMin; - if (!have || layerMax > max) max = layerMax; - if (layerLog) log = true; - have = true; - } - } - } - - return have; -} - -int -View::getTextLabelHeight(const Layer *layer, QPainter &paint) const -{ - std::map sortedLayers; - - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { - if ((*i)->needsTextLabelHeight()) { - sortedLayers[getObjectExportId(*i)] = *i; - } - } - - int y = 15 + paint.fontMetrics().ascent(); - - for (std::map::const_iterator i = sortedLayers.begin(); - i != sortedLayers.end(); ++i) { - if (i->second == layer) return y; - y += paint.fontMetrics().height(); - } - - return y; -} - -void -View::propertyContainerSelected(View *client, PropertyContainer *pc) -{ - if (client != this) return; - - if (pc == m_propertyContainer) { - if (m_haveSelectedLayer) { - m_haveSelectedLayer = false; - update(); - } - return; - } - - delete m_cache; - m_cache = 0; - - Layer *selectedLayer = 0; - - for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if (*i == pc) { - selectedLayer = *i; - m_layers.erase(i); - break; - } - } - - if (selectedLayer) { - m_haveSelectedLayer = true; - m_layers.push_back(selectedLayer); - update(); - } else { - m_haveSelectedLayer = false; - } -} - -void -View::toolModeChanged() -{ -// std::cerr << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << std::endl; -} - -long -View::getStartFrame() const -{ - size_t w2 = (width() / 2) * m_zoomLevel; - size_t frame = m_centreFrame; - if (frame >= w2) { - frame -= w2; - return (frame / m_zoomLevel * m_zoomLevel); - } else { - frame = w2 - frame; - frame = frame / m_zoomLevel * m_zoomLevel; - return -(long)frame - m_zoomLevel; - } -} - -size_t -View::getEndFrame() const -{ - return getFrameForX(width()) - 1; -} - -void -View::setStartFrame(long f) -{ - setCentreFrame(f + m_zoomLevel * (width() / 2)); -} - -bool -View::setCentreFrame(size_t f, bool e) -{ - bool changeVisible = false; - - if (m_centreFrame != f) { - - int formerPixel = m_centreFrame / m_zoomLevel; - - m_centreFrame = f; - - int newPixel = m_centreFrame / m_zoomLevel; - - if (newPixel != formerPixel) { - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl; -#endif - update(); - - changeVisible = true; - } - - if (e) emit centreFrameChanged(this, f, m_followPan); - } - - return changeVisible; -} - -int -View::getXForFrame(long frame) const -{ - return (frame - getStartFrame()) / m_zoomLevel; -} - -long -View::getFrameForX(int x) const -{ - return (long(x) * long(m_zoomLevel)) + getStartFrame(); -} - -float -View::getYForFrequency(float frequency, - float minf, - float maxf, - bool logarithmic) const -{ - int h = height(); - - if (logarithmic) { - - static float lastminf = 0.0, lastmaxf = 0.0; - static float logminf = 0.0, logmaxf = 0.0; - - if (lastminf != minf) { - lastminf = (minf == 0.0 ? 1.0 : minf); - logminf = log10f(minf); - } - if (lastmaxf != maxf) { - lastmaxf = (maxf < lastminf ? lastminf : maxf); - logmaxf = log10f(maxf); - } - - if (logminf == logmaxf) return 0; - return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf); - - } else { - - if (minf == maxf) return 0; - return h - (h * (frequency - minf)) / (maxf - minf); - } -} - -float -View::getFrequencyForY(int y, - float minf, - float maxf, - bool logarithmic) const -{ - int h = height(); - - if (logarithmic) { - - static float lastminf = 0.0, lastmaxf = 0.0; - static float logminf = 0.0, logmaxf = 0.0; - - if (lastminf != minf) { - lastminf = (minf == 0.0 ? 1.0 : minf); - logminf = log10f(minf); - } - if (lastmaxf != maxf) { - lastmaxf = (maxf < lastminf ? lastminf : maxf); - logmaxf = log10f(maxf); - } - - if (logminf == logmaxf) return 0; - return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h); - - } else { - - if (minf == maxf) return 0; - return minf + ((h - y) * (maxf - minf)) / h; - } -} - -int -View::getZoomLevel() const -{ -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cout << "zoom level: " << m_zoomLevel << std::endl; -#endif - return m_zoomLevel; -} - -void -View::setZoomLevel(size_t z) -{ - if (m_zoomLevel != int(z)) { - m_zoomLevel = z; - emit zoomLevelChanged(this, z, m_followZoom); - update(); - } -} - -View::LayerProgressBar::LayerProgressBar(QWidget *parent) : - QProgressBar(parent) -{ - QFont f(font()); - f.setPointSize(f.pointSize() * 8 / 10); - setFont(f); -} - -void -View::addLayer(Layer *layer) -{ - delete m_cache; - m_cache = 0; - - m_layers.push_back(layer); - - m_progressBars[layer] = new LayerProgressBar(this); - m_progressBars[layer]->setMinimum(0); - m_progressBars[layer]->setMaximum(100); - m_progressBars[layer]->setMinimumWidth(80); - m_progressBars[layer]->hide(); - - connect(layer, SIGNAL(layerParametersChanged()), - this, SLOT(layerParametersChanged())); - connect(layer, SIGNAL(layerNameChanged()), - this, SLOT(layerNameChanged())); - connect(layer, SIGNAL(modelChanged()), - this, SLOT(modelChanged())); - connect(layer, SIGNAL(modelCompletionChanged()), - this, SLOT(modelCompletionChanged())); - connect(layer, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(modelChanged(size_t, size_t))); - connect(layer, SIGNAL(modelReplaced()), - this, SLOT(modelReplaced())); - - update(); - - emit propertyContainerAdded(layer); -} - -void -View::removeLayer(Layer *layer) -{ - if (m_deleting) { - return; - } - - delete m_cache; - m_cache = 0; - - for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if (*i == layer) { - m_layers.erase(i); - if (m_progressBars.find(layer) != m_progressBars.end()) { - delete m_progressBars[layer]; - m_progressBars.erase(layer); - } - break; - } - } - - update(); - - emit propertyContainerRemoved(layer); -} - -Layer * -View::getSelectedLayer() -{ - if (m_haveSelectedLayer && !m_layers.empty()) { - return getLayer(getLayerCount() - 1); - } else { - return 0; - } -} - -const Layer * -View::getSelectedLayer() const -{ - return const_cast(const_cast(this)->getSelectedLayer()); -} - -void -View::setViewManager(ViewManager *manager) -{ - if (m_manager) { - m_manager->disconnect(this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool))); - m_manager->disconnect(this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool))); - disconnect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool))); - disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool))); - disconnect(m_manager, SIGNAL(toolModeChanged())); - disconnect(m_manager, SIGNAL(selectionChanged())); - disconnect(m_manager, SIGNAL(inProgressSelectionChanged())); - } - - m_manager = manager; - if (m_followPan) setCentreFrame(m_manager->getGlobalCentreFrame(), false); - if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom()); - - connect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)), - this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool))); - connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)), - this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); - connect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), - this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool))); - connect(m_manager, SIGNAL(toolModeChanged()), - this, SLOT(toolModeChanged())); - connect(m_manager, SIGNAL(selectionChanged()), - this, SLOT(selectionChanged())); - connect(m_manager, SIGNAL(inProgressSelectionChanged()), - this, SLOT(selectionChanged())); - connect(m_manager, SIGNAL(overlayModeChanged()), - this, SLOT(update())); - - connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)), - m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool))); - connect(this, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), - m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool))); - - toolModeChanged(); -} - -void -View::setFollowGlobalPan(bool f) -{ - m_followPan = f; - emit propertyContainerPropertyChanged(m_propertyContainer); -} - -void -View::setFollowGlobalZoom(bool f) -{ - m_followZoom = f; - emit propertyContainerPropertyChanged(m_propertyContainer); -} - -void -View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style) -{ - if (style == OutlinedText) { - - QColor origPenColour = paint.pen().color(); - QColor penColour = origPenColour; - QColor surroundColour = Qt::white; //palette().background().color(); - - if (!hasLightBackground()) { - int h, s, v; - penColour.getHsv(&h, &s, &v); - penColour = QColor::fromHsv(h, s, 255 - v); - surroundColour = Qt::black; - } - - paint.setPen(surroundColour); - - for (int dx = -1; dx <= 1; ++dx) { - for (int dy = -1; dy <= 1; ++dy) { - if (!(dx || dy)) continue; - paint.drawText(x + dx, y + dy, text); - } - } - - paint.setPen(penColour); - - paint.drawText(x, y, text); - - paint.setPen(origPenColour); - - } else { - - std::cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << std::endl; - } -} - -void -View::setPlaybackFollow(PlaybackFollowMode m) -{ - m_followPlay = m; - emit propertyContainerPropertyChanged(m_propertyContainer); -} - -void -View::modelChanged() -{ - QObject *obj = sender(); - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::modelChanged()" << std::endl; -#endif - - // If the model that has changed is not used by any of the cached - // layers, we won't need to recreate the cache - - bool recreate = false; - - bool discard; - LayerList scrollables = getScrollableBackLayers(false, discard); - for (LayerList::const_iterator i = scrollables.begin(); - i != scrollables.end(); ++i) { - if (*i == obj || (*i)->getModel() == obj) { - recreate = true; - break; - } - } - - if (recreate) { - delete m_cache; - m_cache = 0; - } - - checkProgress(obj); - - update(); -} - -void -View::modelChanged(size_t startFrame, size_t endFrame) -{ - QObject *obj = sender(); - - long myStartFrame = getStartFrame(); - size_t myEndFrame = getEndFrame(); - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << std::endl; -#endif - - if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) { - checkProgress(obj); - return; - } - if (startFrame > myEndFrame) { - checkProgress(obj); - return; - } - - // If the model that has changed is not used by any of the cached - // layers, we won't need to recreate the cache - - bool recreate = false; - - bool discard; - LayerList scrollables = getScrollableBackLayers(false, discard); - for (LayerList::const_iterator i = scrollables.begin(); - i != scrollables.end(); ++i) { - if (*i == obj || (*i)->getModel() == obj) { - recreate = true; - break; - } - } - - if (recreate) { - delete m_cache; - m_cache = 0; - } - - if (long(startFrame) < myStartFrame) startFrame = myStartFrame; - if (endFrame > myEndFrame) endFrame = myEndFrame; - - int x0 = getXForFrame(startFrame); - int x1 = getXForFrame(endFrame + 1); - if (x1 < x0) x1 = x0; - - checkProgress(obj); - - update(); -//!!! update(x0, 0, x1 - x0 + 1, height()); -} - -void -View::modelCompletionChanged() -{ - QObject *obj = sender(); - checkProgress(obj); -} - -void -View::modelReplaced() -{ -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::modelReplaced()" << std::endl; -#endif - delete m_cache; - m_cache = 0; - - update(); -} - -void -View::layerParametersChanged() -{ - Layer *layer = dynamic_cast(sender()); - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View::layerParametersChanged()" << std::endl; -#endif - - delete m_cache; - m_cache = 0; - update(); - - if (layer) { - emit propertyContainerPropertyChanged(layer); - } -} - -void -View::layerNameChanged() -{ - Layer *layer = dynamic_cast(sender()); - if (layer) emit propertyContainerNameChanged(layer); -} - -void -View::viewManagerCentreFrameChanged(void *p, unsigned long f, bool locked) -{ - if (m_followPan && p != this && locked) { - if (m_manager && (sender() == m_manager)) { -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << this << ": manager frame changed " << f << " from " << p << std::endl; -#endif - setCentreFrame(f); - if (p == this) repaint(); - } - } -} - -void -View::viewManagerPlaybackFrameChanged(unsigned long f) -{ - if (m_manager) { - if (sender() != m_manager) return; - } - - if (m_playPointerFrame == f) return; - bool visible = (getXForFrame(m_playPointerFrame) != getXForFrame(f)); - size_t oldPlayPointerFrame = m_playPointerFrame; - m_playPointerFrame = f; - if (!visible) return; - - switch (m_followPlay) { - - case PlaybackScrollContinuous: - if (QApplication::mouseButtons() == Qt::NoButton) { - setCentreFrame(f, false); - } - break; - - case PlaybackScrollPage: - { - int xold = getXForFrame(oldPlayPointerFrame); - update(xold - 1, 0, 3, height()); - - long w = getEndFrame() - getStartFrame(); - w -= w/5; - long sf = (f / w) * w - w/8; - - if (m_manager && - m_manager->isPlaying() && - m_manager->getPlaySelectionMode()) { - MultiSelection::SelectionList selections = m_manager->getSelections(); - if (!selections.empty()) { - size_t selectionStart = selections.begin()->getStartFrame(); - if (sf < long(selectionStart) - w / 10) { - sf = long(selectionStart) - w / 10; - } - } - } - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame " - << getStartFrame() << std::endl; -#endif - - // We don't consider scrolling unless the pointer is outside - // the clearly visible range already - - int xnew = getXForFrame(m_playPointerFrame); - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "xnew = " << xnew << ", width = " << width() << std::endl; -#endif - - if (xnew < width()/8 || xnew > (width()*7)/8) { - if (QApplication::mouseButtons() == Qt::NoButton) { - long offset = getFrameForX(width()/2) - getStartFrame(); - long newCentre = sf + offset; - bool changed = setCentreFrame(newCentre, false); - if (changed) { - xold = getXForFrame(oldPlayPointerFrame); - update(xold - 1, 0, 3, height()); - } - } - } - - update(xnew - 1, 0, 3, height()); - - break; - } - - case PlaybackIgnore: - if (long(f) >= getStartFrame() && f < getEndFrame()) { - update(); - } - break; - } -} - -void -View::viewManagerZoomLevelChanged(void *p, unsigned long z, bool locked) -{ - if (m_followZoom && p != this && locked) { - if (m_manager && (sender() == m_manager)) { - setZoomLevel(z); - if (p == this) repaint(); - } - } -} - -void -View::selectionChanged() -{ - if (m_selectionCached) { - delete m_cache; - m_cache = 0; - m_selectionCached = false; - } - update(); -} - -size_t -View::getModelsStartFrame() const -{ - bool first = true; - size_t startFrame = 0; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - - if ((*i)->getModel() && (*i)->getModel()->isOK()) { - - size_t thisStartFrame = (*i)->getModel()->getStartFrame(); - - if (first || thisStartFrame < startFrame) { - startFrame = thisStartFrame; - } - first = false; - } - } - return startFrame; -} - -size_t -View::getModelsEndFrame() const -{ - bool first = true; - size_t endFrame = 0; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - - if ((*i)->getModel() && (*i)->getModel()->isOK()) { - - size_t thisEndFrame = (*i)->getModel()->getEndFrame(); - - if (first || thisEndFrame > endFrame) { - endFrame = thisEndFrame; - } - first = false; - } - } - - if (first) return getModelsStartFrame(); - return endFrame; -} - -int -View::getModelsSampleRate() const -{ - //!!! Just go for the first, for now. If we were supporting - // multiple samplerates, we'd probably want to do frame/time - // conversion in the model - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if ((*i)->getModel() && (*i)->getModel()->isOK()) { - return (*i)->getModel()->getSampleRate(); - } - } - return 0; -} - -bool -View::areLayersScrollable() const -{ - // True iff all views are scrollable - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if (!(*i)->isLayerScrollable(this)) return false; - } - return true; -} - -View::LayerList -View::getScrollableBackLayers(bool testChanged, bool &changed) const -{ - changed = false; - - // We want a list of all the scrollable layers that are behind the - // backmost non-scrollable layer. - - LayerList scrollables; - bool metUnscrollable = false; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if ((*i)->isLayerDormant(this)) continue; - if ((*i)->isLayerOpaque()) { - // You can't see anything behind an opaque layer! - scrollables.clear(); - if (metUnscrollable) break; - } - if (!metUnscrollable && (*i)->isLayerScrollable(this)) { - scrollables.push_back(*i); - } else { - metUnscrollable = true; - } - } - - if (testChanged && scrollables != m_lastScrollableBackLayers) { - m_lastScrollableBackLayers = scrollables; - changed = true; - } - return scrollables; -} - -View::LayerList -View::getNonScrollableFrontLayers(bool testChanged, bool &changed) const -{ - changed = false; - LayerList nonScrollables; - - // Everything in front of the first non-scrollable from the back - // should also be considered non-scrollable - - bool started = false; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - if ((*i)->isLayerDormant(this)) continue; - if (!started && (*i)->isLayerScrollable(this)) { - continue; - } - started = true; - if ((*i)->isLayerOpaque()) { - // You can't see anything behind an opaque layer! - nonScrollables.clear(); - } - nonScrollables.push_back(*i); - } - - if (testChanged && nonScrollables != m_lastNonScrollableBackLayers) { - m_lastNonScrollableBackLayers = nonScrollables; - changed = true; - } - - return nonScrollables; -} - -size_t -View::getZoomConstraintBlockSize(size_t blockSize, - ZoomConstraint::RoundingDirection dir) - const -{ - size_t candidate = blockSize; - bool haveCandidate = false; - - PowerOfSqrtTwoZoomConstraint defaultZoomConstraint; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { - - const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); - if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; - - size_t thisBlockSize = - zoomConstraint->getNearestBlockSize(blockSize, dir); - - // Go for the block size that's furthest from the one - // passed in. Most of the time, that's what we want. - if (!haveCandidate || - (thisBlockSize > blockSize && thisBlockSize > candidate) || - (thisBlockSize < blockSize && thisBlockSize < candidate)) { - candidate = thisBlockSize; - haveCandidate = true; - } - } - - return candidate; -} - -void -View::zoom(bool in) -{ - int newZoomLevel = m_zoomLevel; - - if (in) { - newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, - ZoomConstraint::RoundDown); - } else { - newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, - ZoomConstraint::RoundUp); - } - - if (newZoomLevel != m_zoomLevel) { - setZoomLevel(newZoomLevel); - } -} - -void -View::scroll(bool right, bool lots) -{ - long delta; - if (lots) { - delta = (getEndFrame() - getStartFrame()) / 2; - } else { - delta = (getEndFrame() - getStartFrame()) / 20; - } - if (right) delta = -delta; - - if (int(m_centreFrame) < delta) { - setCentreFrame(0); - } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { - setCentreFrame(getModelsEndFrame()); - } else { - setCentreFrame(m_centreFrame - delta); - } -} - -void -View::checkProgress(void *object) -{ - if (!m_showProgress) return; - - int ph = height(); - - for (ProgressMap::const_iterator i = m_progressBars.begin(); - i != m_progressBars.end(); ++i) { - - if (i->first == object) { - - int completion = i->first->getCompletion(this); - - if (completion >= 100) { - - i->second->hide(); - - } else { - - i->second->setText(i->first->getPropertyContainerName()); - i->second->setValue(completion); - i->second->move(0, ph - i->second->height()); - - i->second->show(); - i->second->update(); - - ph -= i->second->height(); - } - } else { - if (i->second->isVisible()) { - ph -= i->second->height(); - } - } - } -} - -void -View::paintEvent(QPaintEvent *e) -{ -// Profiler prof("View::paintEvent", false); -// std::cerr << "View::paintEvent" << std::endl; - - if (m_layers.empty()) { - QFrame::paintEvent(e); - return; - } - - // ensure our constraints are met - m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel, - ZoomConstraint::RoundUp); - - QPainter paint; - bool repaintCache = false; - bool paintedCacheRect = false; - - QRect cacheRect(rect()); - - if (e) { - cacheRect &= e->rect(); -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() - << ", my rect " << width() << "x" << height() << std::endl; -#endif - } - - QRect nonCacheRect(cacheRect); - - // If not all layers are scrollable, but some of the back layers - // are, we should store only those in the cache. - - bool layersChanged = false; - LayerList scrollables = getScrollableBackLayers(true, layersChanged); - LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); - bool selectionCacheable = nonScrollables.empty(); - bool haveSelections = m_manager && !m_manager->getSelections().empty(); - bool selectionDrawn = false; - - // If all the non-scrollable layers are non-opaque, then we draw - // the selection rectangle behind them and cache it. If any are - // opaque, however, we can't cache. - // - if (!selectionCacheable) { - selectionCacheable = true; - for (LayerList::const_iterator i = nonScrollables.begin(); - i != nonScrollables.end(); ++i) { - if ((*i)->isLayerOpaque()) { - selectionCacheable = false; - break; - } - } - } - - if (selectionCacheable) { - QPoint localPos; - bool closeToLeft, closeToRight; - if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { - selectionCacheable = false; - } - } - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() - << " scrollable back layers and " << nonScrollables.size() - << " non-scrollable front layers" << std::endl; - std::cerr << "haveSelections " << haveSelections << ", selectionCacheable " - << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl; -#endif - - if (layersChanged || scrollables.empty() || - (haveSelections && (selectionCacheable != m_selectionCached))) { - delete m_cache; - m_cache = 0; - m_selectionCached = false; - } - - if (!scrollables.empty()) { - if (!m_cache || - m_cacheZoomLevel != m_zoomLevel || - width() != m_cache->width() || - height() != m_cache->height()) { - - // cache is not valid - - if (cacheRect.width() < width()/10) { -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl; -#endif - } else { - delete m_cache; - m_cache = new QPixmap(width(), height()); -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl; -#endif - cacheRect = rect(); - repaintCache = true; - } - - } else if (m_cacheCentreFrame != m_centreFrame) { - - long dx = - getXForFrame(m_cacheCentreFrame) - - getXForFrame(m_centreFrame); - - if (dx > -width() && dx < width()) { -#if defined(Q_WS_WIN32) || defined(Q_WS_MAC) - // Copying a pixmap to itself doesn't work properly on Windows - // or Mac (it only works when moving in one direction) - static QPixmap *tmpPixmap = 0; - if (!tmpPixmap || - tmpPixmap->width() != width() || - tmpPixmap->height() != height()) { - delete tmpPixmap; - tmpPixmap = new QPixmap(width(), height()); - } - paint.begin(tmpPixmap); - paint.drawPixmap(0, 0, *m_cache); - paint.end(); - paint.begin(m_cache); - paint.drawPixmap(dx, 0, *tmpPixmap); - paint.end(); -#else - // But it seems to be fine on X11 - paint.begin(m_cache); - paint.drawPixmap(dx, 0, *m_cache); - paint.end(); -#endif - - if (dx < 0) { - cacheRect = QRect(width() + dx, 0, -dx, height()); - } else { - cacheRect = QRect(0, 0, dx, height()); - } -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl; -#endif - } else { - cacheRect = rect(); -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl; -#endif - } - repaintCache = true; - - } else { -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl; -#endif - paint.begin(this); - paint.drawPixmap(cacheRect, *m_cache, cacheRect); - paint.end(); - QFrame::paintEvent(e); - paintedCacheRect = true; - } - - m_cacheCentreFrame = m_centreFrame; - m_cacheZoomLevel = m_zoomLevel; - } - -#ifdef DEBUG_VIEW_WIDGET_PAINT -// std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl; -#endif - - // Scrollable (cacheable) items first - - if (!paintedCacheRect) { - - if (repaintCache) paint.begin(m_cache); - else paint.begin(this); - - paint.setClipRect(cacheRect); - - if (hasLightBackground()) { - paint.setPen(Qt::white); - paint.setBrush(Qt::white); - } else { - paint.setPen(Qt::black); - paint.setBrush(Qt::black); - } - paint.drawRect(cacheRect); - - paint.setPen(Qt::black); - paint.setBrush(Qt::NoBrush); - - for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { - paint.setRenderHint(QPainter::Antialiasing, false); - paint.save(); - (*i)->paint(this, paint, cacheRect); - paint.restore(); - } - - if (haveSelections && selectionCacheable) { - drawSelections(paint); - m_selectionCached = repaintCache; - selectionDrawn = true; - } - - paint.end(); - - if (repaintCache) { - cacheRect |= (e ? e->rect() : rect()); - paint.begin(this); - paint.drawPixmap(cacheRect, *m_cache, cacheRect); - paint.end(); - } - } - - // Now non-cacheable items. We always need to redraw the - // non-cacheable items across at least the area we drew of the - // cacheable items. - - nonCacheRect |= cacheRect; - - paint.begin(this); - paint.setClipRect(nonCacheRect); - - if (scrollables.empty()) { - if (hasLightBackground()) { - paint.setPen(Qt::white); - paint.setBrush(Qt::white); - } else { - paint.setPen(Qt::black); - paint.setBrush(Qt::black); - } - paint.drawRect(nonCacheRect); - } - - paint.setPen(Qt::black); - paint.setBrush(Qt::NoBrush); - - for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { -// Profiler profiler2("View::paintEvent non-cacheable"); - (*i)->paint(this, paint, nonCacheRect); - } - - paint.end(); - - paint.begin(this); - if (e) paint.setClipRect(e->rect()); - if (!m_selectionCached) { - drawSelections(paint); - } - paint.end(); - - if (m_followPlay != PlaybackScrollContinuous) { - - paint.begin(this); - - if (long(m_playPointerFrame) > getStartFrame() && - m_playPointerFrame < getEndFrame()) { - - int playx = getXForFrame(m_playPointerFrame); - - paint.setPen(Qt::black); - paint.drawLine(playx - 1, 0, playx - 1, height() - 1); - paint.drawLine(playx + 1, 0, playx + 1, height() - 1); - paint.drawPoint(playx, 0); - paint.drawPoint(playx, height() - 1); - paint.setPen(Qt::white); - paint.drawLine(playx, 1, playx, height() - 2); - } - - paint.end(); - } - - QFrame::paintEvent(e); -} - -void -View::drawSelections(QPainter &paint) -{ - MultiSelection::SelectionList selections; - - if (m_manager) { - selections = m_manager->getSelections(); - if (m_manager->haveInProgressSelection()) { - bool exclusive; - Selection inProgressSelection = - m_manager->getInProgressSelection(exclusive); - if (exclusive) selections.clear(); - selections.insert(inProgressSelection); - } - } - - paint.save(); - paint.setBrush(QColor(150, 150, 255, 80)); - - int sampleRate = getModelsSampleRate(); - - QPoint localPos; - long illuminateFrame = -1; - bool closeToLeft, closeToRight; - - if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { - illuminateFrame = getFrameForX(localPos.x()); - } - - const QFontMetrics &metrics = paint.fontMetrics(); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - - int p0 = getXForFrame(i->getStartFrame()); - int p1 = getXForFrame(i->getEndFrame()); - - if (p1 < 0 || p0 > width()) continue; - -#ifdef DEBUG_VIEW_WIDGET_PAINT - std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl; -#endif - - bool illuminateThis = - (illuminateFrame >= 0 && i->contains(illuminateFrame)); - - paint.setPen(QColor(150, 150, 255)); - paint.drawRect(p0, -1, p1 - p0, height() + 1); - - if (illuminateThis) { - paint.save(); - if (hasLightBackground()) { - paint.setPen(QPen(Qt::black, 2)); - } else { - paint.setPen(QPen(Qt::white, 2)); - } - if (closeToLeft) { - paint.drawLine(p0, 1, p1, 1); - paint.drawLine(p0, 0, p0, height()); - paint.drawLine(p0, height() - 1, p1, height() - 1); - } else if (closeToRight) { - paint.drawLine(p0, 1, p1, 1); - paint.drawLine(p1, 0, p1, height()); - paint.drawLine(p0, height() - 1, p1, height() - 1); - } else { - paint.setBrush(Qt::NoBrush); - paint.drawRect(p0, 1, p1 - p0, height() - 2); - } - paint.restore(); - } - - if (sampleRate && shouldLabelSelections() && m_manager && - m_manager->getOverlayMode() != ViewManager::NoOverlays) { - - QString startText = QString("%1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (i->getStartFrame(), sampleRate).toText(true))) - .arg(i->getStartFrame()); - - QString endText = QString(" %1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (i->getEndFrame(), sampleRate).toText(true))) - .arg(i->getEndFrame()); - - QString durationText = QString("(%1 / %2) ") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (i->getEndFrame() - i->getStartFrame(), sampleRate) - .toText(true))) - .arg(i->getEndFrame() - i->getStartFrame()); - - int sw = metrics.width(startText), - ew = metrics.width(endText), - dw = metrics.width(durationText); - - int sy = metrics.ascent() + metrics.height() + 4; - int ey = sy; - int dy = sy + metrics.height(); - - int sx = p0 + 2; - int ex = sx; - int dx = sx; - - if (sw + ew > (p1 - p0)) { - ey += metrics.height(); - dy += metrics.height(); - } - - if (ew < (p1 - p0)) { - ex = p1 - 2 - ew; - } - - if (dw < (p1 - p0)) { - dx = p1 - 2 - dw; - } - - paint.drawText(sx, sy, startText); - paint.drawText(ex, ey, endText); - paint.drawText(dx, dy, durationText); - } - } - - paint.restore(); -} - -QString -View::toXmlString(QString indent, QString extraAttributes) const -{ - QString s; - - s += indent; - - s += QString("\n") - .arg(m_centreFrame) - .arg(m_zoomLevel) - .arg(m_followPan) - .arg(m_followZoom) - .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : - m_followPlay == PlaybackScrollPage ? "page" : "ignore") - .arg(m_lightBackground) - .arg(extraAttributes); - - for (size_t i = 0; i < m_layers.size(); ++i) { - s += m_layers[i]->toXmlString(indent + " "); - } - - s += indent + "\n"; - - return s; -} - -ViewPropertyContainer::ViewPropertyContainer(View *v) : - m_v(v) -{ - connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), - this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); -} - -#ifdef INCLUDE_MOCFILES -#include "View.moc.cpp" -#endif - diff -r f90fad823cea -r 3a13b0d4934e base/View.h --- a/base/View.h Fri Jul 28 11:09:36 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,362 +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 _CANVAS_H_ -#define _CANVAS_H_ - -#include -#include - -#include "base/ZoomConstraint.h" -#include "base/PropertyContainer.h" -#include "base/ViewManager.h" -#include "base/XmlExportable.h" - -// #define DEBUG_VIEW_WIDGET_PAINT 1 - -class Layer; -class ViewPropertyContainer; - -#include - -/** - * View is the base class of widgets that display one or more - * overlaid views of data against a horizontal time scale. - * - * A View may have any number of attached Layers, each of which - * is expected to have one data Model (although multiple views may - * share the same model). - * - * A View may be panned in time and zoomed, although the - * mechanisms for doing so (as well as any other operations and - * properties available) depend on the subclass. - */ - -class View : public QFrame, - public XmlExportable -{ - Q_OBJECT - -public: - /** - * Deleting a View does not delete any of its layers. They should - * be managed elsewhere (e.g. by the Document). - */ - virtual ~View(); - - /** - * Retrieve the first visible sample frame on the widget. - * This is a calculated value based on the centre-frame, widget - * width and zoom level. The result may be negative. - */ - virtual long getStartFrame() const; - - /** - * Set the widget pan based on the given first visible frame. The - * frame value may be negative. - */ - virtual void setStartFrame(long); - - /** - * Return the centre frame of the visible widget. This is an - * exact value that does not depend on the zoom block size. Other - * frame values (start, end) are calculated from this based on the - * zoom and other factors. - */ - virtual size_t getCentreFrame() const { return m_centreFrame; } - - /** - * Set the centre frame of the visible widget. - */ - virtual void setCentreFrame(size_t f) { setCentreFrame(f, true); } - - /** - * Retrieve the last visible sample frame on the widget. - * This is a calculated value based on the centre-frame, widget - * width and zoom level. - */ - virtual size_t getEndFrame() const; - - /** - * Return the pixel x-coordinate corresponding to a given sample - * frame (which may be negative). - */ - int getXForFrame(long frame) const; - - /** - * Return the closest frame to the given pixel x-coordinate. - */ - long getFrameForX(int x) const; - - /** - * Return the pixel y-coordinate corresponding to a given - * frequency, if the frequency range is as specified. This does - * not imply any policy about layer frequency ranges, but it might - * be useful for layers to match theirs up if desired. - * - * Not thread-safe in logarithmic mode. Call only from GUI thread. - */ - float getYForFrequency(float frequency, float minFreq, float maxFreq, - bool logarithmic) const; - - /** - * Return the closest frequency to the given pixel y-coordinate, - * if the frequency range is as specified. - * - * Not thread-safe in logarithmic mode. Call only from GUI thread. - */ - float getFrequencyForY(int y, float minFreq, float maxFreq, - bool logarithmic) const; - - /** - * Return the zoom level, i.e. the number of frames per pixel - */ - int getZoomLevel() const; - - /** - * Set the zoom level, i.e. the number of frames per pixel. The - * centre frame will be unchanged; the start and end frames will - * change. - */ - virtual void setZoomLevel(size_t z); - - /** - * Zoom in or out. - */ - virtual void zoom(bool in); - - /** - * Scroll left or right by a smallish or largish amount. - */ - virtual void scroll(bool right, bool lots); - - virtual void addLayer(Layer *v); - virtual void removeLayer(Layer *v); // does not delete the layer - virtual int getLayerCount() const { return m_layers.size(); } - - /** - * Return a layer, counted in stacking order. That is, layer 0 is - * the bottom layer and layer "getLayerCount()-1" is the top one. - */ - virtual Layer *getLayer(int n) { return m_layers[n]; } - - /** - * Return the layer last selected by the user. This is normally - * the top layer, the same as getLayer(getLayerCount()-1). - * However, if the user has selected the pane itself more recently - * than any of the layers on it, this function will return 0. It - * will also return 0 if there are no layers. - */ - virtual Layer *getSelectedLayer(); - virtual const Layer *getSelectedLayer() const; - - virtual void setViewManager(ViewManager *m); - virtual ViewManager *getViewManager() const { return m_manager; } - - virtual void setFollowGlobalPan(bool f); - virtual bool getFollowGlobalPan() const { return m_followPan; } - - virtual void setFollowGlobalZoom(bool f); - virtual bool getFollowGlobalZoom() const { return m_followZoom; } - - virtual void setLightBackground(bool lb) { m_lightBackground = lb; } - virtual bool hasLightBackground() const { return m_lightBackground; } - - enum TextStyle { - BoxedText, - OutlinedText - }; - - virtual void drawVisibleText(QPainter &p, int x, int y, - QString text, TextStyle style); - - virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const { - return false; - } - virtual bool shouldIlluminateLocalSelection(QPoint &, bool &, bool &) const { - return false; - } - - enum PlaybackFollowMode { - PlaybackScrollContinuous, - PlaybackScrollPage, - PlaybackIgnore - }; - virtual void setPlaybackFollow(PlaybackFollowMode m); - virtual PlaybackFollowMode getPlaybackFollow() const { return m_followPlay; } - - typedef PropertyContainer::PropertyName PropertyName; - - // We implement the PropertyContainer API, although we don't - // actually subclass PropertyContainer. We have our own - // PropertyContainer that we can return on request that just - // delegates back to us. - virtual PropertyContainer::PropertyList getProperties() const; - virtual QString getPropertyLabel(const PropertyName &) const; - virtual PropertyContainer::PropertyType getPropertyType(const PropertyName &) const; - virtual int getPropertyRangeAndValue(const PropertyName &, - int *min, int *max) const; - virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; - virtual void setProperty(const PropertyName &, int value); - virtual QString getPropertyContainerName() const { - return objectName(); - } - virtual QString getPropertyContainerIconName() const = 0; - - virtual size_t getPropertyContainerCount() const; - - virtual const PropertyContainer *getPropertyContainer(size_t i) const; - virtual PropertyContainer *getPropertyContainer(size_t i); - - virtual int getTextLabelHeight(const Layer *layer, QPainter &) const; - - virtual bool getValueExtents(QString unit, float &min, float &max, - bool &log) const; - - virtual QString toXmlString(QString indent = "", - QString extraAttributes = "") const; - - size_t getModelsStartFrame() const; - size_t getModelsEndFrame() const; - -signals: - void propertyContainerAdded(PropertyContainer *pc); - void propertyContainerRemoved(PropertyContainer *pc); - void propertyContainerPropertyChanged(PropertyContainer *pc); - void propertyContainerNameChanged(PropertyContainer *pc); - void propertyChanged(PropertyContainer::PropertyName); - - void centreFrameChanged(void *, unsigned long, bool); - void zoomLevelChanged(void *, unsigned long, bool); - -public slots: - virtual void modelChanged(); - virtual void modelChanged(size_t startFrame, size_t endFrame); - virtual void modelCompletionChanged(); - virtual void modelReplaced(); - virtual void layerParametersChanged(); - virtual void layerNameChanged(); - - virtual void viewManagerCentreFrameChanged(void *, unsigned long, bool); - virtual void viewManagerPlaybackFrameChanged(unsigned long); - virtual void viewManagerZoomLevelChanged(void *, unsigned long, bool); - - virtual void propertyContainerSelected(View *, PropertyContainer *pc); - - virtual void selectionChanged(); - virtual void toolModeChanged(); - -protected: - View(QWidget *, bool showProgress); - virtual void paintEvent(QPaintEvent *e); - virtual void drawSelections(QPainter &); - virtual bool shouldLabelSelections() const { return true; } - - typedef std::vector LayerList; - - int getModelsSampleRate() const; - bool areLayersScrollable() const; - LayerList getScrollableBackLayers(bool testChanged, bool &changed) const; - LayerList getNonScrollableFrontLayers(bool testChanged, bool &changed) const; - size_t getZoomConstraintBlockSize(size_t blockSize, - ZoomConstraint::RoundingDirection dir = - ZoomConstraint::RoundNearest) const; - - bool setCentreFrame(size_t f, bool doEmit); - - void checkProgress(void *object); - - size_t m_centreFrame; - int m_zoomLevel; - bool m_followPan; - bool m_followZoom; - PlaybackFollowMode m_followPlay; - size_t m_playPointerFrame; - bool m_lightBackground; - bool m_showProgress; - - QPixmap *m_cache; - size_t m_cacheCentreFrame; - int m_cacheZoomLevel; - bool m_selectionCached; - - bool m_deleting; - - LayerList m_layers; // I don't own these, but see dtor note above - bool m_haveSelectedLayer; - - // caches for use in getScrollableBackLayers, getNonScrollableFrontLayers - mutable LayerList m_lastScrollableBackLayers; - mutable LayerList m_lastNonScrollableBackLayers; - - class LayerProgressBar : public QProgressBar { - public: - LayerProgressBar(QWidget *parent); - virtual QString text() const { return m_text; } - virtual void setText(QString text) { m_text = text; } - protected: - QString m_text; - }; - - typedef std::map ProgressMap; - ProgressMap m_progressBars; // I own the ProgressBars - - ViewManager *m_manager; // I don't own this - ViewPropertyContainer *m_propertyContainer; // I own this -}; - - -// Use this for delegation, because we can't subclass from -// PropertyContainer (which is a QObject) ourselves because of -// ambiguity with QFrame parent - -class ViewPropertyContainer : public PropertyContainer -{ - Q_OBJECT - -public: - ViewPropertyContainer(View *v); - PropertyList getProperties() const { return m_v->getProperties(); } - QString getPropertyLabel(const PropertyName &n) const { - return m_v->getPropertyLabel(n); - } - PropertyType getPropertyType(const PropertyName &n) const { - return m_v->getPropertyType(n); - } - int getPropertyRangeAndValue(const PropertyName &n, int *min, int *max) const { - return m_v->getPropertyRangeAndValue(n, min, max); - } - QString getPropertyValueLabel(const PropertyName &n, int value) const { - return m_v->getPropertyValueLabel(n, value); - } - QString getPropertyContainerName() const { - return m_v->getPropertyContainerName(); - } - QString getPropertyContainerIconName() const { - return m_v->getPropertyContainerIconName(); - } - -public slots: - virtual void setProperty(const PropertyName &n, int value) { - m_v->setProperty(n, value); - } - -protected: - View *m_v; -}; - -#endif - diff -r f90fad823cea -r 3a13b0d4934e base/ViewManager.cpp --- a/base/ViewManager.cpp Fri Jul 28 11:09:36 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,361 +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 "ViewManager.h" -#include "AudioPlaySource.h" -#include "Model.h" -#include "CommandHistory.h" - -#include - -// #define DEBUG_VIEW_MANAGER 1 - -ViewManager::ViewManager() : - m_playSource(0), - m_globalCentreFrame(0), - m_globalZoom(1024), - m_playbackFrame(0), - m_mainModelSampleRate(0), - m_lastLeft(0), - m_lastRight(0), - m_inProgressExclusive(true), - m_toolMode(NavigateMode), - m_playLoopMode(false), - m_playSelectionMode(false), - m_overlayMode(BasicOverlays) -{ - connect(this, - SIGNAL(centreFrameChanged(void *, unsigned long, bool)), - SLOT(considerSeek(void *, unsigned long, bool))); - - connect(this, - SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), - SLOT(considerZoomChange(void *, unsigned long, bool))); -} - -ViewManager::~ViewManager() -{ -} - -unsigned long -ViewManager::getGlobalCentreFrame() const -{ -#ifdef DEBUG_VIEW_MANAGER - std::cout << "ViewManager::getGlobalCentreFrame: returning " << m_globalCentreFrame << std::endl; -#endif - return m_globalCentreFrame; -} - -unsigned long -ViewManager::getGlobalZoom() const -{ -#ifdef DEBUG_VIEW_MANAGER - std::cout << "ViewManager::getGlobalZoom: returning " << m_globalZoom << std::endl; -#endif - return m_globalZoom; -} - -unsigned long -ViewManager::getPlaybackFrame() const -{ - if (m_playSource && m_playSource->isPlaying()) { - m_playbackFrame = m_playSource->getCurrentPlayingFrame(); - } - return m_playbackFrame; -} - -void -ViewManager::setPlaybackFrame(unsigned long f) -{ - if (m_playbackFrame != f) { - m_playbackFrame = f; - emit playbackFrameChanged(f); - if (m_playSource && m_playSource->isPlaying()) { - m_playSource->play(f); - } - } -} - -bool -ViewManager::haveInProgressSelection() const -{ - return !m_inProgressSelection.isEmpty(); -} - -const Selection & -ViewManager::getInProgressSelection(bool &exclusive) const -{ - exclusive = m_inProgressExclusive; - return m_inProgressSelection; -} - -void -ViewManager::setInProgressSelection(const Selection &selection, bool exclusive) -{ - m_inProgressExclusive = exclusive; - m_inProgressSelection = selection; - if (exclusive) clearSelections(); - emit inProgressSelectionChanged(); -} - -void -ViewManager::clearInProgressSelection() -{ - m_inProgressSelection = Selection(); - emit inProgressSelectionChanged(); -} - -const MultiSelection & -ViewManager::getSelection() const -{ - return m_selections; -} - -const MultiSelection::SelectionList & -ViewManager::getSelections() const -{ - return m_selections.getSelections(); -} - -void -ViewManager::setSelection(const Selection &selection) -{ - MultiSelection ms(m_selections); - ms.setSelection(selection); - setSelections(ms); -} - -void -ViewManager::addSelection(const Selection &selection) -{ - MultiSelection ms(m_selections); - ms.addSelection(selection); - setSelections(ms); -} - -void -ViewManager::removeSelection(const Selection &selection) -{ - MultiSelection ms(m_selections); - ms.removeSelection(selection); - setSelections(ms); -} - -void -ViewManager::clearSelections() -{ - MultiSelection ms(m_selections); - ms.clearSelections(); - setSelections(ms); -} - -void -ViewManager::setSelections(const MultiSelection &ms) -{ - if (m_selections.getSelections() == ms.getSelections()) return; - SetSelectionCommand *command = new SetSelectionCommand(this, ms); - CommandHistory::getInstance()->addCommand(command); -} - -void -ViewManager::signalSelectionChange() -{ - emit selectionChanged(); -} - -ViewManager::SetSelectionCommand::SetSelectionCommand(ViewManager *vm, - const MultiSelection &ms) : - m_vm(vm), - m_oldSelection(vm->m_selections), - m_newSelection(ms) -{ -} - -ViewManager::SetSelectionCommand::~SetSelectionCommand() { } - -void -ViewManager::SetSelectionCommand::execute() -{ - m_vm->m_selections = m_newSelection; - m_vm->signalSelectionChange(); -} - -void -ViewManager::SetSelectionCommand::unexecute() -{ - m_vm->m_selections = m_oldSelection; - m_vm->signalSelectionChange(); -} - -QString -ViewManager::SetSelectionCommand::getName() const -{ - if (m_newSelection.getSelections().empty()) return tr("Clear Selection"); - else return tr("Select"); -} - -Selection -ViewManager::getContainingSelection(size_t frame, bool defaultToFollowing) const -{ - return m_selections.getContainingSelection(frame, defaultToFollowing); -} - -void -ViewManager::setToolMode(ToolMode mode) -{ - m_toolMode = mode; - - emit toolModeChanged(); -} - -void -ViewManager::setPlayLoopMode(bool mode) -{ - m_playLoopMode = mode; - - emit playLoopModeChanged(); -} - -void -ViewManager::setPlaySelectionMode(bool mode) -{ - m_playSelectionMode = mode; - - emit playSelectionModeChanged(); -} - -size_t -ViewManager::getPlaybackSampleRate() const -{ - if (m_playSource) { - return m_playSource->getTargetSampleRate(); - } - return 0; -} - -void -ViewManager::setAudioPlaySource(AudioPlaySource *source) -{ - if (!m_playSource) { - QTimer::singleShot(100, this, SLOT(checkPlayStatus())); - } - m_playSource = source; -} - -void -ViewManager::playStatusChanged(bool playing) -{ - checkPlayStatus(); -} - -void -ViewManager::checkPlayStatus() -{ - if (m_playSource && m_playSource->isPlaying()) { - - float left = 0, right = 0; - if (m_playSource->getOutputLevels(left, right)) { - if (left != m_lastLeft || right != m_lastRight) { - emit outputLevelsChanged(left, right); - m_lastLeft = left; - m_lastRight = right; - } - } - - m_playbackFrame = m_playSource->getCurrentPlayingFrame(); - -#ifdef DEBUG_VIEW_MANAGER - std::cout << "ViewManager::checkPlayStatus: Playing, frame " << m_playbackFrame << ", levels " << m_lastLeft << "," << m_lastRight << std::endl; -#endif - - emit playbackFrameChanged(m_playbackFrame); - - QTimer::singleShot(20, this, SLOT(checkPlayStatus())); - - } else { - - QTimer::singleShot(100, this, SLOT(checkPlayStatus())); - - if (m_lastLeft != 0.0 || m_lastRight != 0.0) { - emit outputLevelsChanged(0.0, 0.0); - m_lastLeft = 0.0; - m_lastRight = 0.0; - } - -#ifdef DEBUG_VIEW_MANAGER -// std::cout << "ViewManager::checkPlayStatus: Not playing" << std::endl; -#endif - } -} - -bool -ViewManager::isPlaying() const -{ - return m_playSource && m_playSource->isPlaying(); -} - -void -ViewManager::considerSeek(void *p, unsigned long f, bool locked) -{ - if (locked) { - m_globalCentreFrame = f; - } - -#ifdef DEBUG_VIEW_MANAGER - std::cout << "ViewManager::considerSeek(" << p << ", " << f << ", " << locked << ")" << std::endl; -#endif - - if (p == this || !locked) return; - - if (m_playSource && m_playSource->isPlaying()) { - unsigned long playFrame = m_playSource->getCurrentPlayingFrame(); - unsigned long diff = std::max(f, playFrame) - std::min(f, playFrame); - if (diff > 20000) { - m_playbackFrame = f; - m_playSource->play(f); -#ifdef DEBUG_VIEW_MANAGER - std::cout << "ViewManager::considerSeek: reseeking from " << playFrame << " to " << f << std::endl; -#endif - } - } else { - m_playbackFrame = f; //!!! only if that view is in scroll mode? - } -} - -void -ViewManager::considerZoomChange(void *p, unsigned long z, bool locked) -{ - if (locked) { - m_globalZoom = z; - } - -#ifdef DEBUG_VIEW_MANAGER - std::cout << "ViewManager::considerZoomChange(" << p << ", " << z << ", " << locked << ")" << std::endl; -#endif -} - -void -ViewManager::setOverlayMode(OverlayMode mode) -{ - if (m_overlayMode != mode) { - m_overlayMode = mode; - emit overlayModeChanged(); - } -} - -#ifdef INCLUDE_MOCFILES -#include "ViewManager.moc.cpp" -#endif - diff -r f90fad823cea -r 3a13b0d4934e base/ViewManager.h --- a/base/ViewManager.h Fri Jul 28 11:09:36 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,188 +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 _VIEW_MANAGER_H_ -#define _VIEW_MANAGER_H_ - -#include -#include - -#include - -#include "Selection.h" -#include "Command.h" -#include "Clipboard.h" - -class AudioPlaySource; -class Model; - -/** - * The ViewManager manages properties that may need to be synchronised - * between separate Views. For example, it handles signals associated - * with changes to the global pan and zoom, and it handles selections. - * - * Views should be implemented in such a way as to work - * correctly whether they are supplied with a ViewManager or not. - */ - -class ViewManager : public QObject -{ - Q_OBJECT - -public: - ViewManager(); - virtual ~ViewManager(); - - void setAudioPlaySource(AudioPlaySource *source); - - bool isPlaying() const; - - unsigned long getGlobalCentreFrame() const; - unsigned long getGlobalZoom() const; - - unsigned long getPlaybackFrame() const; - void setPlaybackFrame(unsigned long frame); - - bool haveInProgressSelection() const; - const Selection &getInProgressSelection(bool &exclusive) const; - void setInProgressSelection(const Selection &selection, bool exclusive); - void clearInProgressSelection(); - - const MultiSelection &getSelection() const; - - const MultiSelection::SelectionList &getSelections() const; - void setSelection(const Selection &selection); - void addSelection(const Selection &selection); - void removeSelection(const Selection &selection); - void clearSelections(); - - /** - * Return the selection that contains a given frame. - * If defaultToFollowing is true, and if the frame is not in a - * selected area, return the next selection after the given frame. - * Return the empty selection if no appropriate selection is found. - */ - Selection getContainingSelection(size_t frame, bool defaultToFollowing) const; - - Clipboard &getClipboard() { return m_clipboard; } - - enum ToolMode { - NavigateMode, - SelectMode, - EditMode, - DrawMode - }; - ToolMode getToolMode() const { return m_toolMode; } - void setToolMode(ToolMode mode); - - bool getPlayLoopMode() const { return m_playLoopMode; } - void setPlayLoopMode(bool on); - - bool getPlaySelectionMode() const { return m_playSelectionMode; } - void setPlaySelectionMode(bool on); - - size_t getPlaybackSampleRate() const; - size_t getMainModelSampleRate() const { return m_mainModelSampleRate; } - void setMainModelSampleRate(size_t sr) { m_mainModelSampleRate = sr; } - - enum OverlayMode { - NoOverlays, - BasicOverlays, - AllOverlays - }; - void setOverlayMode(OverlayMode mode); - OverlayMode getOverlayMode() const { return m_overlayMode; } - -signals: - /** Emitted when a widget pans. The originator identifies the widget. */ - void centreFrameChanged(void *originator, unsigned long frame, bool locked); - - /** Emitted when a widget zooms. The originator identifies the widget. */ - void zoomLevelChanged(void *originator, unsigned long zoom, bool locked); - - /** Emitted when the playback frame changes. */ - void playbackFrameChanged(unsigned long frame); - - /** Emitted when the output levels change. Values in range 0.0 -> 1.0. */ - void outputLevelsChanged(float left, float right); - - /** Emitted when the selection has changed. */ - void selectionChanged(); - - /** Emitted when the in-progress (rubberbanding) selection has changed. */ - void inProgressSelectionChanged(); - - /** Emitted when the tool mode has been changed. */ - void toolModeChanged(); - - /** Emitted when the play loop mode has been changed. */ - void playLoopModeChanged(); - - /** Emitted when the play selection mode has been changed. */ - void playSelectionModeChanged(); - - /** Emitted when the overlay mode has been changed. */ - void overlayModeChanged(); - -protected slots: - void checkPlayStatus(); - void playStatusChanged(bool playing); - void considerSeek(void *, unsigned long, bool); - void considerZoomChange(void *, unsigned long, bool); - -protected: - AudioPlaySource *m_playSource; - unsigned long m_globalCentreFrame; - unsigned long m_globalZoom; - mutable unsigned long m_playbackFrame; - size_t m_mainModelSampleRate; - - float m_lastLeft; - float m_lastRight; - - MultiSelection m_selections; - Selection m_inProgressSelection; - bool m_inProgressExclusive; - - Clipboard m_clipboard; - - ToolMode m_toolMode; - - bool m_playLoopMode; - bool m_playSelectionMode; - - void setSelections(const MultiSelection &ms); - void signalSelectionChange(); - - class SetSelectionCommand : public Command - { - public: - SetSelectionCommand(ViewManager *vm, const MultiSelection &ms); - virtual ~SetSelectionCommand(); - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const; - - protected: - ViewManager *m_vm; - MultiSelection m_oldSelection; - MultiSelection m_newSelection; - }; - - OverlayMode m_overlayMode; -}; - -#endif - diff -r f90fad823cea -r 3a13b0d4934e data/model/DenseThreeDimensionalModel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/DenseThreeDimensionalModel.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,314 @@ +/* -*- 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 "DenseThreeDimensionalModel.h" + +#include + +DenseThreeDimensionalModel::DenseThreeDimensionalModel(size_t sampleRate, + size_t windowSize, + size_t yBinCount, + bool notifyOnAdd) : + m_sampleRate(sampleRate), + m_windowSize(windowSize), + m_yBinCount(yBinCount), + m_minimum(0.0), + m_maximum(0.0), + m_notifyOnAdd(notifyOnAdd), + m_sinceLastNotifyMin(-1), + m_sinceLastNotifyMax(-1), + m_completion(100) +{ +} + +bool +DenseThreeDimensionalModel::isOK() const +{ + return true; +} + +size_t +DenseThreeDimensionalModel::getSampleRate() const +{ + return m_sampleRate; +} + +size_t +DenseThreeDimensionalModel::getStartFrame() const +{ + return 0; +} + +size_t +DenseThreeDimensionalModel::getEndFrame() const +{ + return m_windowSize * m_data.size() + (m_windowSize - 1); +} + +Model * +DenseThreeDimensionalModel::clone() const +{ + DenseThreeDimensionalModel *model = new DenseThreeDimensionalModel + (m_sampleRate, m_windowSize, m_yBinCount); + + model->m_minimum = m_minimum; + model->m_maximum = m_maximum; + + for (size_t i = 0; i < m_data.size(); ++i) { + model->setBinValues(i * m_windowSize, m_data[i]); + } + + return model; +} + +size_t +DenseThreeDimensionalModel::getWindowSize() const +{ + return m_windowSize; +} + +void +DenseThreeDimensionalModel::setWindowSize(size_t sz) +{ + m_windowSize = sz; +} + +size_t +DenseThreeDimensionalModel::getYBinCount() const +{ + return m_yBinCount; +} + +void +DenseThreeDimensionalModel::setYBinCount(size_t sz) +{ + m_yBinCount = sz; +} + +float +DenseThreeDimensionalModel::getMinimumLevel() const +{ + return m_minimum; +} + +void +DenseThreeDimensionalModel::setMinimumLevel(float level) +{ + m_minimum = level; +} + +float +DenseThreeDimensionalModel::getMaximumLevel() const +{ + return m_maximum; +} + +void +DenseThreeDimensionalModel::setMaximumLevel(float level) +{ + m_maximum = level; +} + +void +DenseThreeDimensionalModel::getBinValues(long windowStart, + BinValueSet &result) const +{ + QMutexLocker locker(&m_mutex); + + long index = windowStart / m_windowSize; + + if (index >= 0 && index < long(m_data.size())) { + result = m_data[index]; + } else { + result.clear(); + } + + while (result.size() < m_yBinCount) result.push_back(m_minimum); +} + +float +DenseThreeDimensionalModel::getBinValue(long windowStart, + size_t n) const +{ + QMutexLocker locker(&m_mutex); + + long index = windowStart / m_windowSize; + + if (index >= 0 && index < long(m_data.size())) { + const BinValueSet &s = m_data[index]; + if (n < s.size()) return s[n]; + } + + return m_minimum; +} + +void +DenseThreeDimensionalModel::setBinValues(long windowStart, + const BinValueSet &values) +{ + QMutexLocker locker(&m_mutex); + + long index = windowStart / m_windowSize; + + while (index >= long(m_data.size())) { + m_data.push_back(BinValueSet()); + } + + bool newExtents = (m_data.empty() && (m_minimum == m_maximum)); + bool allChange = false; + + for (size_t i = 0; i < values.size(); ++i) { + if (newExtents || values[i] < m_minimum) { + m_minimum = values[i]; + allChange = true; + } + if (newExtents || values[i] > m_maximum) { + m_maximum = values[i]; + allChange = true; + } + } + + m_data[index] = values; + + if (m_notifyOnAdd) { + if (allChange) { + emit modelChanged(); + } else { + emit modelChanged(windowStart, windowStart + m_windowSize); + } + } else { + if (allChange) { + m_sinceLastNotifyMin = -1; + m_sinceLastNotifyMax = -1; + emit modelChanged(); + } else { + if (m_sinceLastNotifyMin == -1 || + windowStart < m_sinceLastNotifyMin) { + m_sinceLastNotifyMin = windowStart; + } + if (m_sinceLastNotifyMax == -1 || + windowStart > m_sinceLastNotifyMax) { + m_sinceLastNotifyMax = windowStart; + } + } + } +} + +QString +DenseThreeDimensionalModel::getBinName(size_t n) const +{ + if (m_binNames.size() > n) return m_binNames[n]; + else return ""; +} + +void +DenseThreeDimensionalModel::setBinName(size_t n, QString name) +{ + while (m_binNames.size() <= n) m_binNames.push_back(""); + m_binNames[n] = name; + emit modelChanged(); +} + +void +DenseThreeDimensionalModel::setBinNames(std::vector names) +{ + m_binNames = names; + emit modelChanged(); +} + +void +DenseThreeDimensionalModel::setCompletion(int completion) +{ + if (m_completion != completion) { + m_completion = completion; + + if (completion == 100) { + + m_notifyOnAdd = true; // henceforth + emit modelChanged(); + + } else if (!m_notifyOnAdd) { + + if (m_sinceLastNotifyMin >= 0 && + m_sinceLastNotifyMax >= 0) { + emit modelChanged(m_sinceLastNotifyMin, + m_sinceLastNotifyMax + m_windowSize); + m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; + } else { + emit completionChanged(); + } + } else { + emit completionChanged(); + } + } +} + +void +DenseThreeDimensionalModel::toXml(QTextStream &out, + QString indent, + QString extraAttributes) const +{ + out << Model::toXmlString + (indent, QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" %6") + .arg(m_windowSize) + .arg(m_yBinCount) + .arg(m_minimum) + .arg(m_maximum) + .arg(getObjectExportId(&m_data)) + .arg(extraAttributes)); + + out << indent; + out << QString("\n") + .arg(getObjectExportId(&m_data)); + + for (size_t i = 0; i < m_binNames.size(); ++i) { + if (m_binNames[i] != "") { + out << indent + " "; + out << QString("\n") + .arg(i).arg(m_binNames[i]); + } + } + + for (size_t i = 0; i < m_data.size(); ++i) { + out << indent + " "; + out << QString("").arg(i); + for (size_t j = 0; j < m_data[i].size(); ++j) { + if (j > 0) out << " "; + out << m_data[i][j]; + } + out << QString("\n"); + } + + out << indent + "\n"; +} + +QString +DenseThreeDimensionalModel::toXmlString(QString indent, + QString extraAttributes) const +{ + QString s; + + { + QTextStream out(&s); + toXml(out, indent, extraAttributes); + } + + return s; +} + +#ifdef INCLUDE_MOCFILES +#include "DenseThreeDimensionalModel.moc.cpp" +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/DenseThreeDimensionalModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/DenseThreeDimensionalModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,139 @@ +/* -*- 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 _DENSE_THREE_DIMENSIONAL_MODEL_H_ +#define _DENSE_THREE_DIMENSIONAL_MODEL_H_ + +#include "base/Model.h" +#include "base/ZoomConstraint.h" + +#include +#include + +class DenseThreeDimensionalModel : public Model, + virtual public ZoomConstraint, + virtual public QObject +{ + Q_OBJECT + +public: + //!!! need to reconcile terminology -- windowSize here, resolution in sparse models + DenseThreeDimensionalModel(size_t sampleRate, + size_t windowSize, + size_t yBinCount, + bool notifyOnAdd = true); + + virtual bool isOK() const; + + virtual size_t getSampleRate() const; + virtual size_t getStartFrame() const; + virtual size_t getEndFrame() const; + + virtual Model *clone() const; + + + /** + * Return the number of sample frames covered by each set of bins. + */ + virtual size_t getWindowSize() const; + + /** + * Set the number of sample frames covered by each set of bins. + */ + virtual void setWindowSize(size_t sz); + + /** + * Return the number of bins in each set of bins. + */ + virtual size_t getYBinCount() const; + + /** + * Set the number of bins in each set of bins. + */ + virtual void setYBinCount(size_t sz); + + /** + * Return the minimum value of the value in each bin. + */ + virtual float getMinimumLevel() const; + + /** + * Set the minimum value of the value in a bin. + */ + virtual void setMinimumLevel(float sz); + + /** + * Return the maximum value of the value in each bin. + */ + virtual float getMaximumLevel() const; + + /** + * Set the maximum value of the value in a bin. + */ + virtual void setMaximumLevel(float sz); + + typedef std::vector BinValueSet; + + /** + * Get the set of bin values at the given sample frame (i.e. the + * windowStartFrame/getWindowSize()'th set of bins). + */ + virtual void getBinValues(long windowStartFrame, BinValueSet &result) const; + + /** + * Get a single value, the one at the n'th bin of the set of bins + * starting at the given sample frame. + */ + virtual float getBinValue(long windowStartFrame, size_t n) const; + + /** + * Set the entire set of bin values at the given sample frame. + */ + virtual void setBinValues(long windowStartFrame, const BinValueSet &values); + + virtual QString getBinName(size_t n) const; + virtual void setBinName(size_t n, QString); + virtual void setBinNames(std::vector names); + + virtual void setCompletion(int completion); + virtual int getCompletion() const { return m_completion; } + + virtual void toXml(QTextStream &out, + QString indent = "", + QString extraAttributes = "") const; + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const; + +protected: + typedef std::vector ValueMatrix; + ValueMatrix m_data; + + std::vector m_binNames; + + size_t m_sampleRate; + size_t m_windowSize; + size_t m_yBinCount; + float m_minimum; + float m_maximum; + bool m_notifyOnAdd; + long m_sinceLastNotifyMin; + long m_sinceLastNotifyMax; + int m_completion; + + mutable QMutex m_mutex; +}; + +#endif diff -r f90fad823cea -r 3a13b0d4934e data/model/DenseTimeValueModel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/DenseTimeValueModel.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,28 @@ +/* -*- 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 "DenseTimeValueModel.h" +#include "PlayParameterRepository.h" + +DenseTimeValueModel::DenseTimeValueModel() +{ + PlayParameterRepository::getInstance()->addModel(this); +} + + +#ifdef INCLUDE_MOCFILES +#include "DenseTimeValueModel.moc.cpp" +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/DenseTimeValueModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/DenseTimeValueModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,76 @@ +/* -*- 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 _DENSE_TIME_VALUE_MODEL_H_ +#define _DENSE_TIME_VALUE_MODEL_H_ + +#include + +#include "base/Model.h" + +/** + * Base class for models containing dense two-dimensional data (value + * against time). For example, audio waveform data. + */ + +class DenseTimeValueModel : public Model, + virtual public QObject +{ + Q_OBJECT + +public: + DenseTimeValueModel(); + + /** + * Return the minimum possible value found in this model type. + * (That is, the minimum that would be valid, not the minimum + * actually found in a particular model). + */ + virtual float getValueMinimum() const = 0; + + /** + * Return the minimum possible value found in this model type. + * (That is, the minimum that would be valid, not the minimum + * actually found in a particular model). + */ + virtual float getValueMaximum() const = 0; + + /** + * Return the number of distinct channels for this model. + */ + virtual size_t getChannelCount() const = 0; + + /** + * Get the specified set of samples from the given channel of the + * model in single-precision floating-point format. Return the + * number of samples actually retrieved. + * If the channel is given as -1, mix all available channels and + * return the result. + */ + virtual size_t getValues(int channel, size_t start, size_t end, + float *buffer) const = 0; + + /** + * Get the specified set of samples from the given channel of the + * model in double-precision floating-point format. Return the + * number of samples actually retrieved. + * If the channel is given as -1, mix all available channels and + * return the result. + */ + virtual size_t getValues(int channel, size_t start, size_t end, + double *buffer) const = 0; +}; + +#endif diff -r f90fad823cea -r 3a13b0d4934e data/model/NoteModel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/NoteModel.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,75 @@ +/* -*- 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 "NoteModel.h" + +NoteModel::PointList +NoteModel::getPoints(long start, long end) const +{ + if (start > end) return PointList(); + QMutexLocker locker(&m_mutex); + + Note endPoint(end); + + PointListIterator endItr = m_points.upper_bound(endPoint); + + if (endItr != m_points.end()) ++endItr; + if (endItr != m_points.end()) ++endItr; + + PointList rv; + + for (PointListIterator i = endItr; i != m_points.begin(); ) { + --i; + if (i->frame < start) { + if (i->frame + i->duration >= start) { + rv.insert(*i); + } + } else if (i->frame <= end) { + rv.insert(*i); + } + } + + return rv; +} + +NoteModel::PointList +NoteModel::getPoints(long frame) const +{ + QMutexLocker locker(&m_mutex); + + if (m_resolution == 0) return PointList(); + + long start = (frame / m_resolution) * m_resolution; + long end = start + m_resolution; + + Note endPoint(end); + + PointListIterator endItr = m_points.upper_bound(endPoint); + + PointList rv; + + for (PointListIterator i = endItr; i != m_points.begin(); ) { + --i; + if (i->frame < start) { + if (i->frame + i->duration >= start) { + rv.insert(*i); + } + } else if (i->frame <= end) { + rv.insert(*i); + } + } + + return rv; +} diff -r f90fad823cea -r 3a13b0d4934e data/model/NoteModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/NoteModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,127 @@ +/* -*- 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 _NOTE_MODEL_H_ +#define _NOTE_MODEL_H_ + +#include "SparseValueModel.h" +#include "PlayParameterRepository.h" +#include "base/RealTime.h" + +/** + * Note type for use in a SparseModel or SparseValueModel. All we + * mean by a "note" is something that has an onset time, a single + * value, and a duration. Like other points, it can also have a + * label. With this point type, the model can be thought of as + * representing a simple MIDI-type piano roll, except that the y + * coordinates (values) do not have to be discrete integers. + */ + +struct Note +{ +public: + Note(long _frame) : frame(_frame), value(0.0f), duration(0) { } + Note(long _frame, float _value, size_t _duration, QString _label) : + frame(_frame), value(_value), duration(_duration), label(_label) { } + + int getDimensions() const { return 3; } + + long frame; + float value; + size_t duration; + QString label; + + QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return QString("%1\n") + .arg(indent).arg(frame).arg(value).arg(duration).arg(label).arg(extraAttributes); + } + + QString toDelimitedDataString(QString delimiter, size_t sampleRate) const + { + QStringList list; + list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); + list << QString("%1").arg(value); + list << QString("%1").arg(duration); + list << label; + return list.join(delimiter); + } + + struct Comparator { + bool operator()(const Note &p1, + const Note &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + if (p1.value != p2.value) return p1.value < p2.value; + if (p1.duration != p2.duration) return p1.duration < p2.duration; + return p1.label < p2.label; + } + }; + + struct OrderComparator { + bool operator()(const Note &p1, + const Note &p2) const { + return p1.frame < p2.frame; + } + }; +}; + + +class NoteModel : public SparseValueModel +{ +public: + NoteModel(size_t sampleRate, size_t resolution, + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + SparseValueModel(sampleRate, resolution, + valueMinimum, valueMaximum, + notifyOnAdd), + m_valueQuantization(0) + { + PlayParameterRepository::getInstance()->addModel(this); + } + + float getValueQuantization() const { return m_valueQuantization; } + void setValueQuantization(float q) { m_valueQuantization = q; } + + /** + * Notes have a duration, so this returns all points that span any + * of the given range (as well as the usual additional few before + * and after). Consequently this can be very slow (optimised data + * structures still to be done!). + */ + virtual PointList getPoints(long start, long end) const; + + /** + * Notes have a duration, so this returns all points that span the + * given frame. Consequently this can be very slow (optimised + * data structures still to be done!). + */ + virtual PointList getPoints(long frame) const; + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return SparseValueModel::toXmlString + (indent, + QString("%1 valueQuantization=\"%2\"") + .arg(extraAttributes).arg(m_valueQuantization)); + } + +protected: + float m_valueQuantization; +}; + +#endif diff -r f90fad823cea -r 3a13b0d4934e data/model/PowerOfSqrtTwoZoomConstraint.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,104 @@ +/* -*- 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 "PowerOfSqrtTwoZoomConstraint.h" + +#include +#include + + +size_t +PowerOfSqrtTwoZoomConstraint::getNearestBlockSize(size_t blockSize, + RoundingDirection dir) const +{ + int type, power; + size_t rv = getNearestBlockSize(blockSize, type, power, dir); + return rv; +} + +size_t +PowerOfSqrtTwoZoomConstraint::getNearestBlockSize(size_t blockSize, + int &type, + int &power, + RoundingDirection dir) const +{ +// std::cerr << "given " << blockSize << std::endl; + + size_t minCachePower = getMinCachePower(); + + if (blockSize < (1U << minCachePower)) { + type = -1; + power = 0; + float val = 1.0, prevVal = 1.0; + while (val + 0.01 < blockSize) { + prevVal = val; + val *= sqrt(2); + } + size_t rval; + if (dir == RoundUp) rval = size_t(val + 0.01); + else if (dir == RoundDown) rval = size_t(prevVal + 0.01); + else if (val - blockSize < blockSize - prevVal) rval = size_t(val + 0.01); + else rval = size_t(prevVal + 0.01); +// std::cerr << "returning " << rval << std::endl; + return rval; + } + + unsigned int prevBase = (1 << minCachePower); + unsigned int prevPower = minCachePower; + unsigned int prevType = 0; + + size_t result = 0; + + for (unsigned int i = 0; ; ++i) { + + power = minCachePower + i/2; + type = i % 2; + + unsigned int base; + if (type == 0) { + base = (1 << power); + } else { + base = (((unsigned int)((1 << minCachePower) * sqrt(2) + 0.01)) + << (power - minCachePower)); + } + +// std::cerr << "Testing base " << base << std::endl; + if (base >= blockSize) { + if (dir == RoundNearest) { + if (base - blockSize < blockSize - prevBase) { + dir = RoundUp; + } else { + dir = RoundDown; + } + } + if (dir == RoundUp) { + result = base; + break; + } else { + type = prevType; + power = prevPower; + result = prevBase; + break; + } + } + + prevType = type; + prevPower = power; + prevBase = base; + } + + if (result > getMaxZoomLevel()) result = getMaxZoomLevel(); + return result; +} diff -r f90fad823cea -r 3a13b0d4934e data/model/PowerOfSqrtTwoZoomConstraint.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,39 @@ +/* -*- 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 _POWER_OF_SQRT_TWO_ZOOM_CONSTRAINT_H_ +#define _POWER_OF_SQRT_TWO_ZOOM_CONSTRAINT_H_ + +#include "base/ZoomConstraint.h" + +class PowerOfSqrtTwoZoomConstraint : virtual public ZoomConstraint +{ +public: + virtual size_t getNearestBlockSize(size_t requestedBlockSize, + RoundingDirection dir = RoundNearest) + const; + +protected: + virtual size_t getNearestBlockSize(size_t requestedBlockSize, + int &type, + int &power, + RoundingDirection dir = RoundNearest) + const; + + virtual size_t getMinCachePower() const { return 6; } +}; + +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/PowerOfTwoZoomConstraint.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/PowerOfTwoZoomConstraint.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,47 @@ +/* -*- 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 "PowerOfTwoZoomConstraint.h" + +size_t +PowerOfTwoZoomConstraint::getNearestBlockSize(size_t req, + RoundingDirection dir) const +{ + size_t result = 0; + + for (size_t bs = 1; ; bs *= 2) { + if (bs >= req) { + if (dir == RoundNearest) { + if (bs - req < req - bs/2) { + result = bs; + break; + } else { + result = bs/2; + break; + } + } else if (dir == RoundDown) { + result = bs/2; + break; + } else { + result = bs; + break; + } + } + } + + if (result > getMaxZoomLevel()) result = getMaxZoomLevel(); + return result; +} + diff -r f90fad823cea -r 3a13b0d4934e data/model/PowerOfTwoZoomConstraint.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/PowerOfTwoZoomConstraint.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,35 @@ +/* -*- 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 _POWER_OF_TWO_ZOOM_CONSTRAINT_H_ +#define _POWER_OF_TWO_ZOOM_CONSTRAINT_H_ + +#include "base/ZoomConstraint.h" + +class PowerOfTwoZoomConstraint : virtual public ZoomConstraint +{ +public: + virtual size_t getNearestBlockSize(size_t requestedBlockSize, + RoundingDirection dir = RoundNearest) + const; +/* + virtual size_t getNextBlockSize(size_t blockSize, + RoundingDirection dir = RoundNearest) + const; +*/ +}; + +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/RangeSummarisableTimeValueModel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/RangeSummarisableTimeValueModel.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,5 @@ + +#ifdef INCLUDE_MOCFILES +#include "RangeSummarisableTimeValueModel.moc.cpp" +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/RangeSummarisableTimeValueModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/RangeSummarisableTimeValueModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,75 @@ +/* -*- 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 _RANGE_SUMMARISABLE_TIME_VALUE_MODEL_H_ +#define _RANGE_SUMMARISABLE_TIME_VALUE_MODEL_H_ + +#include + +#include "DenseTimeValueModel.h" +#include "base/ZoomConstraint.h" + +/** + * Base class for models containing dense two-dimensional data (value + * against time) that may be meaningfully represented in a zoomed view + * using min/max range summaries. Audio waveform data is an obvious + * example: think "peaks and minima" for "ranges". + */ + +class RangeSummarisableTimeValueModel : public DenseTimeValueModel, + virtual public ZoomConstraint, + virtual public QObject +{ + Q_OBJECT + +public: + struct Range + { + float min; + float max; + float absmean; + Range() : + min(0.f), max(0.f), absmean(0.f) { } + Range(const Range &r) : + min(r.min), max(r.max), absmean(r.absmean) { } + Range(float min_, float max_, float absmean_) : + min(min_), max(max_), absmean(absmean_) { } + }; + + typedef std::vector RangeBlock; + + /** + * Return ranges between the given start and end frames, + * summarised at the given block size. ((end - start + 1) / + * blockSize) ranges should ideally be returned. + * + * If the given block size is not supported by this model + * (according to its zoom constraint), also modify the blockSize + * parameter so as to return the block size that was actually + * obtained. + */ + virtual RangeBlock getRanges(size_t channel, size_t start, size_t end, + size_t &blockSize) const = 0; + + /** + * Return the range between the given start and end frames, + * summarised at a block size equal to the distance between start + * and end frames. + */ + virtual Range getRange(size_t channel, size_t start, size_t end) const = 0; +}; + +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/SparseModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/SparseModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,641 @@ +/* -*- 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 _SPARSE_MODEL_H_ +#define _SPARSE_MODEL_H_ + +#include "base/Model.h" +#include "base/Command.h" +#include "base/CommandHistory.h" + +#include + +#include +#include +#include + + +/** + * Model containing sparse data (points with some properties). The + * properties depend on the point type. + */ + +template +class SparseModel : public Model +{ +public: + SparseModel(size_t sampleRate, size_t resolution, + bool notifyOnAdd = true); + virtual ~SparseModel() { } + + virtual bool isOK() const { return true; } + virtual size_t getStartFrame() const; + virtual size_t getEndFrame() const; + virtual size_t getSampleRate() const { return m_sampleRate; } + + virtual Model *clone() const; + + // Number of frames of the underlying sample rate that this model + // is capable of resolving to. For example, if m_resolution == 10 + // then every point in this model will be at a multiple of 10 + // sample frames and should be considered to cover a window ending + // 10 sample frames later. + virtual size_t getResolution() const { + return m_resolution ? m_resolution : 1; + } + virtual void setResolution(size_t resolution); + + typedef PointType Point; + typedef std::multiset PointList; + typedef typename PointList::iterator PointListIterator; + + /** + * Return whether the model is empty or not. + */ + virtual bool isEmpty() const; + + /** + * Get the total number of points in the model. + */ + virtual size_t getPointCount() const; + + /** + * Get all of the points in this model between the given + * boundaries (in frames), as well as up to two points before and + * after the boundaries. If you need exact boundaries, check the + * point coordinates in the returned list. + */ + virtual PointList getPoints(long start, long end) const; + + /** + * Get all points that cover the given frame number, taking the + * resolution of the model into account. + */ + virtual PointList getPoints(long frame) const; + + /** + * Return all points that share the nearest frame number prior to + * the given one at which there are any points. + */ + virtual PointList getPreviousPoints(long frame) const; + + /** + * Return all points that share the nearest frame number + * subsequent to the given one at which there are any points. + */ + virtual PointList getNextPoints(long frame) const; + + /** + * Remove all points. + */ + virtual void clear(); + + /** + * Add a point. + */ + virtual void addPoint(const PointType &point); + + /** + * Remove a point. Points are not necessarily unique, so this + * function will remove the first point that compares equal to the + * supplied one using Point::Comparator. Other identical points + * may remain in the model. + */ + virtual void deletePoint(const PointType &point); + + virtual void setCompletion(int completion); + virtual int getCompletion() const { return m_completion; } + + virtual bool hasTextLabels() const { return m_hasTextLabels; } + + virtual void toXml(QTextStream &out, + QString indent = "", + QString extraAttributes = "") const; + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const; + + virtual QString toDelimitedDataString(QString delimiter) const + { + QString s; + for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) { + s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n"; + } + return s; + } + + /** + * Command to add a point, with undo. + */ + class AddPointCommand : public Command + { + public: + AddPointCommand(SparseModel *model, + const PointType &point, + QString name = "") : + m_model(model), m_point(point), m_name(name) { } + + virtual QString getName() const { + return (m_name == "" ? tr("Add Point") : m_name); + } + + virtual void execute() { m_model->addPoint(m_point); } + virtual void unexecute() { m_model->deletePoint(m_point); } + + const PointType &getPoint() const { return m_point; } + + private: + SparseModel *m_model; + PointType m_point; + QString m_name; + }; + + + /** + * Command to remove a point, with undo. + */ + class DeletePointCommand : public Command + { + public: + DeletePointCommand(SparseModel *model, + const PointType &point) : + m_model(model), m_point(point) { } + + virtual QString getName() const { return tr("Delete Point"); } + + virtual void execute() { m_model->deletePoint(m_point); } + virtual void unexecute() { m_model->addPoint(m_point); } + + const PointType &getPoint() const { return m_point; } + + private: + SparseModel *m_model; + PointType m_point; + }; + + + /** + * Command to add or remove a series of points, with undo. + * Consecutive add/remove pairs for the same point are collapsed. + */ + class EditCommand : public MacroCommand + { + public: + EditCommand(SparseModel *model, QString commandName); + + virtual void addPoint(const PointType &point); + virtual void deletePoint(const PointType &point); + + /** + * Stack an arbitrary other command in the same sequence. + */ + virtual void addCommand(Command *command) { addCommand(command, true); } + + /** + * If any points have been added or deleted, add this command + * to the command history. Otherwise delete the command. + */ + virtual void finish(); + + protected: + virtual void addCommand(Command *command, bool executeFirst); + + SparseModel *m_model; + }; + + + /** + * Command to relabel a point. + */ + class RelabelCommand : public Command + { + public: + RelabelCommand(SparseModel *model, + const PointType &point, + QString newLabel) : + m_model(model), m_oldPoint(point), m_newPoint(point) { + m_newPoint.label = newLabel; + } + + virtual QString getName() const { return tr("Re-Label Point"); } + + virtual void execute() { + m_model->deletePoint(m_oldPoint); + m_model->addPoint(m_newPoint); + std::swap(m_oldPoint, m_newPoint); + } + + virtual void unexecute() { execute(); } + + private: + SparseModel *m_model; + PointType m_oldPoint; + PointType m_newPoint; + }; + + + +protected: + size_t m_sampleRate; + size_t m_resolution; + bool m_notifyOnAdd; + long m_sinceLastNotifyMin; + long m_sinceLastNotifyMax; + bool m_hasTextLabels; + + PointList m_points; + size_t m_pointCount; + mutable QMutex m_mutex; + int m_completion; +}; + + +template +SparseModel::SparseModel(size_t sampleRate, + size_t resolution, + bool notifyOnAdd) : + m_sampleRate(sampleRate), + m_resolution(resolution), + m_notifyOnAdd(notifyOnAdd), + m_sinceLastNotifyMin(-1), + m_sinceLastNotifyMax(-1), + m_hasTextLabels(false), + m_pointCount(0), + m_completion(100) +{ +} + +template +size_t +SparseModel::getStartFrame() const +{ + QMutexLocker locker(&m_mutex); + size_t f = 0; + if (!m_points.empty()) { + f = m_points.begin()->frame; + } + return f; +} + +template +size_t +SparseModel::getEndFrame() const +{ + QMutexLocker locker(&m_mutex); + size_t f = 0; + if (!m_points.empty()) { + PointListIterator i(m_points.end()); + f = (--i)->frame; + } + return f; +} + +template +Model * +SparseModel::clone() const +{ + SparseModel *model = + new SparseModel(m_sampleRate, m_resolution, m_notifyOnAdd); + model->m_points = m_points; + model->m_pointCount = m_pointCount; + return model; +} + +template +bool +SparseModel::isEmpty() const +{ + return m_pointCount == 0; +} + +template +size_t +SparseModel::getPointCount() const +{ + return m_pointCount; +} + +template +typename SparseModel::PointList +SparseModel::getPoints(long start, long end) const +{ + if (start > end) return PointList(); + QMutexLocker locker(&m_mutex); + + PointType startPoint(start), endPoint(end); + + PointListIterator startItr = m_points.lower_bound(startPoint); + PointListIterator endItr = m_points.upper_bound(endPoint); + + if (startItr != m_points.begin()) --startItr; + if (startItr != m_points.begin()) --startItr; + if (endItr != m_points.end()) ++endItr; + if (endItr != m_points.end()) ++endItr; + + PointList rv; + + for (PointListIterator i = startItr; i != endItr; ++i) { + rv.insert(*i); + } + + return rv; +} + +template +typename SparseModel::PointList +SparseModel::getPoints(long frame) const +{ + QMutexLocker locker(&m_mutex); + + if (m_resolution == 0) return PointList(); + + long start = (frame / m_resolution) * m_resolution; + long end = start + m_resolution; + + PointType startPoint(start), endPoint(end); + + PointListIterator startItr = m_points.lower_bound(startPoint); + PointListIterator endItr = m_points.upper_bound(endPoint); + + PointList rv; + + for (PointListIterator i = startItr; i != endItr; ++i) { + rv.insert(*i); + } + + return rv; +} + +template +typename SparseModel::PointList +SparseModel::getPreviousPoints(long originFrame) const +{ + QMutexLocker locker(&m_mutex); + + PointType lookupPoint(originFrame); + PointList rv; + + PointListIterator i = m_points.lower_bound(lookupPoint); + if (i == m_points.begin()) return rv; + + --i; + long frame = i->frame; + while (i->frame == frame) { + rv.insert(*i); + if (i == m_points.begin()) break; + --i; + } + + return rv; +} + +template +typename SparseModel::PointList +SparseModel::getNextPoints(long originFrame) const +{ + QMutexLocker locker(&m_mutex); + + PointType lookupPoint(originFrame); + PointList rv; + + PointListIterator i = m_points.upper_bound(lookupPoint); + if (i == m_points.end()) return rv; + + long frame = i->frame; + while (i != m_points.end() && i->frame == frame) { + rv.insert(*i); + ++i; + } + + return rv; +} + +template +void +SparseModel::setResolution(size_t resolution) +{ + { + QMutexLocker locker(&m_mutex); + m_resolution = resolution; + } + emit modelChanged(); +} + +template +void +SparseModel::clear() +{ + { + QMutexLocker locker(&m_mutex); + m_points.clear(); + m_pointCount = 0; + } + emit modelChanged(); +} + +template +void +SparseModel::addPoint(const PointType &point) +{ +// std::cout << "SparseModel::addPoint(" << point.frame << ", " +// << point.value << ")" << std::endl; + + { + QMutexLocker locker(&m_mutex); + m_points.insert(point); + m_pointCount++; + if (point.label != "") m_hasTextLabels = true; + } + + // Even though this model is nominally sparse, there may still be + // too many signals going on here (especially as they'll probably + // be queued from one thread to another), which is why we need the + // notifyOnAdd as an option rather than a necessity (the + // alternative is to notify on setCompletion). + + if (m_notifyOnAdd) { + emit modelChanged(point.frame, point.frame + m_resolution); + } else { + if (m_sinceLastNotifyMin == -1 || + point.frame < m_sinceLastNotifyMin) { + m_sinceLastNotifyMin = point.frame; + } + if (m_sinceLastNotifyMax == -1 || + point.frame > m_sinceLastNotifyMax) { + m_sinceLastNotifyMax = point.frame; + } + } +} + +template +void +SparseModel::deletePoint(const PointType &point) +{ + { + QMutexLocker locker(&m_mutex); + + PointListIterator i = m_points.lower_bound(point); + typename PointType::Comparator comparator; + while (i != m_points.end()) { + if (i->frame > point.frame) break; + if (!comparator(*i, point) && !comparator(point, *i)) { + m_points.erase(i); + m_pointCount--; + break; + } + ++i; + } + } +// std::cout << "SparseOneDimensionalModel: emit modelChanged(" +// << point.frame << ")" << std::endl; + emit modelChanged(point.frame, point.frame + m_resolution); +} + +template +void +SparseModel::setCompletion(int completion) +{ + if (m_completion != completion) { + m_completion = completion; + + if (completion == 100) { + + m_notifyOnAdd = true; // henceforth + emit modelChanged(); + + } else if (!m_notifyOnAdd) { + + if (m_sinceLastNotifyMin >= 0 && + m_sinceLastNotifyMax >= 0) { + emit modelChanged(m_sinceLastNotifyMin, m_sinceLastNotifyMax); + m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; + } else { + emit completionChanged(); + } + } else { + emit completionChanged(); + } + } +} + +template +void +SparseModel::toXml(QTextStream &out, + QString indent, + QString extraAttributes) const +{ + Model::toXml + (out, + indent, + QString("type=\"sparse\" dimensions=\"%1\" resolution=\"%2\" notifyOnAdd=\"%3\" dataset=\"%4\" %5") + .arg(PointType(0).getDimensions()) + .arg(m_resolution) + .arg(m_notifyOnAdd ? "true" : "false") + .arg(getObjectExportId(&m_points)) + .arg(extraAttributes)); + + out << indent; + out << QString("\n") + .arg(getObjectExportId(&m_points)) + .arg(PointType(0).getDimensions()); + + for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) { + out << i->toXmlString(indent + " "); + } + + out << indent; + out << "\n"; +} + +template +QString +SparseModel::toXmlString(QString indent, + QString extraAttributes) const +{ + QString s; + + { + QTextStream out(&s); + toXml(out, indent, extraAttributes); + } + + return s; +} + +template +SparseModel::EditCommand::EditCommand(SparseModel *model, + QString commandName) : + MacroCommand(commandName), + m_model(model) +{ +} + +template +void +SparseModel::EditCommand::addPoint(const PointType &point) +{ + addCommand(new AddPointCommand(m_model, point), true); +} + +template +void +SparseModel::EditCommand::deletePoint(const PointType &point) +{ + addCommand(new DeletePointCommand(m_model, point), true); +} + +template +void +SparseModel::EditCommand::finish() +{ + if (!m_commands.empty()) { + CommandHistory::getInstance()->addCommand(this, false); + } else { + delete this; + } +} + +template +void +SparseModel::EditCommand::addCommand(Command *command, + bool executeFirst) +{ + if (executeFirst) command->execute(); + + if (!m_commands.empty()) { + DeletePointCommand *dpc = dynamic_cast(command); + if (dpc) { + AddPointCommand *apc = dynamic_cast + (m_commands[m_commands.size() - 1]); + typename PointType::Comparator comparator; + if (apc) { + if (!comparator(apc->getPoint(), dpc->getPoint()) && + !comparator(dpc->getPoint(), apc->getPoint())) { + deleteCommand(apc); + return; + } + } + } + } + + MacroCommand::addCommand(command); +} + + +#endif + + + diff -r f90fad823cea -r 3a13b0d4934e data/model/SparseOneDimensionalModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/SparseOneDimensionalModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,91 @@ +/* -*- 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 _SPARSE_ONE_DIMENSIONAL_MODEL_H_ +#define _SPARSE_ONE_DIMENSIONAL_MODEL_H_ + +#include "SparseModel.h" +#include "PlayParameterRepository.h" +#include "base/RealTime.h" + +struct OneDimensionalPoint +{ +public: + OneDimensionalPoint(long _frame) : frame(_frame) { } + OneDimensionalPoint(long _frame, QString _label) : frame(_frame), label(_label) { } + + int getDimensions() const { return 1; } + + long frame; + QString label; + + QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return QString("%1\n") + .arg(indent).arg(frame).arg(label).arg(extraAttributes); + } + + QString toDelimitedDataString(QString delimiter, size_t sampleRate) const + { + QStringList list; + list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); + list << label; + return list.join(delimiter); + } + + struct Comparator { + bool operator()(const OneDimensionalPoint &p1, + const OneDimensionalPoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + return p1.label < p2.label; + } + }; + + struct OrderComparator { + bool operator()(const OneDimensionalPoint &p1, + const OneDimensionalPoint &p2) const { + return p1.frame < p2.frame; + } + }; +}; + + +class SparseOneDimensionalModel : public SparseModel +{ +public: + SparseOneDimensionalModel(size_t sampleRate, size_t resolution, + bool notifyOnAdd = true) : + SparseModel(sampleRate, resolution, notifyOnAdd) + { + PlayParameterRepository::getInstance()->addModel(this); + } + + int getIndexOf(const Point &point) { + // slow + int i = 0; + Point::Comparator comparator; + for (PointList::const_iterator j = m_points.begin(); + j != m_points.end(); ++j, ++i) { + if (!comparator(*j, point) && !comparator(point, *j)) return i; + } + return -1; + } +}; + +#endif + + + diff -r f90fad823cea -r 3a13b0d4934e data/model/SparseTimeValueModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/SparseTimeValueModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,94 @@ +/* -*- 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 _SPARSE_TIME_VALUE_MODEL_H_ +#define _SPARSE_TIME_VALUE_MODEL_H_ + +#include "SparseValueModel.h" +#include "PlayParameterRepository.h" +#include "base/RealTime.h" + +/** + * Time/value point type for use in a SparseModel or SparseValueModel. + * With this point type, the model basically represents a wiggly-line + * plot with points at arbitrary intervals of the model resolution. + */ + +struct TimeValuePoint +{ +public: + TimeValuePoint(long _frame) : frame(_frame), value(0.0f) { } + TimeValuePoint(long _frame, float _value, QString _label) : + frame(_frame), value(_value), label(_label) { } + + int getDimensions() const { return 2; } + + long frame; + float value; + QString label; + + QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return QString("%1\n") + .arg(indent).arg(frame).arg(value).arg(label).arg(extraAttributes); + } + + QString toDelimitedDataString(QString delimiter, size_t sampleRate) const + { + QStringList list; + list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); + list << QString("%1").arg(value); + list << label; + return list.join(delimiter); + } + + struct Comparator { + bool operator()(const TimeValuePoint &p1, + const TimeValuePoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + if (p1.value != p2.value) return p1.value < p2.value; + return p1.label < p2.label; + } + }; + + struct OrderComparator { + bool operator()(const TimeValuePoint &p1, + const TimeValuePoint &p2) const { + return p1.frame < p2.frame; + } + }; +}; + + +class SparseTimeValueModel : public SparseValueModel +{ +public: + SparseTimeValueModel(size_t sampleRate, size_t resolution, + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + SparseValueModel(sampleRate, resolution, + valueMinimum, valueMaximum, + notifyOnAdd) + { + PlayParameterRepository::getInstance()->addModel(this); + } +}; + + +#endif + + + diff -r f90fad823cea -r 3a13b0d4934e data/model/SparseValueModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/SparseValueModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,111 @@ +/* -*- 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 _SPARSE_VALUE_MODEL_H_ +#define _SPARSE_VALUE_MODEL_H_ + +#include "SparseModel.h" +#include "UnitDatabase.h" + +/** + * Model containing sparse data (points with some properties) of which + * one of the properties is an arbitrary float value. The other + * properties depend on the point type. + */ + +template +class SparseValueModel : public SparseModel +{ +public: + SparseValueModel(size_t sampleRate, size_t resolution, + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + SparseModel(sampleRate, resolution, notifyOnAdd), + m_valueMinimum(valueMinimum), + m_valueMaximum(valueMaximum) + { } + + using SparseModel::m_points; + using SparseModel::modelChanged; + + virtual float getValueMinimum() const { return m_valueMinimum; } + virtual float getValueMaximum() const { return m_valueMaximum; } + + virtual QString getScaleUnits() const { return m_units; } + virtual void setScaleUnits(QString units) { + m_units = units; + UnitDatabase::getInstance()->registerUnit(units); + } + + virtual void addPoint(const PointType &point) + { + bool allChange = false; + if (m_points.empty() || point.value < m_valueMinimum) { + m_valueMinimum = point.value; allChange = true; + } + if (m_points.empty() || point.value > m_valueMaximum) { + m_valueMaximum = point.value; allChange = true; + } + + SparseModel::addPoint(point); + if (allChange) emit modelChanged(); + } + + virtual void deletePoint(const PointType &point) + { + SparseModel::deletePoint(point); + + if (point.value == m_valueMinimum || + point.value == m_valueMaximum) { + + float formerMin = m_valueMinimum, formerMax = m_valueMaximum; + + for (typename SparseModel::PointList::const_iterator i + = m_points.begin(); + i != m_points.end(); ++i) { + + if (i == m_points.begin() || i->value < m_valueMinimum) { + m_valueMinimum = i->value; + } + if (i == m_points.begin() || i->value > m_valueMaximum) { + m_valueMaximum = i->value; + } + } + + if (formerMin != m_valueMinimum || formerMax != m_valueMaximum) { + emit modelChanged(); + } + } + } + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return SparseModel::toXmlString + (indent, + QString("%1 minimum=\"%2\" maximum=\"%3\" units=\"%4\"") + .arg(extraAttributes).arg(m_valueMinimum).arg(m_valueMaximum) + .arg(this->encodeEntities(m_units))); + } + +protected: + float m_valueMinimum; + float m_valueMaximum; + QString m_units; +}; + + +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/TextModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/TextModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,98 @@ +/* -*- 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 _TEXT_MODEL_H_ +#define _TEXT_MODEL_H_ + +#include "SparseModel.h" +#include "base/RealTime.h" + +/** + * Text point type for use in a SparseModel. This represents a piece + * of text at a given time and y-value in the [0,1) range (indicative + * of height on the window). Intended for casual textual annotations. + */ + +struct TextPoint +{ +public: + TextPoint(long _frame) : frame(_frame), height(0.0f) { } + TextPoint(long _frame, float _height, QString _label) : + frame(_frame), height(_height), label(_label) { } + + int getDimensions() const { return 2; } + + long frame; + float height; + QString label; + + QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return QString("%1\n") + .arg(indent).arg(frame).arg(height).arg(label).arg(extraAttributes); + } + + QString toDelimitedDataString(QString delimiter, size_t sampleRate) const + { + QStringList list; + list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); + list << QString("%1").arg(height); + list << label; + return list.join(delimiter); + } + + struct Comparator { + bool operator()(const TextPoint &p1, + const TextPoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + if (p1.height != p2.height) return p1.height < p2.height; + return p1.label < p2.label; + } + }; + + struct OrderComparator { + bool operator()(const TextPoint &p1, + const TextPoint &p2) const { + return p1.frame < p2.frame; + } + }; +}; + + +// Make this a class rather than a typedef so it can be predeclared. + +class TextModel : public SparseModel +{ +public: + TextModel(size_t sampleRate, size_t resolution, bool notifyOnAdd = true) : + SparseModel(sampleRate, resolution, notifyOnAdd) + { } + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const + { + return SparseModel::toXmlString + (indent, + QString("%1 subtype=\"text\"") + .arg(extraAttributes)); + } +}; + + +#endif + + + diff -r f90fad823cea -r 3a13b0d4934e data/model/WaveFileModel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/WaveFileModel.cpp Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,497 @@ +/* -*- 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 "model/WaveFileModel.h" + +#include "fileio/AudioFileReader.h" +#include "fileio/AudioFileReaderFactory.h" + +#include "base/System.h" + +#include +#include + +#include +#include +#include +#include + +#include + +using std::cerr; +using std::endl; + +WaveFileModel::WaveFileModel(QString path) : + m_path(path), + m_fillThread(0), + m_updateTimer(0), + m_lastFillExtent(0), + m_exiting(false) +{ + m_reader = AudioFileReaderFactory::createReader(path); + setObjectName(QFileInfo(path).fileName()); + if (isOK()) fillCache(); +} + +WaveFileModel::~WaveFileModel() +{ + m_exiting = true; + if (m_fillThread) m_fillThread->wait(); + delete m_reader; + m_reader = 0; +} + +bool +WaveFileModel::isOK() const +{ + return m_reader && m_reader->isOK(); +} + +bool +WaveFileModel::isReady(int *completion) const +{ + bool ready = (isOK() && (m_fillThread == 0)); + double c = double(m_lastFillExtent) / double(getEndFrame() - getStartFrame()); + if (completion) *completion = int(c * 100.0 + 0.01); + return ready; +} + +Model * +WaveFileModel::clone() const +{ + WaveFileModel *model = new WaveFileModel(m_path); + return model; +} + +size_t +WaveFileModel::getFrameCount() const +{ + if (!m_reader) return 0; + return m_reader->getFrameCount(); +} + +size_t +WaveFileModel::getChannelCount() const +{ + if (!m_reader) return 0; + return m_reader->getChannelCount(); +} + +size_t +WaveFileModel::getSampleRate() const +{ + if (!m_reader) return 0; + return m_reader->getSampleRate(); +} + +size_t +WaveFileModel::getValues(int channel, size_t start, size_t end, + float *buffer) const +{ + // Always read these directly from the file. + // This is used for e.g. audio playback. + // Could be much more efficient (although compiler optimisation will help) + + if (end < start) { + std::cerr << "ERROR: WaveFileModel::getValues[float]: end < start (" + << end << " < " << start << ")" << std::endl; + assert(end >= start); + } + + if (!m_reader || !m_reader->isOK()) return 0; + + SampleBlock frames; + m_reader->getInterleavedFrames(start, end - start, frames); + + size_t i = 0; + + int ch0 = channel, ch1 = channel, channels = getChannelCount(); + if (channel == -1) { + ch0 = 0; + ch1 = channels - 1; + } + + while (i < end - start) { + + buffer[i] = 0.0; + + for (int ch = ch0; ch <= ch1; ++ch) { + + size_t index = i * channels + ch; + if (index >= frames.size()) break; + + float sample = frames[index]; + buffer[i] += sample; + } + + ++i; + } + + return i; +} + +size_t +WaveFileModel::getValues(int channel, size_t start, size_t end, + double *buffer) const +{ + if (end < start) { + std::cerr << "ERROR: WaveFileModel::getValues[double]: end < start (" + << end << " < " << start << ")" << std::endl; + assert(end >= start); + } + + if (!m_reader || !m_reader->isOK()) return 0; + + SampleBlock frames; + m_reader->getInterleavedFrames(start, end - start, frames); + + size_t i = 0; + + int ch0 = channel, ch1 = channel, channels = getChannelCount(); + if (channel == -1) { + ch0 = 0; + ch1 = channels - 1; + } + + while (i < end - start) { + + buffer[i] = 0.0; + + for (int ch = ch0; ch <= ch1; ++ch) { + + size_t index = i * channels + ch; + if (index >= frames.size()) break; + + float sample = frames[index]; + buffer[i] += sample; + } + + ++i; + } + + return i; +} + +WaveFileModel::RangeBlock +WaveFileModel::getRanges(size_t channel, size_t start, size_t end, + size_t &blockSize) const +{ + RangeBlock ranges; + if (!isOK()) return ranges; + + if (end <= start) { + std::cerr << "WARNING: Internal error: end <= start in WaveFileModel::getRanges (end = " << end << ", start = " << start << ", blocksize = " << blockSize << ")" << std::endl; + return ranges; + } + + int cacheType = 0; + int power = getMinCachePower(); + blockSize = getNearestBlockSize(blockSize, cacheType, power, + ZoomConstraint::RoundUp); + + size_t channels = getChannelCount(); + + if (cacheType != 0 && cacheType != 1) { + + // We need to read directly from the file. We haven't got + // this cached. Hope the requested area is small. This is + // not optimal -- we'll end up reading the same frames twice + // for stereo files, in two separate calls to this method. + // We could fairly trivially handle this for most cases that + // matter by putting a single cache in getInterleavedFrames + // for short queries. + + SampleBlock frames; + m_reader->getInterleavedFrames(start, end - start, frames); + float max = 0.0, min = 0.0, total = 0.0; + size_t i = 0, count = 0; + + while (i < end - start) { + + size_t index = i * channels + channel; + if (index >= frames.size()) break; + + float sample = frames[index]; + if (sample > max || count == 0) max = sample; + if (sample < min || count == 0) min = sample; + total += fabsf(sample); + + ++i; + ++count; + + if (count == blockSize) { + ranges.push_back(Range(min, max, total / count)); + min = max = total = 0.0f; + count = 0; + } + } + + if (count > 0) { + ranges.push_back(Range(min, max, total / count)); + } + + return ranges; + + } else { + + QMutexLocker locker(&m_mutex); + + const RangeBlock &cache = m_cache[cacheType]; + + size_t cacheBlock, div; + + if (cacheType == 0) { + cacheBlock = (1 << getMinCachePower()); + div = (1 << power) / cacheBlock; + } else { + cacheBlock = ((unsigned int)((1 << getMinCachePower()) * sqrt(2) + 0.01)); + div = ((unsigned int)((1 << power) * sqrt(2) + 0.01)) / cacheBlock; + } + + size_t startIndex = start / cacheBlock; + size_t endIndex = end / cacheBlock; + + float max = 0.0, min = 0.0, total = 0.0; + size_t i = 0, count = 0; + + //cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", end " << end << ", power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; + + for (i = 0; i < endIndex - startIndex; ) { + + size_t index = (i + startIndex) * channels + channel; + if (index >= cache.size()) break; + + const Range &range = cache[index]; + if (range.max > max || count == 0) max = range.max; + if (range.min < min || count == 0) min = range.min; + total += range.absmean; + + ++i; + ++count; + + if (count == div) { + ranges.push_back(Range(min, max, total / count)); + min = max = total = 0.0f; + count = 0; + } + } + + if (count > 0) { + ranges.push_back(Range(min, max, total / count)); + } + } + + //cerr << "returning " << ranges.size() << " ranges" << endl; + return ranges; +} + +WaveFileModel::Range +WaveFileModel::getRange(size_t channel, size_t start, size_t end) const +{ + Range range; + if (!isOK()) return range; + + if (end <= start) { + std::cerr << "WARNING: Internal error: end <= start in WaveFileModel::getRange (end = " << end << ", start = " << start << ")" << std::endl; + return range; + } + + size_t blockSize; + for (blockSize = 1; blockSize <= end - start; blockSize *= 2); + blockSize /= 2; + + bool first = false; + + size_t blockStart = (start / blockSize) * blockSize; + size_t blockEnd = (end / blockSize) * blockSize; + + if (blockStart < start) blockStart += blockSize; + + if (blockEnd > blockStart) { + RangeBlock ranges = getRanges(channel, blockStart, blockEnd, blockSize); + for (size_t i = 0; i < ranges.size(); ++i) { + if (first || ranges[i].min < range.min) range.min = ranges[i].min; + if (first || ranges[i].max > range.max) range.max = ranges[i].max; + if (first || ranges[i].absmean < range.absmean) range.absmean = ranges[i].absmean; + first = false; + } + } + + if (blockStart > start) { + Range startRange = getRange(channel, start, blockStart); + range.min = std::min(range.min, startRange.min); + range.max = std::max(range.max, startRange.max); + range.absmean = std::min(range.absmean, startRange.absmean); + } + + if (blockEnd < end) { + Range endRange = getRange(channel, blockEnd, end); + range.min = std::min(range.min, endRange.min); + range.max = std::max(range.max, endRange.max); + range.absmean = std::min(range.absmean, endRange.absmean); + } + + return range; +} + +void +WaveFileModel::fillCache() +{ + m_mutex.lock(); + m_updateTimer = new QTimer(this); + connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut())); + m_updateTimer->start(100); + m_fillThread = new RangeCacheFillThread(*this); + connect(m_fillThread, SIGNAL(finished()), this, SLOT(cacheFilled())); + m_mutex.unlock(); + m_fillThread->start(); +} + +void +WaveFileModel::fillTimerTimedOut() +{ + if (m_fillThread) { + size_t fillExtent = m_fillThread->getFillExtent(); + if (fillExtent > m_lastFillExtent) { + emit modelChanged(m_lastFillExtent, fillExtent); + m_lastFillExtent = fillExtent; + } + } else { + emit modelChanged(); + } +} + +void +WaveFileModel::cacheFilled() +{ + m_mutex.lock(); + delete m_fillThread; + m_fillThread = 0; + delete m_updateTimer; + m_updateTimer = 0; + m_mutex.unlock(); + emit modelChanged(); +// cerr << "WaveFileModel::cacheFilled" << endl; +} + +void +WaveFileModel::RangeCacheFillThread::run() +{ + size_t cacheBlockSize[2]; + cacheBlockSize[0] = (1 << m_model.getMinCachePower()); + cacheBlockSize[1] = ((unsigned int)((1 << m_model.getMinCachePower()) * + sqrt(2) + 0.01)); + + size_t frame = 0; + size_t readBlockSize = 16384; + SampleBlock block; + + if (!m_model.isOK()) return; + + size_t channels = m_model.getChannelCount(); + size_t frames = m_model.getFrameCount(); + + Range *range = new Range[2 * channels]; + size_t count[2]; + count[0] = count[1] = 0; + + while (frame < frames) { + + m_model.m_reader->getInterleavedFrames(frame, readBlockSize, block); + + for (size_t i = 0; i < readBlockSize; ++i) { + + for (size_t ch = 0; ch < size_t(channels); ++ch) { + + size_t index = channels * i + ch; + if (index >= block.size()) continue; + float sample = block[index]; + + for (size_t ct = 0; ct < 2; ++ct) { + + size_t rangeIndex = ch * 2 + ct; + + if (sample > range[rangeIndex].max || count[ct] == 0) { + range[rangeIndex].max = sample; + } + if (sample < range[rangeIndex].min || count[ct] == 0) { + range[rangeIndex].min = sample; + } + range[rangeIndex].absmean += fabsf(sample); + } + } + + QMutexLocker locker(&m_model.m_mutex); + for (size_t ct = 0; ct < 2; ++ct) { + if (++count[ct] == cacheBlockSize[ct]) { + for (size_t ch = 0; ch < size_t(channels); ++ch) { + size_t rangeIndex = ch * 2 + ct; + range[rangeIndex].absmean /= count[ct]; + m_model.m_cache[ct].push_back(range[rangeIndex]); + range[rangeIndex] = Range(); + } + count[ct] = 0; + } + } + + ++frame; + } + + if (m_model.m_exiting) break; + + m_fillExtent = frame; + } + + QMutexLocker locker(&m_model.m_mutex); + for (size_t ct = 0; ct < 2; ++ct) { + if (count[ct] > 0) { + for (size_t ch = 0; ch < size_t(channels); ++ch) { + size_t rangeIndex = ch * 2 + ct; + range[rangeIndex].absmean /= count[ct]; + m_model.m_cache[ct].push_back(range[rangeIndex]); + range[rangeIndex] = Range(); + } + count[ct] = 0; + } + + const Range &rr = *m_model.m_cache[ct].begin(); + MUNLOCK(&rr, m_model.m_cache[ct].capacity() * sizeof(Range)); + } + + delete[] range; + + m_fillExtent = frames; + +// for (size_t ct = 0; ct < 2; ++ct) { +// cerr << "Cache type " << ct << " now contains " << m_model.m_cache[ct].size() << " ranges" << endl; +// } +} + +QString +WaveFileModel::toXmlString(QString indent, + QString extraAttributes) const +{ + return Model::toXmlString(indent, + QString("type=\"wavefile\" file=\"%1\" %2") + .arg(m_path).arg(extraAttributes)); +} + + +#ifdef INCLUDE_MOCFILES +#ifdef INCLUDE_MOCFILES +#include "WaveFileModel.moc.cpp" +#endif +#endif + diff -r f90fad823cea -r 3a13b0d4934e data/model/WaveFileModel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/WaveFileModel.h Mon Jul 31 11:44:37 2006 +0000 @@ -0,0 +1,102 @@ +/* -*- 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 _WAVE_FILE_MODEL_H_ +#define _WAVE_FILE_MODEL_H_ + +#include "Thread.h" +#include +#include + +#include "RangeSummarisableTimeValueModel.h" +#include "PowerOfSqrtTwoZoomConstraint.h" + +#include + +class AudioFileReader; + +class WaveFileModel : public RangeSummarisableTimeValueModel, + virtual public PowerOfSqrtTwoZoomConstraint +{ + Q_OBJECT + +public: + WaveFileModel(QString path); + ~WaveFileModel(); + + bool isOK() const; + bool isReady(int *) const; + + size_t getFrameCount() const; + size_t getChannelCount() const; + size_t getSampleRate() const; + + virtual Model *clone() const; + + float getValueMinimum() const { return -1.0f; } + float getValueMaximum() const { return 1.0f; } + + virtual size_t getStartFrame() const { return 0; } + virtual size_t getEndFrame() const { return getFrameCount(); } + + virtual size_t getValues(int channel, size_t start, size_t end, + float *buffer) const; + + virtual size_t getValues(int channel, size_t start, size_t end, + double *buffer) const; + + virtual RangeBlock getRanges(size_t channel, size_t start, size_t end, + size_t &blockSize) const; + + virtual Range getRange(size_t channel, size_t start, size_t end) const; + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const; + +protected slots: + void fillTimerTimedOut(); + void cacheFilled(); + +protected: + void initialize(); + + class RangeCacheFillThread : public Thread + { + public: + RangeCacheFillThread(WaveFileModel &model) : + m_model(model), m_fillExtent(0) { } + + size_t getFillExtent() const { return m_fillExtent; } + virtual void run(); + + protected: + WaveFileModel &m_model; + size_t m_fillExtent; + }; + + void fillCache(); + + QString m_path; + AudioFileReader *m_reader; + + RangeBlock m_cache[2]; // interleaved at two base resolutions + mutable QMutex m_mutex; + RangeCacheFillThread *m_fillThread; + QTimer *m_updateTimer; + size_t m_lastFillExtent; + bool m_exiting; +}; + +#endif