Mercurial > hg > svgui
changeset 277:8acd30ed735c
* Fix up and simplify the LayerTreeModel, removing a horrible memory leak
* Move phase-unwrapped frequency estimation from SpectrogramLayer to
FFTDataServer
* Make the spectrum show peak phase-unwrapped frequencies as well (still
needs work)
* Start adding piano keyboard horizontal scale to spectrum
* Debug output for id3 tags
author | Chris Cannam |
---|---|
date | Tue, 03 Jul 2007 12:46:18 +0000 |
parents | 21c7152ddba8 |
children | a078aa2932cc |
files | layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/SpectrumLayer.cpp layer/SpectrumLayer.h view/Pane.cpp view/Pane.h view/PaneStack.cpp view/PaneStack.h view/View.h widgets/LayerTree.cpp |
diffstat | 10 files changed, 220 insertions(+), 235 deletions(-) [+] |
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp Mon Jul 02 14:58:34 2007 +0000 +++ b/layer/SpectrogramLayer.cpp Tue Jul 03 12:46:18 2007 +0000 @@ -1064,48 +1064,6 @@ } } -float -SpectrogramLayer::calculateFrequency(size_t bin, - size_t windowSize, - size_t windowIncrement, - size_t sampleRate, - float oldPhase, - float newPhase, - bool &steadyState) -{ - // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec. - // At hopsize h and sample rate sr, one hop happens in h/sr sec. - // At window size w, for bin b, f is b*sr/w. - // thus 2pi phase shift happens in w/(b*sr) sec. - // We need to know what phase shift we expect from h/sr sec. - // -> 2pi * ((h/sr) / (w/(b*sr))) - // = 2pi * ((h * b * sr) / (w * sr)) - // = 2pi * (h * b) / w. - - float frequency = (float(bin) * sampleRate) / windowSize; - - float expectedPhase = - oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize; - - float phaseError = princargf(newPhase - expectedPhase); - - if (fabsf(phaseError) < (1.1f * (windowIncrement * M_PI) / windowSize)) { - - // The new frequency estimate based on the phase error - // resulting from assuming the "native" frequency of this bin - - float newFrequency = - (sampleRate * (expectedPhase + phaseError - oldPhase)) / - (2 * M_PI * windowIncrement); - - steadyState = true; - return newFrequency; - } - - steadyState = false; - return frequency; -} - unsigned char SpectrogramLayer::getDisplayValue(View *v, float input) const { @@ -1363,6 +1321,10 @@ float &adjFreqMin, float &adjFreqMax) const { + if (!m_model || !m_model->isOK() || !m_model->isReady()) { + return false; + } + FFTModel *fft = getFFTModel(v); if (!fft) return false; @@ -1407,13 +1369,7 @@ if (s < int(fft->getWidth()) - 1) { - freq = calculateFrequency(q, - windowSize, - windowIncrement, - sr, - fft->getPhaseAt(s, q), - fft->getPhaseAt(s+1, q), - steady); + fft->estimateStableFrequency(s, q, freq); if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; @@ -1435,6 +1391,10 @@ float &min, float &max, float &phaseMin, float &phaseMax) const { + if (!m_model || !m_model->isOK() || !m_model->isReady()) { + return false; + } + float q0 = 0, q1 = 0; if (!getYBinRange(v, y, q0, q1)) return false; @@ -2133,14 +2093,8 @@ if (m_binDisplay == PeakFrequencies && s < int(fft->getWidth()) - 1) { - bool steady = false; - float f = calculateFrequency(q, - m_windowSize, - increment, - sr, - fft->getPhaseAt(s, q), - fft->getPhaseAt(s+1, q), - steady); + float f = 0; + fft->estimateStableFrequency(s, q, f); y0 = y1 = v->getYForFrequency (f, displayMinFreq, displayMaxFreq, logarithmic); @@ -2855,6 +2809,8 @@ if (m_frequencyScale == LogFrequencyScale) { + // piano keyboard + paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h); float minf = getEffectiveMinFrequency(); @@ -2879,11 +2835,9 @@ // C# -- fill the C from here if (ppy - y > 2) { paint.fillRect(w - pkw, -// y - (py - y) / 2 - (py - y) / 4, y, pkw, (py + ppy) / 2 - y, -// py - y + 1, Qt::gray); } }
--- a/layer/SpectrogramLayer.h Mon Jul 02 14:58:34 2007 +0000 +++ b/layer/SpectrogramLayer.h Tue Jul 03 12:46:18 2007 +0000 @@ -296,14 +296,6 @@ void initialisePalette(); void rotatePalette(int distance); - static float calculateFrequency(size_t bin, - size_t windowSize, - size_t windowIncrement, - size_t sampleRate, - float previousPhase, - float currentPhase, - bool &steadyState); - unsigned char getDisplayValue(View *v, float input) const; float getInputForDisplayValue(unsigned char uc) const;
--- a/layer/SpectrumLayer.cpp Mon Jul 02 14:58:34 2007 +0000 +++ b/layer/SpectrumLayer.cpp Tue Jul 03 12:46:18 2007 +0000 @@ -21,6 +21,7 @@ #include "base/AudioLevel.h" #include "base/Preferences.h" #include "base/RangeMapper.h" +#include "base/Pitch.h" #include "ColourMapper.h" #include <QPainter> @@ -53,6 +54,14 @@ { if (m_originModel == model) return; m_originModel = model; + + if (m_sliceableModel) { + const Model *oldModel = m_sliceableModel; + setSliceableModel(0); + // surprised I'm allowed to delete a const pointer -- may be a + // source of future compiler rejection? + delete oldModel; + } //!!! setupFFT(); } @@ -549,8 +558,112 @@ const_cast<SpectrumLayer *>(this)->setupFFT(); //ugh m_newFFTNeeded = false; } + + FFTModel *fft = dynamic_cast<FFTModel *> + (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); + + float thresh = powf(10, -8) / m_gain; // -80dB + + int xorigin = getVerticalScaleWidth(v, paint) + 1; + int w = v->width() - xorigin - 1; + + if (fft) { + + // draw peak lines + //!!! should be optional + + size_t col = v->getCentreFrame() / fft->getResolution(); + + paint.save(); + paint.setRenderHint(QPainter::Antialiasing, false); + paint.setPen(QColor(160, 160, 160)); //!!! + + ColourMapper mapper(m_colourMap, 0, 1); + + BiasCurve curve; + getBiasCurve(curve); + size_t cs = curve.size(); + + for (size_t bin = 0; bin < fft->getHeight(); ++bin) { + + if (!fft->isLocalPeak(col, bin)) continue; + if (!fft->isOverThreshold(col, bin, thresh)) continue; + + float freq = 0; + bool haveFreq = fft->estimateStableFrequency(col, bin, freq); + if (!haveFreq) continue; + + int x = lrintf(getXForFrequency(freq, w)); + + float value = m_sliceableModel->getValueAt(col, bin); + if (bin < cs) value *= curve[bin]; + float norm = 0.f; + float y = getYForValue(value, v, norm); // don't need y, need norm + + paint.setPen(mapper.map(norm)); + paint.drawLine(xorigin + x, 0, xorigin + x, v->height()); + } + + paint.restore(); + } SliceLayer::paint(v, paint, rect); + + if (m_binScale == LogBins) { + + int pkh = 10; + int h = v->height(); + + // piano keyboard + //!!! should be in a new paintHorizontalScale()? + + paint.drawLine(xorigin, h - pkh - 1, w, h - pkh - 1); + + int px = xorigin, ppx = xorigin; +// paint.setBrush(paint.pen().color()); + + for (int i = 0; i < 128; ++i) { + + float f = Pitch::getFrequencyForPitch(i); + int x = lrintf(getXForFrequency(f, w)); + + if (x < 0) break; + if (x + xorigin > w) { + continue; + } + + x += xorigin; + + int n = (i % 12); + + if (n == 1) { + // C# -- fill the C from here + if (x - ppx > 2) { + paint.fillRect(x, + h - pkh, + x - (px + ppx) / 2, + pkh, + Qt::gray); + } + } + + if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { + // black notes + paint.drawLine(x, h - pkh, x, h); + int rw = ((px - x) / 4) * 2; + if (rw < 2) rw = 2; + paint.drawRect(x - (px-x)/4, h - pkh, rw, pkh/2); + } else if (n == 0 || n == 5) { + // C, F + if (px < w) { + paint.drawLine((x + px) / 2, h - pkh, (x + px) / 2, h); + } + } + + ppx = px; + px = x; + } + } } void
--- a/layer/SpectrumLayer.h Mon Jul 02 14:58:34 2007 +0000 +++ b/layer/SpectrumLayer.h Tue Jul 03 12:46:18 2007 +0000 @@ -46,6 +46,10 @@ virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual VerticalPosition getPreferredFrameCountPosition() const { + return PositionTop; + } + virtual PropertyList getProperties() const; virtual QString getPropertyLabel(const PropertyName &) const; virtual PropertyType getPropertyType(const PropertyName &) const;
--- a/view/Pane.cpp Mon Jul 02 14:58:34 2007 +0000 +++ b/view/Pane.cpp Tue Jul 03 12:46:18 2007 +0000 @@ -397,14 +397,18 @@ } Layer *topLayer = getTopLayer(); + bool haveSomeTimeXAxis = false; const Model *waveformModel = 0; // just for reporting purposes for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { --vi; + if (!haveSomeTimeXAxis && (*vi)->hasTimeXAxis()) { + haveSomeTimeXAxis = true; + } if (dynamic_cast<WaveformLayer *>(*vi)) { waveformModel = (*vi)->getModel(); - break; } + if (waveformModel && haveSomeTimeXAxis) break; } m_scaleWidth = 0; @@ -423,7 +427,7 @@ if (m_centreLineVisible && m_manager && m_manager->shouldShowCentreLine()) { - drawCentreLine(sampleRate, paint); + drawCentreLine(sampleRate, paint, !haveSomeTimeXAxis); } paint.setPen(QColor(50, 50, 50)); @@ -628,7 +632,7 @@ } void -Pane::drawCentreLine(int sampleRate, QPainter &paint) +Pane::drawCentreLine(int sampleRate, QPainter &paint, bool omitLine) { int fontHeight = paint.fontMetrics().height(); int fontAscent = paint.fontMetrics().ascent(); @@ -637,13 +641,17 @@ if (!hasLightBackground()) { c = QColor(240, 240, 240); } + paint.setPen(c); int x = width() / 2; - paint.drawLine(x, 0, x, height() - 1); - paint.drawLine(x-1, 1, x+1, 1); - paint.drawLine(x-2, 0, x+2, 0); - paint.drawLine(x-1, height() - 2, x+1, height() - 2); - paint.drawLine(x-2, height() - 1, x+2, height() - 1); + + if (!omitLine) { + paint.drawLine(x, 0, x, height() - 1); + paint.drawLine(x-1, 1, x+1, 1); + paint.drawLine(x-2, 0, x+2, 0); + paint.drawLine(x-1, height() - 2, x+1, height() - 2); + paint.drawLine(x-2, height() - 1, x+2, height() - 1); + } paint.setPen(QColor(50, 50, 50));
--- a/view/Pane.h Mon Jul 02 14:58:34 2007 +0000 +++ b/view/Pane.h Tue Jul 03 12:46:18 2007 +0000 @@ -92,7 +92,7 @@ void drawVerticalScale(QRect r, Layer *, QPainter &); void drawFeatureDescription(Layer *, QPainter &); - void drawCentreLine(int, QPainter &); + void drawCentreLine(int, QPainter &, bool omitLine); void drawDurationAndRate(QRect, const Model *, int, QPainter &); void drawLayerNames(QRect, QPainter &); void drawEditingSelection(QPainter &);
--- a/view/PaneStack.cpp Mon Jul 02 14:58:34 2007 +0000 +++ b/view/PaneStack.cpp Tue Jul 03 12:46:18 2007 +0000 @@ -172,7 +172,22 @@ Pane * PaneStack::getPane(int n) { - return m_panes[n].pane; + if (n < m_panes.size()) { + return m_panes[n].pane; + } else { + return 0; + } +} + +int +PaneStack::getPaneIndex(Pane *pane) +{ + for (int i = 0; i < getPaneCount(); ++i) { + if (pane == getPane(i)) { + return i; + } + } + return -1; } Pane *
--- a/view/PaneStack.h Mon Jul 02 14:58:34 2007 +0000 +++ b/view/PaneStack.h Tue Jul 03 12:46:18 2007 +0000 @@ -43,6 +43,7 @@ int getPaneCount() const; // Returns only count of visible panes Pane *getPane(int n); // Of visible panes; I own the returned value + int getPaneIndex(Pane *pane); // so getPane(index)==pane; -1 if absent void hidePane(Pane *pane); // Also removes pane from getPane/getPaneCount void showPane(Pane *pane); // Returns pane to getPane/getPaneCount
--- a/view/View.h Mon Jul 02 14:58:34 2007 +0000 +++ b/view/View.h Tue Jul 03 12:46:18 2007 +0000 @@ -150,7 +150,9 @@ * 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]; } + virtual Layer *getLayer(int n) { + if (n < m_layers.size()) return m_layers[n]; else return 0; + } /** * Return the top layer. This is the same as
--- a/widgets/LayerTree.cpp Mon Jul 02 14:58:34 2007 +0000 +++ b/widgets/LayerTree.cpp Tue Jul 03 12:46:18 2007 +0000 @@ -24,30 +24,6 @@ #include <iostream> -class ViewObjectAssoc : public QObject -{ -public: - ViewObjectAssoc(View *v, QObject *o) : - QObject(0), view(v), object(o) { - ++extantCount; - std::cerr << "ViewObjectAssoc (now " << extantCount << " extant)" - << std::endl; - } - - virtual ~ViewObjectAssoc() { - std::cerr << "~ViewObjectAssoc (now " << --extantCount << " extant)" - << std::endl; - } - - View *view; - QObject *object; - - static int extantCount; -}; - -int ViewObjectAssoc::extantCount = 0; - - LayerTreeModel::LayerTreeModel(PaneStack *stack, QObject *parent) : QAbstractItemModel(parent), m_stack(stack) @@ -64,43 +40,43 @@ LayerTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - if (role != Qt::DisplayRole) return QVariant(); - - std::cerr << "LayerTreeModel::data(" << &index << ", role " << role << ")" << std::endl; QObject *obj = static_cast<QObject *>(index.internalPointer()); - - PaneStack *paneStack = dynamic_cast<PaneStack *>(obj); - if (paneStack) { - std::cerr << "node is pane stack" << std::endl; - return QVariant("Pane stack"); + int row = index.row(), col = index.column(); + + Pane *pane = dynamic_cast<Pane *>(obj); + if (!pane) { + if (col == 0 && row < m_stack->getPaneCount()) { + switch (role) { + case Qt::DisplayRole: + return QVariant(QString("Pane %1").arg(row + 1)); + case Qt::DecorationRole: + return QVariant(QIcon(QString(":/icons/pane.png"))); + default: break; + } + } } - Pane *pane = dynamic_cast<Pane *>(obj); - if (pane) { - // need index of pane in pane stack - for (int i = 0; i < m_stack->getPaneCount(); ++i) { - if (pane == m_stack->getPane(i)) { - std::cerr << "node is pane " << i << std::endl; - return QVariant(QString("Pane %1").arg(i + 1)); - } - } - return QVariant(); - } - - ViewObjectAssoc *assoc = dynamic_cast<ViewObjectAssoc *>(obj); - if (assoc) { - std::cerr << "node is assoc" << std::endl; - Layer *layer = dynamic_cast<Layer *>(assoc->object); - if (layer) { - std::cerr << "with layer" << std::endl; - return QVariant(layer->objectName()); - } - Model *model = dynamic_cast<Model *>(assoc->object); - if (model) { - std::cerr << "with model" << std::endl; - return QVariant(model->objectName()); - } + if (pane && pane->getLayerCount() > row) { + Layer *layer = pane->getLayer(row); + if (layer) { + if (col == 0) { + switch (role) { + case Qt::DisplayRole: + return QVariant(layer->objectName()); + case Qt::DecorationRole: + return QVariant + (QIcon(QString(":/icons/%1.png") + .arg(layer->getPropertyContainerIconName()))); + default: break; + } + } else if (col == 1) { + Model *model = layer->getModel(); + if (model && role == Qt::DisplayRole) { + return QVariant(model->objectName()); + } + } + } } return QVariant(); @@ -129,145 +105,65 @@ QModelIndex LayerTreeModel::index(int row, int column, const QModelIndex &parent) const { - std::cerr << "LayerTreeModel::index(" << row << ", " << column << ", " - << &parent << ")" << std::endl; + // cell for a pane contains row, column, pane stack + // -> its parent is the invalid cell + + // cell for a layer contains row, column, pane + // -> its parent is row, column, pane stack (which identify the pane) if (!parent.isValid()) { - // this is the pane stack - std::cerr << "parent invalid, returning pane stack as root" << std::endl; - if (column > 0) return QModelIndex(); + if (row >= m_stack->getPaneCount() || column > 0) return QModelIndex(); return createIndex(row, column, m_stack); } QObject *obj = static_cast<QObject *>(parent.internalPointer()); - - PaneStack *paneStack = dynamic_cast<PaneStack *>(obj); - if (paneStack) { - if (column > 0) return QModelIndex(); - if (paneStack == m_stack && row < m_stack->getPaneCount()) { - std::cerr << "parent is pane stack, returning a pane" << std::endl; - return createIndex(row, column, m_stack->getPane(row)); - } - std::cerr << "parent is wrong pane stack, returning nothing" << std::endl; - return QModelIndex(); + + if (obj == m_stack) { + Pane *pane = m_stack->getPane(parent.row()); + if (!pane || parent.column() > 0) return QModelIndex(); + return createIndex(row, column, pane); } - Pane *pane = dynamic_cast<Pane *>(obj); - if (pane) { - std::cerr << "parent is pane" << std::endl; - if (row < pane->getLayerCount()) { - Layer *layer = pane->getLayer(row); - if (column == 0) { - std::cerr << "parent is pane, returning layer" << std::endl; - ViewObjectAssoc *assoc = new ViewObjectAssoc -// (const_cast<LayerTreeModel *>(this), pane, layer); - (pane, layer); - return createIndex(row, column, assoc); - } else { - std::cerr << "parent is pane, column != 0, returning model" << std::endl; - ViewObjectAssoc *assoc = new ViewObjectAssoc -// (const_cast<LayerTreeModel *>(this), pane, layer->getModel()); - (pane, layer->getModel()); - return createIndex(row, column, assoc); - } - } - } - - std::cerr << "unknown parent, returning nothing" << std::endl; return QModelIndex(); } QModelIndex LayerTreeModel::parent(const QModelIndex &index) const { - std::cerr << "LayerTreeModel::parent(" << &index << ")" << std::endl; - QObject *obj = static_cast<QObject *>(index.internalPointer()); - - PaneStack *paneStack = dynamic_cast<PaneStack *>(obj); - if (paneStack) { - std::cerr << "node is pane stack, returning no parent" << std::endl; - return QModelIndex(); - } Pane *pane = dynamic_cast<Pane *>(obj); if (pane) { - std::cerr << "node is pane, returning pane stack as parent" << std::endl; - return createIndex(0, 0, m_stack); + int index = m_stack->getPaneIndex(pane); + if (index >= 0) return createIndex(index, 0, m_stack); } - ViewObjectAssoc *assoc = dynamic_cast<ViewObjectAssoc *>(obj); - if (assoc) { - View *view = assoc->view; - Pane *pane = dynamic_cast<Pane *>(view); - if (pane) { - // need index of pane in pane stack - for (int i = 0; i < m_stack->getPaneCount(); ++i) { - if (pane == m_stack->getPane(i)) { - std::cerr << "node is assoc, returning pane " << i << " as parent" << std::endl; - return createIndex(i, 0, pane); - } - } - } - std::cerr << "node is assoc, but no parent found" << std::endl; - return QModelIndex(); - } - - std::cerr << "unknown node" << std::endl; return QModelIndex(); } int LayerTreeModel::rowCount(const QModelIndex &parent) const { - std::cerr << "LayerTreeModel::rowCount(" << &parent << ")" << std::endl; - - if (!parent.isValid()) { - std::cerr << "parent invalid, returning 1 for the pane stack" << std::endl; - return 1; // the pane stack - } + if (!parent.isValid()) return m_stack->getPaneCount(); QObject *obj = static_cast<QObject *>(parent.internalPointer()); - PaneStack *paneStack = dynamic_cast<PaneStack *>(obj); - if (paneStack) { - if (paneStack == m_stack) { - std::cerr << "parent is pane stack, returning " - << m_stack->getPaneCount() << " panes" << std::endl; - return m_stack->getPaneCount(); - } else { - return 0; - } - } - - Pane *pane = dynamic_cast<Pane *>(obj); - if (pane) { - std::cerr << "parent is pane, returning " - << pane->getLayerCount() << " layers" << std::endl; - return pane->getLayerCount(); + if (obj == m_stack) { + Pane *pane = m_stack->getPane(parent.row()); + if (!pane || parent.column() > 0) return 0; + return pane->getLayerCount(); } - std::cerr << "parent unknown, returning 0" << std::endl; return 0; } int LayerTreeModel::columnCount(const QModelIndex &parent) const { - if (!parent.isValid()) { - std::cerr << "LayerTreeModel::columnCount: parent invalid, returning 2" << std::endl; - return 2; - } + if (!parent.isValid()) return 2; QObject *obj = static_cast<QObject *>(parent.internalPointer()); - - Pane *pane = dynamic_cast<Pane *>(obj); - if (pane) { - std::cerr << "LayerTreeModel::columnCount: pane, returning 2" << std::endl; - return 2; // layer and model - } - - std::cerr << "LayerTreeModel::columnCount: returning 1" << std::endl; + if (obj == m_stack) return 2; return 1; }