lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: This file copyright 2006 Chris Cannam and QMUL. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@191: #include lbajardsilogic@191: lbajardsilogic@191: #include lbajardsilogic@191: #include lbajardsilogic@191: lbajardsilogic@191: #include "system/System.h" lbajardsilogic@191: lbajardsilogic@0: #include "SpectrogramLayer.h" lbajardsilogic@0: lbajardsilogic@0: #include "view/View.h" lbajardsilogic@0: #include "base/Profiler.h" lbajardsilogic@0: #include "base/AudioLevel.h" lbajardsilogic@0: #include "base/Window.h" lbajardsilogic@0: #include "base/Pitch.h" lbajardsilogic@0: #include "base/Preferences.h" lbajardsilogic@0: #include "base/RangeMapper.h" lbajardsilogic@0: #include "ColourMapper.h" lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: //#define DEBUG_SPECTROGRAM_REPAINT 1 lbajardsilogic@0: lbajardsilogic@0: SpectrogramLayer::SpectrogramLayer(Configuration config) : lbajardsilogic@0: m_model(0), lbajardsilogic@0: m_channel(0), lbajardsilogic@0: m_windowSize(1024), lbajardsilogic@0: m_windowType(HanningWindow), lbajardsilogic@0: m_windowHopLevel(2), lbajardsilogic@0: m_zeroPadLevel(0), lbajardsilogic@0: m_fftSize(1024), lbajardsilogic@0: m_gain(1.0), lbajardsilogic@0: m_initialGain(1.0), lbajardsilogic@0: m_threshold(0.0), lbajardsilogic@0: m_initialThreshold(0.0), lbajardsilogic@0: m_colourRotation(0), lbajardsilogic@0: m_initialRotation(0), lbajardsilogic@0: m_minFrequency(10), lbajardsilogic@0: m_maxFrequency(8000), lbajardsilogic@0: m_initialMaxFrequency(8000), lbajardsilogic@0: m_colourScale(dBColourScale), lbajardsilogic@0: m_colourMap(0), lbajardsilogic@0: m_frequencyScale(LinearFrequencyScale), lbajardsilogic@0: m_binDisplay(AllBins), lbajardsilogic@0: m_normalizeColumns(false), lbajardsilogic@0: m_normalizeVisibleArea(false), lbajardsilogic@0: m_lastEmittedZoomStep(-1), lbajardsilogic@0: m_lastPaintBlockWidth(0), lbajardsilogic@0: m_updateTimer(0), lbajardsilogic@0: m_candidateFillStartFrame(0), lbajardsilogic@0: m_exiting(false), lbajardsilogic@0: m_sliceableModel(0) lbajardsilogic@0: { lbajardsilogic@0: if (config == FullRangeDb) { lbajardsilogic@0: m_initialMaxFrequency = 0; lbajardsilogic@0: setMaxFrequency(0); lbajardsilogic@0: } else if (config == MelodicRange) { lbajardsilogic@0: setWindowSize(8192); lbajardsilogic@0: setWindowHopLevel(4); lbajardsilogic@0: m_initialMaxFrequency = 1500; lbajardsilogic@0: setMaxFrequency(1500); lbajardsilogic@0: setMinFrequency(40); lbajardsilogic@0: setColourScale(LinearColourScale); lbajardsilogic@0: setColourMap(ColourMapper::Sunset); lbajardsilogic@0: setFrequencyScale(LogFrequencyScale); lbajardsilogic@0: // setGain(20); lbajardsilogic@0: } else if (config == MelodicPeaks) { lbajardsilogic@0: setWindowSize(4096); lbajardsilogic@0: setWindowHopLevel(5); lbajardsilogic@0: m_initialMaxFrequency = 2000; lbajardsilogic@0: setMaxFrequency(2000); lbajardsilogic@0: setMinFrequency(40); lbajardsilogic@0: setFrequencyScale(LogFrequencyScale); lbajardsilogic@0: setColourScale(LinearColourScale); lbajardsilogic@0: setBinDisplay(PeakFrequencies); lbajardsilogic@0: setNormalizeColumns(true); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Preferences *prefs = Preferences::getInstance(); lbajardsilogic@0: connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), lbajardsilogic@0: this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); lbajardsilogic@0: setWindowType(prefs->getWindowType()); lbajardsilogic@0: lbajardsilogic@0: initialisePalette(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SpectrogramLayer::~SpectrogramLayer() lbajardsilogic@0: { lbajardsilogic@0: delete m_updateTimer; lbajardsilogic@0: m_updateTimer = 0; lbajardsilogic@0: lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setModel(const DenseTimeValueModel *model) lbajardsilogic@0: { lbajardsilogic@0: // std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (model == m_model) return; lbajardsilogic@0: lbajardsilogic@0: m_model = model; lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: if (!m_model || !m_model->isOK()) return; lbajardsilogic@0: lbajardsilogic@0: connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); lbajardsilogic@0: connect(m_model, SIGNAL(modelChanged(size_t, size_t)), lbajardsilogic@0: this, SIGNAL(modelChanged(size_t, size_t))); lbajardsilogic@0: lbajardsilogic@0: connect(m_model, SIGNAL(completionChanged()), lbajardsilogic@0: this, SIGNAL(modelCompletionChanged())); lbajardsilogic@0: lbajardsilogic@0: connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); lbajardsilogic@0: connect(m_model, SIGNAL(modelChanged(size_t, size_t)), lbajardsilogic@0: this, SLOT(cacheInvalid(size_t, size_t))); lbajardsilogic@0: lbajardsilogic@0: emit modelReplaced(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Layer::PropertyList lbajardsilogic@0: SpectrogramLayer::getProperties() const lbajardsilogic@0: { lbajardsilogic@0: PropertyList list; lbajardsilogic@0: list.push_back("Colour"); lbajardsilogic@0: list.push_back("Colour Scale"); lbajardsilogic@0: list.push_back("Window Size"); lbajardsilogic@0: list.push_back("Window Increment"); lbajardsilogic@0: list.push_back("Normalize Columns"); lbajardsilogic@0: list.push_back("Normalize Visible Area"); lbajardsilogic@0: list.push_back("Bin Display"); lbajardsilogic@0: list.push_back("Threshold"); lbajardsilogic@0: list.push_back("Gain"); lbajardsilogic@0: list.push_back("Colour Rotation"); lbajardsilogic@0: // list.push_back("Min Frequency"); lbajardsilogic@0: // list.push_back("Max Frequency"); lbajardsilogic@0: list.push_back("Frequency Scale"); lbajardsilogic@0: //// list.push_back("Zero Padding"); lbajardsilogic@0: return list; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrogramLayer::getPropertyLabel(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Colour") return tr("Colour"); lbajardsilogic@0: if (name == "Colour Scale") return tr("Colour Scale"); lbajardsilogic@0: if (name == "Window Size") return tr("Window Size"); lbajardsilogic@0: if (name == "Window Increment") return tr("Window Overlap"); lbajardsilogic@0: if (name == "Normalize Columns") return tr("Normalize Columns"); lbajardsilogic@0: if (name == "Normalize Visible Area") return tr("Normalize Visible Area"); lbajardsilogic@0: if (name == "Bin Display") return tr("Bin Display"); lbajardsilogic@0: if (name == "Threshold") return tr("Threshold"); lbajardsilogic@0: if (name == "Gain") return tr("Gain"); lbajardsilogic@0: if (name == "Colour Rotation") return tr("Colour Rotation"); lbajardsilogic@0: if (name == "Min Frequency") return tr("Min Frequency"); lbajardsilogic@0: if (name == "Max Frequency") return tr("Max Frequency"); lbajardsilogic@0: if (name == "Frequency Scale") return tr("Frequency Scale"); lbajardsilogic@0: if (name == "Zero Padding") return tr("Smoothing"); lbajardsilogic@0: return ""; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Layer::PropertyType lbajardsilogic@0: SpectrogramLayer::getPropertyType(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Gain") return RangeProperty; lbajardsilogic@0: if (name == "Colour Rotation") return RangeProperty; lbajardsilogic@0: if (name == "Normalize Columns") return ToggleProperty; lbajardsilogic@0: if (name == "Normalize Visible Area") return ToggleProperty; lbajardsilogic@0: if (name == "Threshold") return RangeProperty; lbajardsilogic@0: if (name == "Zero Padding") return ToggleProperty; lbajardsilogic@0: return ValueProperty; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Bin Display" || lbajardsilogic@0: name == "Frequency Scale") return tr("Bins"); lbajardsilogic@0: if (name == "Window Size" || lbajardsilogic@0: name == "Window Increment" || lbajardsilogic@0: name == "Zero Padding") return tr("Window"); lbajardsilogic@0: if (name == "Colour" || lbajardsilogic@0: name == "Threshold" || lbajardsilogic@0: name == "Colour Rotation") return tr("Colour"); lbajardsilogic@0: if (name == "Normalize Columns" || lbajardsilogic@0: name == "Normalize Visible Area" || lbajardsilogic@0: name == "Gain" || lbajardsilogic@0: name == "Colour Scale") return tr("Scale"); lbajardsilogic@0: return QString(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, lbajardsilogic@0: int *min, int *max, int *deflt) const lbajardsilogic@0: { lbajardsilogic@0: int val = 0; lbajardsilogic@0: lbajardsilogic@0: int garbage0, garbage1, garbage2; lbajardsilogic@0: if (!min) min = &garbage0; lbajardsilogic@0: if (!max) max = &garbage1; lbajardsilogic@0: if (!deflt) deflt = &garbage2; lbajardsilogic@0: lbajardsilogic@0: if (name == "Gain") { lbajardsilogic@0: lbajardsilogic@0: *min = -50; lbajardsilogic@0: *max = 50; lbajardsilogic@0: lbajardsilogic@0: *deflt = lrintf(log10(m_initialGain) * 20.0);; lbajardsilogic@0: if (*deflt < *min) *deflt = *min; lbajardsilogic@0: if (*deflt > *max) *deflt = *max; lbajardsilogic@0: lbajardsilogic@0: val = lrintf(log10(m_gain) * 20.0); lbajardsilogic@0: if (val < *min) val = *min; lbajardsilogic@0: if (val > *max) val = *max; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Threshold") { lbajardsilogic@0: lbajardsilogic@0: *min = -50; lbajardsilogic@0: *max = 0; lbajardsilogic@0: lbajardsilogic@0: *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold)); lbajardsilogic@0: if (*deflt < *min) *deflt = *min; lbajardsilogic@0: if (*deflt > *max) *deflt = *max; lbajardsilogic@0: lbajardsilogic@0: val = lrintf(AudioLevel::multiplier_to_dB(m_threshold)); lbajardsilogic@0: if (val < *min) val = *min; lbajardsilogic@0: if (val > *max) val = *max; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Colour Rotation") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 256; lbajardsilogic@0: *deflt = m_initialRotation; lbajardsilogic@0: lbajardsilogic@0: val = m_colourRotation; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Colour Scale") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 4; lbajardsilogic@0: *deflt = int(dBColourScale); lbajardsilogic@0: lbajardsilogic@0: val = (int)m_colourScale; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Colour") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = ColourMapper::getColourMapCount() - 1; lbajardsilogic@0: *deflt = 0; lbajardsilogic@0: lbajardsilogic@0: val = m_colourMap; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Window Size") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 10; lbajardsilogic@0: *deflt = 5; lbajardsilogic@0: lbajardsilogic@0: val = 0; lbajardsilogic@0: int ws = m_windowSize; lbajardsilogic@0: while (ws > 32) { ws >>= 1; val ++; } lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Window Increment") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 5; lbajardsilogic@0: *deflt = 2; lbajardsilogic@0: lbajardsilogic@0: val = m_windowHopLevel; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Zero Padding") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 1; lbajardsilogic@0: *deflt = 0; lbajardsilogic@0: lbajardsilogic@0: val = m_zeroPadLevel > 0 ? 1 : 0; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Min Frequency") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 9; lbajardsilogic@0: *deflt = 1; lbajardsilogic@0: lbajardsilogic@0: switch (m_minFrequency) { lbajardsilogic@0: case 0: default: val = 0; break; lbajardsilogic@0: case 10: val = 1; break; lbajardsilogic@0: case 20: val = 2; break; lbajardsilogic@0: case 40: val = 3; break; lbajardsilogic@0: case 100: val = 4; break; lbajardsilogic@0: case 250: val = 5; break; lbajardsilogic@0: case 500: val = 6; break; lbajardsilogic@0: case 1000: val = 7; break; lbajardsilogic@0: case 4000: val = 8; break; lbajardsilogic@0: case 10000: val = 9; break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Max Frequency") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 9; lbajardsilogic@0: *deflt = 6; lbajardsilogic@0: lbajardsilogic@0: switch (m_maxFrequency) { lbajardsilogic@0: case 500: val = 0; break; lbajardsilogic@0: case 1000: val = 1; break; lbajardsilogic@0: case 1500: val = 2; break; lbajardsilogic@0: case 2000: val = 3; break; lbajardsilogic@0: case 4000: val = 4; break; lbajardsilogic@0: case 6000: val = 5; break; lbajardsilogic@0: case 8000: val = 6; break; lbajardsilogic@0: case 12000: val = 7; break; lbajardsilogic@0: case 16000: val = 8; break; lbajardsilogic@0: default: val = 9; break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Frequency Scale") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 1; lbajardsilogic@0: *deflt = int(LinearFrequencyScale); lbajardsilogic@0: val = (int)m_frequencyScale; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Bin Display") { lbajardsilogic@0: lbajardsilogic@0: *min = 0; lbajardsilogic@0: *max = 2; lbajardsilogic@0: *deflt = int(AllBins); lbajardsilogic@0: val = (int)m_binDisplay; lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Normalize Columns") { lbajardsilogic@0: lbajardsilogic@0: *deflt = 0; lbajardsilogic@0: val = (m_normalizeColumns ? 1 : 0); lbajardsilogic@0: lbajardsilogic@0: } else if (name == "Normalize Visible Area") { lbajardsilogic@0: lbajardsilogic@0: *deflt = 0; lbajardsilogic@0: val = (m_normalizeVisibleArea ? 1 : 0); lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: val = Layer::getPropertyRangeAndValue(name, min, max, deflt); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return val; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, lbajardsilogic@0: int value) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Colour") { lbajardsilogic@0: return ColourMapper::getColourMapName(value); lbajardsilogic@0: } lbajardsilogic@0: if (name == "Colour Scale") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: return tr("Linear"); lbajardsilogic@0: case 1: return tr("Meter"); lbajardsilogic@0: case 2: return tr("dBV^2"); lbajardsilogic@0: case 3: return tr("dBV"); lbajardsilogic@0: case 4: return tr("Phase"); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (name == "Window Size") { lbajardsilogic@0: return QString("%1").arg(32 << value); lbajardsilogic@0: } lbajardsilogic@0: if (name == "Window Increment") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: return tr("None"); lbajardsilogic@0: case 1: return tr("25 %"); lbajardsilogic@0: case 2: return tr("50 %"); lbajardsilogic@0: case 3: return tr("75 %"); lbajardsilogic@0: case 4: return tr("87.5 %"); lbajardsilogic@0: case 5: return tr("93.75 %"); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (name == "Zero Padding") { lbajardsilogic@0: if (value == 0) return tr("None"); lbajardsilogic@0: return QString("%1x").arg(value + 1); lbajardsilogic@0: } lbajardsilogic@0: if (name == "Min Frequency") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: return tr("No min"); lbajardsilogic@0: case 1: return tr("10 Hz"); lbajardsilogic@0: case 2: return tr("20 Hz"); lbajardsilogic@0: case 3: return tr("40 Hz"); lbajardsilogic@0: case 4: return tr("100 Hz"); lbajardsilogic@0: case 5: return tr("250 Hz"); lbajardsilogic@0: case 6: return tr("500 Hz"); lbajardsilogic@0: case 7: return tr("1 KHz"); lbajardsilogic@0: case 8: return tr("4 KHz"); lbajardsilogic@0: case 9: return tr("10 KHz"); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (name == "Max Frequency") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: return tr("500 Hz"); lbajardsilogic@0: case 1: return tr("1 KHz"); lbajardsilogic@0: case 2: return tr("1.5 KHz"); lbajardsilogic@0: case 3: return tr("2 KHz"); lbajardsilogic@0: case 4: return tr("4 KHz"); lbajardsilogic@0: case 5: return tr("6 KHz"); lbajardsilogic@0: case 6: return tr("8 KHz"); lbajardsilogic@0: case 7: return tr("12 KHz"); lbajardsilogic@0: case 8: return tr("16 KHz"); lbajardsilogic@0: case 9: return tr("No max"); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (name == "Frequency Scale") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: return tr("Linear"); lbajardsilogic@0: case 1: return tr("Log"); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (name == "Bin Display") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: return tr("All Bins"); lbajardsilogic@0: case 1: return tr("Peak Bins"); lbajardsilogic@0: case 2: return tr("Frequencies"); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: return tr(""); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: RangeMapper * lbajardsilogic@0: SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Gain") { lbajardsilogic@0: return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); lbajardsilogic@0: } lbajardsilogic@0: if (name == "Threshold") { lbajardsilogic@0: return new LinearRangeMapper(-50, 0, -50, 0, tr("dB")); lbajardsilogic@0: } lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setProperty(const PropertyName &name, int value) lbajardsilogic@0: { lbajardsilogic@0: if (name == "Gain") { lbajardsilogic@0: setGain(pow(10, float(value)/20.0)); lbajardsilogic@0: } else if (name == "Threshold") { lbajardsilogic@0: if (value == -50) setThreshold(0.0); lbajardsilogic@0: else setThreshold(AudioLevel::dB_to_multiplier(value)); lbajardsilogic@0: } else if (name == "Colour Rotation") { lbajardsilogic@0: setColourRotation(value); lbajardsilogic@0: } else if (name == "Colour") { lbajardsilogic@0: setColourMap(value); lbajardsilogic@0: } else if (name == "Window Size") { lbajardsilogic@0: setWindowSize(32 << value); lbajardsilogic@0: } else if (name == "Window Increment") { lbajardsilogic@0: setWindowHopLevel(value); lbajardsilogic@0: } else if (name == "Zero Padding") { lbajardsilogic@0: setZeroPadLevel(value > 0.1 ? 3 : 0); lbajardsilogic@0: } else if (name == "Min Frequency") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: setMinFrequency(0); break; lbajardsilogic@0: case 1: setMinFrequency(10); break; lbajardsilogic@0: case 2: setMinFrequency(20); break; lbajardsilogic@0: case 3: setMinFrequency(40); break; lbajardsilogic@0: case 4: setMinFrequency(100); break; lbajardsilogic@0: case 5: setMinFrequency(250); break; lbajardsilogic@0: case 6: setMinFrequency(500); break; lbajardsilogic@0: case 7: setMinFrequency(1000); break; lbajardsilogic@0: case 8: setMinFrequency(4000); break; lbajardsilogic@0: case 9: setMinFrequency(10000); break; lbajardsilogic@0: } lbajardsilogic@0: int vs = getCurrentVerticalZoomStep(); lbajardsilogic@0: if (vs != m_lastEmittedZoomStep) { lbajardsilogic@0: emit verticalZoomChanged(); lbajardsilogic@0: m_lastEmittedZoomStep = vs; lbajardsilogic@0: } lbajardsilogic@0: } else if (name == "Max Frequency") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: case 0: setMaxFrequency(500); break; lbajardsilogic@0: case 1: setMaxFrequency(1000); break; lbajardsilogic@0: case 2: setMaxFrequency(1500); break; lbajardsilogic@0: case 3: setMaxFrequency(2000); break; lbajardsilogic@0: case 4: setMaxFrequency(4000); break; lbajardsilogic@0: case 5: setMaxFrequency(6000); break; lbajardsilogic@0: case 6: setMaxFrequency(8000); break; lbajardsilogic@0: case 7: setMaxFrequency(12000); break; lbajardsilogic@0: case 8: setMaxFrequency(16000); break; lbajardsilogic@0: default: lbajardsilogic@0: case 9: setMaxFrequency(0); break; lbajardsilogic@0: } lbajardsilogic@0: int vs = getCurrentVerticalZoomStep(); lbajardsilogic@0: if (vs != m_lastEmittedZoomStep) { lbajardsilogic@0: emit verticalZoomChanged(); lbajardsilogic@0: m_lastEmittedZoomStep = vs; lbajardsilogic@0: } lbajardsilogic@0: } else if (name == "Colour Scale") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: setColourScale(LinearColourScale); break; lbajardsilogic@0: case 1: setColourScale(MeterColourScale); break; lbajardsilogic@0: case 2: setColourScale(dBSquaredColourScale); break; lbajardsilogic@0: case 3: setColourScale(dBColourScale); break; lbajardsilogic@0: case 4: setColourScale(PhaseColourScale); break; lbajardsilogic@0: } lbajardsilogic@0: } else if (name == "Frequency Scale") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: setFrequencyScale(LinearFrequencyScale); break; lbajardsilogic@0: case 1: setFrequencyScale(LogFrequencyScale); break; lbajardsilogic@0: } lbajardsilogic@0: } else if (name == "Bin Display") { lbajardsilogic@0: switch (value) { lbajardsilogic@0: default: lbajardsilogic@0: case 0: setBinDisplay(AllBins); break; lbajardsilogic@0: case 1: setBinDisplay(PeakBins); break; lbajardsilogic@0: case 2: setBinDisplay(PeakFrequencies); break; lbajardsilogic@0: } lbajardsilogic@0: } else if (name == "Normalize Columns") { lbajardsilogic@0: setNormalizeColumns(value ? true : false); lbajardsilogic@0: } else if (name == "Normalize Visible Area") { lbajardsilogic@0: setNormalizeVisibleArea(value ? true : false); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::invalidatePixmapCaches() lbajardsilogic@0: { lbajardsilogic@0: for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); lbajardsilogic@0: i != m_pixmapCaches.end(); ++i) { lbajardsilogic@0: i->second.validArea = QRect(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame) lbajardsilogic@0: { lbajardsilogic@0: for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); lbajardsilogic@0: i != m_pixmapCaches.end(); ++i) { lbajardsilogic@0: lbajardsilogic@0: //!!! when are views removed from the map? on setLayerDormant? lbajardsilogic@0: const View *v = i->first; lbajardsilogic@0: lbajardsilogic@0: if (startFrame < v->getEndFrame() && int(endFrame) >= v->getStartFrame()) { lbajardsilogic@0: i->second.validArea = QRect(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) lbajardsilogic@0: { lbajardsilogic@0: std::cerr << "SpectrogramLayer::preferenceChanged(" << name.toStdString() << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (name == "Window Type") { lbajardsilogic@0: setWindowType(Preferences::getInstance()->getWindowType()); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: if (name == "Spectrogram Smoothing") { lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: if (name == "Tuning Frequency") { lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setChannel(int ch) lbajardsilogic@0: { lbajardsilogic@0: if (m_channel == ch) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: m_channel = ch; lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getChannel() const lbajardsilogic@0: { lbajardsilogic@0: return m_channel; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setWindowSize(size_t ws) lbajardsilogic@0: { lbajardsilogic@0: if (m_windowSize == ws) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_windowSize = ws; lbajardsilogic@0: m_fftSize = ws * (m_zeroPadLevel + 1); lbajardsilogic@0: lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getWindowSize() const lbajardsilogic@0: { lbajardsilogic@0: return m_windowSize; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setWindowHopLevel(size_t v) lbajardsilogic@0: { lbajardsilogic@0: if (m_windowHopLevel == v) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_windowHopLevel = v; lbajardsilogic@0: lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: lbajardsilogic@0: // fillCache(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getWindowHopLevel() const lbajardsilogic@0: { lbajardsilogic@0: return m_windowHopLevel; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setZeroPadLevel(size_t v) lbajardsilogic@0: { lbajardsilogic@0: if (m_zeroPadLevel == v) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_zeroPadLevel = v; lbajardsilogic@0: m_fftSize = m_windowSize * (v + 1); lbajardsilogic@0: lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getZeroPadLevel() const lbajardsilogic@0: { lbajardsilogic@0: return m_zeroPadLevel; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setWindowType(WindowType w) lbajardsilogic@0: { lbajardsilogic@0: if (m_windowType == w) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_windowType = w; lbajardsilogic@0: lbajardsilogic@0: invalidateFFTModels(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: WindowType lbajardsilogic@0: SpectrogramLayer::getWindowType() const lbajardsilogic@0: { lbajardsilogic@0: return m_windowType; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setGain(float gain) lbajardsilogic@0: { lbajardsilogic@0: // std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " lbajardsilogic@0: // << m_gain << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (m_gain == gain) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_gain = gain; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getGain() const lbajardsilogic@0: { lbajardsilogic@0: return m_gain; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setThreshold(float threshold) lbajardsilogic@0: { lbajardsilogic@0: if (m_threshold == threshold) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_threshold = threshold; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getThreshold() const lbajardsilogic@0: { lbajardsilogic@0: return m_threshold; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setMinFrequency(size_t mf) lbajardsilogic@0: { lbajardsilogic@0: if (m_minFrequency == mf) return; lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer::setMinFrequency: " << mf << std::endl; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: lbajardsilogic@0: m_minFrequency = mf; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getMinFrequency() const lbajardsilogic@0: { lbajardsilogic@0: return m_minFrequency; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setMaxFrequency(size_t mf) lbajardsilogic@0: { lbajardsilogic@0: if (m_maxFrequency == mf) return; lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer::setMaxFrequency: " << mf << std::endl; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: lbajardsilogic@0: m_maxFrequency = mf; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getMaxFrequency() const lbajardsilogic@0: { lbajardsilogic@0: return m_maxFrequency; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setColourRotation(int r) lbajardsilogic@0: { lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: if (r < 0) r = 0; lbajardsilogic@0: if (r > 256) r = 256; lbajardsilogic@0: int distance = r - m_colourRotation; lbajardsilogic@0: lbajardsilogic@0: if (distance != 0) { lbajardsilogic@0: rotatePalette(-distance); lbajardsilogic@0: m_colourRotation = r; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setColourScale(ColourScale colourScale) lbajardsilogic@0: { lbajardsilogic@0: if (m_colourScale == colourScale) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_colourScale = colourScale; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SpectrogramLayer::ColourScale lbajardsilogic@0: SpectrogramLayer::getColourScale() const lbajardsilogic@0: { lbajardsilogic@0: return m_colourScale; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setColourMap(int map) lbajardsilogic@0: { lbajardsilogic@0: if (m_colourMap == map) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: lbajardsilogic@0: m_colourMap = map; lbajardsilogic@0: initialisePalette(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getColourMap() const lbajardsilogic@0: { lbajardsilogic@0: return m_colourMap; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) lbajardsilogic@0: { lbajardsilogic@0: if (m_frequencyScale == frequencyScale) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: m_frequencyScale = frequencyScale; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SpectrogramLayer::FrequencyScale lbajardsilogic@0: SpectrogramLayer::getFrequencyScale() const lbajardsilogic@0: { lbajardsilogic@0: return m_frequencyScale; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setBinDisplay(BinDisplay binDisplay) lbajardsilogic@0: { lbajardsilogic@0: if (m_binDisplay == binDisplay) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: m_binDisplay = binDisplay; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SpectrogramLayer::BinDisplay lbajardsilogic@0: SpectrogramLayer::getBinDisplay() const lbajardsilogic@0: { lbajardsilogic@0: return m_binDisplay; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setNormalizeColumns(bool n) lbajardsilogic@0: { lbajardsilogic@0: if (m_normalizeColumns == n) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: m_normalizeColumns = n; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getNormalizeColumns() const lbajardsilogic@0: { lbajardsilogic@0: return m_normalizeColumns; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setNormalizeVisibleArea(bool n) lbajardsilogic@0: { lbajardsilogic@0: if (m_normalizeVisibleArea == n) return; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: m_normalizeVisibleArea = n; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getNormalizeVisibleArea() const lbajardsilogic@0: { lbajardsilogic@0: return m_normalizeVisibleArea; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setLayerDormant(const View *v, bool dormant) lbajardsilogic@0: { lbajardsilogic@0: if (dormant) { lbajardsilogic@0: lbajardsilogic@0: if (isLayerDormant(v)) { lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Layer::setLayerDormant(v, true); lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: m_pixmapCaches.erase(v); lbajardsilogic@0: lbajardsilogic@0: if (m_fftModels.find(v) != m_fftModels.end()) { lbajardsilogic@0: lbajardsilogic@0: if (m_sliceableModel == m_fftModels[v].first) { lbajardsilogic@0: bool replaced = false; lbajardsilogic@0: for (ViewFFTMap::iterator i = m_fftModels.begin(); lbajardsilogic@0: i != m_fftModels.end(); ++i) { lbajardsilogic@0: if (i->second.first != m_sliceableModel) { lbajardsilogic@0: emit sliceableModelReplaced(m_sliceableModel, i->second.first); lbajardsilogic@0: replaced = true; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: delete m_fftModels[v].first; lbajardsilogic@0: m_fftModels.erase(v); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: Layer::setLayerDormant(v, false); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::cacheInvalid() lbajardsilogic@0: { lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::cacheInvalid(size_t, size_t) lbajardsilogic@0: { lbajardsilogic@0: // for now (or forever?) lbajardsilogic@0: cacheInvalid(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::fillTimerTimedOut() lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return; lbajardsilogic@0: lbajardsilogic@0: bool allDone = true; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: for (ViewFFTMap::iterator i = m_fftModels.begin(); lbajardsilogic@0: i != m_fftModels.end(); ++i) { lbajardsilogic@0: lbajardsilogic@0: const FFTModel *model = i->second.first; lbajardsilogic@0: size_t lastFill = i->second.second; lbajardsilogic@0: lbajardsilogic@0: if (model) { lbajardsilogic@0: lbajardsilogic@0: size_t fill = model->getFillExtent(); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (fill >= lastFill) { lbajardsilogic@0: if (fill >= m_model->getEndFrame() && lastFill > 0) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "complete!" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: i->second.second = -1; lbajardsilogic@0: emit modelChanged(); lbajardsilogic@0: lbajardsilogic@0: } else if (fill > lastFill) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: emitting modelChanged(" lbajardsilogic@0: << lastFill << "," << fill << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: invalidatePixmapCaches(lastFill, fill); lbajardsilogic@0: i->second.second = fill; lbajardsilogic@0: emit modelChanged(lastFill, fill); lbajardsilogic@0: } lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged(" lbajardsilogic@0: << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: i->second.second = fill; lbajardsilogic@0: emit modelChanged(m_model->getStartFrame(), m_model->getEndFrame()); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (i->second.second >= 0) { lbajardsilogic@0: allDone = false; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (allDone) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: all complete!" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: delete m_updateTimer; lbajardsilogic@0: m_updateTimer = 0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::hasLightBackground() const lbajardsilogic@0: { lbajardsilogic@0: return (m_colourMap == (int)ColourMapper::BlackOnWhite); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::initialisePalette() lbajardsilogic@0: { lbajardsilogic@0: int formerRotation = m_colourRotation; lbajardsilogic@0: lbajardsilogic@0: if (m_colourMap == (int)ColourMapper::BlackOnWhite) { lbajardsilogic@0: m_palette.setColour(NO_VALUE, Qt::white); lbajardsilogic@0: } else { lbajardsilogic@0: m_palette.setColour(NO_VALUE, Qt::black); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: ColourMapper mapper(m_colourMap, 1.f, 255.f); lbajardsilogic@0: lbajardsilogic@0: for (int pixel = 1; pixel < 256; ++pixel) { lbajardsilogic@0: m_palette.setColour(pixel, mapper.map(pixel)); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_crosshairColour = mapper.getContrastingColour(); lbajardsilogic@0: lbajardsilogic@0: m_colourRotation = 0; lbajardsilogic@0: rotatePalette(m_colourRotation - formerRotation); lbajardsilogic@0: m_colourRotation = formerRotation; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::rotatePalette(int distance) lbajardsilogic@0: { lbajardsilogic@0: QColor newPixels[256]; lbajardsilogic@0: lbajardsilogic@0: newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE); lbajardsilogic@0: lbajardsilogic@0: for (int pixel = 1; pixel < 256; ++pixel) { lbajardsilogic@0: int target = pixel + distance; lbajardsilogic@0: while (target < 1) target += 255; lbajardsilogic@0: while (target > 255) target -= 255; lbajardsilogic@0: newPixels[target] = m_palette.getColour(pixel); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (int pixel = 0; pixel < 256; ++pixel) { lbajardsilogic@0: m_palette.setColour(pixel, newPixels[pixel]); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::calculateFrequency(size_t bin, lbajardsilogic@0: size_t windowSize, lbajardsilogic@0: size_t windowIncrement, lbajardsilogic@0: size_t sampleRate, lbajardsilogic@0: float oldPhase, lbajardsilogic@0: float newPhase, lbajardsilogic@0: bool &steadyState) lbajardsilogic@0: { lbajardsilogic@0: // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec. lbajardsilogic@0: // At hopsize h and sample rate sr, one hop happens in h/sr sec. lbajardsilogic@0: // At window size w, for bin b, f is b*sr/w. lbajardsilogic@0: // thus 2pi phase shift happens in w/(b*sr) sec. lbajardsilogic@0: // We need to know what phase shift we expect from h/sr sec. lbajardsilogic@0: // -> 2pi * ((h/sr) / (w/(b*sr))) lbajardsilogic@0: // = 2pi * ((h * b * sr) / (w * sr)) lbajardsilogic@0: // = 2pi * (h * b) / w. lbajardsilogic@0: lbajardsilogic@0: float frequency = (float(bin) * sampleRate) / windowSize; lbajardsilogic@0: lbajardsilogic@0: float expectedPhase = lbajardsilogic@0: oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize; lbajardsilogic@0: lbajardsilogic@0: float phaseError = princargf(newPhase - expectedPhase); lbajardsilogic@0: lbajardsilogic@0: if (fabsf(phaseError) < (1.1f * (windowIncrement * M_PI) / windowSize)) { lbajardsilogic@0: lbajardsilogic@0: // The new frequency estimate based on the phase error lbajardsilogic@0: // resulting from assuming the "native" frequency of this bin lbajardsilogic@0: lbajardsilogic@0: float newFrequency = lbajardsilogic@0: (sampleRate * (expectedPhase + phaseError - oldPhase)) / lbajardsilogic@0: (2 * M_PI * windowIncrement); lbajardsilogic@0: lbajardsilogic@0: steadyState = true; lbajardsilogic@0: return newFrequency; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: steadyState = false; lbajardsilogic@0: return frequency; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: unsigned char lbajardsilogic@0: SpectrogramLayer::getDisplayValue(View *v, float input) const lbajardsilogic@0: { lbajardsilogic@0: int value; lbajardsilogic@0: lbajardsilogic@0: float min = 0.f; lbajardsilogic@0: float max = 1.f; lbajardsilogic@0: lbajardsilogic@0: if (m_normalizeVisibleArea) { lbajardsilogic@0: min = m_viewMags[v].getMin(); lbajardsilogic@0: max = m_viewMags[v].getMax(); lbajardsilogic@0: } else if (!m_normalizeColumns) { lbajardsilogic@0: if (m_colourScale == LinearColourScale //|| lbajardsilogic@0: // m_colourScale == MeterColourScale) { lbajardsilogic@0: ) { lbajardsilogic@0: max = 0.1f; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float thresh = -80.f; lbajardsilogic@0: lbajardsilogic@0: if (max == 0.f) max = 1.f; lbajardsilogic@0: if (max == min) min = max - 0.0001f; lbajardsilogic@0: lbajardsilogic@0: switch (m_colourScale) { lbajardsilogic@0: lbajardsilogic@0: default: lbajardsilogic@0: case LinearColourScale: lbajardsilogic@0: value = int(((input - min) / (max - min)) * 255.f) + 1; lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case MeterColourScale: lbajardsilogic@0: value = AudioLevel::multiplier_to_preview lbajardsilogic@0: ((input - min) / (max - min), 254) + 1; lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case dBSquaredColourScale: lbajardsilogic@0: input = ((input - min) * (input - min)) / ((max - min) * (max - min)); lbajardsilogic@0: if (input > 0.f) { lbajardsilogic@0: input = 10.f * log10f(input); lbajardsilogic@0: } else { lbajardsilogic@0: input = thresh; lbajardsilogic@0: } lbajardsilogic@0: if (min > 0.f) { lbajardsilogic@0: thresh = 10.f * log10f(min * min); lbajardsilogic@0: if (thresh < -80.f) thresh = -80.f; lbajardsilogic@0: } lbajardsilogic@0: input = (input - thresh) / (-thresh); lbajardsilogic@0: if (input < 0.f) input = 0.f; lbajardsilogic@0: if (input > 1.f) input = 1.f; lbajardsilogic@0: value = int(input * 255.f) + 1; lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case dBColourScale: lbajardsilogic@0: //!!! experiment with normalizing the visible area this way. lbajardsilogic@0: //In any case, we need to have some indication of what the dB lbajardsilogic@0: //scale is relative to. lbajardsilogic@0: input = (input - min) / (max - min); lbajardsilogic@0: if (input > 0.f) { lbajardsilogic@0: input = 10.f * log10f(input); lbajardsilogic@0: } else { lbajardsilogic@0: input = thresh; lbajardsilogic@0: } lbajardsilogic@0: if (min > 0.f) { lbajardsilogic@0: thresh = 10.f * log10f(min); lbajardsilogic@0: if (thresh < -80.f) thresh = -80.f; lbajardsilogic@0: } lbajardsilogic@0: input = (input - thresh) / (-thresh); lbajardsilogic@0: if (input < 0.f) input = 0.f; lbajardsilogic@0: if (input > 1.f) input = 1.f; lbajardsilogic@0: value = int(input * 255.f) + 1; lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case PhaseColourScale: lbajardsilogic@0: value = int((input * 127.0 / M_PI) + 128); lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (value > UCHAR_MAX) value = UCHAR_MAX; lbajardsilogic@0: if (value < 0) value = 0; lbajardsilogic@0: return value; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const lbajardsilogic@0: { lbajardsilogic@0: //!!! unused lbajardsilogic@0: lbajardsilogic@0: int value = uc; lbajardsilogic@0: float input; lbajardsilogic@0: lbajardsilogic@0: //!!! incorrect for normalizing visible area (and also out of date) lbajardsilogic@0: lbajardsilogic@0: switch (m_colourScale) { lbajardsilogic@0: lbajardsilogic@0: default: lbajardsilogic@0: case LinearColourScale: lbajardsilogic@0: input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50); lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case MeterColourScale: lbajardsilogic@0: input = AudioLevel::preview_to_multiplier(value - 1, 255) lbajardsilogic@0: / (m_normalizeColumns ? 1.0 : 50.0); lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case dBSquaredColourScale: lbajardsilogic@0: input = float(value - 1) / 255.0; lbajardsilogic@0: input = (input * 80.0) - 80.0; lbajardsilogic@0: input = powf(10.0, input) / 20.0; lbajardsilogic@0: value = int(input); lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case dBColourScale: lbajardsilogic@0: input = float(value - 1) / 255.0; lbajardsilogic@0: input = (input * 80.0) - 80.0; lbajardsilogic@0: input = powf(10.0, input) / 20.0; lbajardsilogic@0: value = int(input); lbajardsilogic@0: break; lbajardsilogic@0: lbajardsilogic@0: case PhaseColourScale: lbajardsilogic@0: input = float(value - 128) * M_PI / 127.0; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return input; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getEffectiveMinFrequency() const lbajardsilogic@0: { lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: float minf = float(sr) / m_fftSize; lbajardsilogic@0: lbajardsilogic@0: if (m_minFrequency > 0.0) { lbajardsilogic@0: size_t minbin = size_t((double(m_minFrequency) * m_fftSize) / sr + 0.01); lbajardsilogic@0: if (minbin < 1) minbin = 1; lbajardsilogic@0: minf = minbin * sr / m_fftSize; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return minf; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getEffectiveMaxFrequency() const lbajardsilogic@0: { lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: float maxf = float(sr) / 2; lbajardsilogic@0: lbajardsilogic@0: if (m_maxFrequency > 0.0) { lbajardsilogic@0: size_t maxbin = size_t((double(m_maxFrequency) * m_fftSize) / sr + 0.1); lbajardsilogic@0: if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; lbajardsilogic@0: maxf = maxbin * sr / m_fftSize; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return maxf; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const lbajardsilogic@0: { lbajardsilogic@0: int h = v->height(); lbajardsilogic@0: if (y < 0 || y >= h) return false; lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: float minf = getEffectiveMinFrequency(); lbajardsilogic@0: float maxf = getEffectiveMaxFrequency(); lbajardsilogic@0: lbajardsilogic@0: bool logarithmic = (m_frequencyScale == LogFrequencyScale); lbajardsilogic@0: lbajardsilogic@0: //!!! wrong for smoothing -- wrong fft size for fft model lbajardsilogic@0: lbajardsilogic@0: q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); lbajardsilogic@0: q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); lbajardsilogic@0: lbajardsilogic@0: // Now map these on to actual bins lbajardsilogic@0: lbajardsilogic@0: int b0 = int((q0 * m_fftSize) / sr); lbajardsilogic@0: int b1 = int((q1 * m_fftSize) / sr); lbajardsilogic@0: lbajardsilogic@0: //!!! this is supposed to return fractions-of-bins, as it were, hence the floats lbajardsilogic@0: q0 = b0; lbajardsilogic@0: q1 = b1; lbajardsilogic@0: lbajardsilogic@0: // q0 = (b0 * sr) / m_fftSize; lbajardsilogic@0: // q1 = (b1 * sr) / m_fftSize; lbajardsilogic@0: lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const lbajardsilogic@0: { lbajardsilogic@0: size_t modelStart = m_model->getStartFrame(); lbajardsilogic@0: size_t modelEnd = m_model->getEndFrame(); lbajardsilogic@0: lbajardsilogic@0: // Each pixel column covers an exact range of sample frames: lbajardsilogic@0: int f0 = v->getFrameForX(x) - modelStart; lbajardsilogic@0: int f1 = v->getFrameForX(x + 1) - modelStart - 1; lbajardsilogic@0: lbajardsilogic@0: if (f1 < int(modelStart) || f0 > int(modelEnd)) { lbajardsilogic@0: return false; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // And that range may be drawn from a possibly non-integral lbajardsilogic@0: // range of spectrogram windows: lbajardsilogic@0: lbajardsilogic@0: size_t windowIncrement = getWindowIncrement(); lbajardsilogic@0: s0 = float(f0) / windowIncrement; lbajardsilogic@0: s1 = float(f1) / windowIncrement; lbajardsilogic@0: lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const lbajardsilogic@0: { lbajardsilogic@0: float s0 = 0, s1 = 0; lbajardsilogic@0: if (!getXBinRange(v, x, s0, s1)) return false; lbajardsilogic@0: lbajardsilogic@0: int s0i = int(s0 + 0.001); lbajardsilogic@0: int s1i = int(s1); lbajardsilogic@0: lbajardsilogic@0: int windowIncrement = getWindowIncrement(); lbajardsilogic@0: int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2; lbajardsilogic@0: int w1 = s1i * windowIncrement + windowIncrement + lbajardsilogic@0: (m_windowSize - windowIncrement)/2 - 1; lbajardsilogic@0: lbajardsilogic@0: min = RealTime::frame2RealTime(w0, m_model->getSampleRate()); lbajardsilogic@0: max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) lbajardsilogic@0: const lbajardsilogic@0: { lbajardsilogic@0: float q0 = 0, q1 = 0; lbajardsilogic@0: if (!getYBinRange(v, y, q0, q1)) return false; lbajardsilogic@0: lbajardsilogic@0: int q0i = int(q0 + 0.001); lbajardsilogic@0: int q1i = int(q1); lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: lbajardsilogic@0: for (int q = q0i; q <= q1i; ++q) { lbajardsilogic@0: if (q == q0i) freqMin = (sr * q) / m_fftSize; lbajardsilogic@0: if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize; lbajardsilogic@0: } lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y, lbajardsilogic@0: float &freqMin, float &freqMax, lbajardsilogic@0: float &adjFreqMin, float &adjFreqMax) lbajardsilogic@0: const lbajardsilogic@0: { lbajardsilogic@0: FFTModel *fft = getFFTModel(v); lbajardsilogic@0: if (!fft) return false; lbajardsilogic@0: lbajardsilogic@0: float s0 = 0, s1 = 0; lbajardsilogic@0: if (!getXBinRange(v, x, s0, s1)) return false; lbajardsilogic@0: lbajardsilogic@0: float q0 = 0, q1 = 0; lbajardsilogic@0: if (!getYBinRange(v, y, q0, q1)) return false; lbajardsilogic@0: lbajardsilogic@0: int s0i = int(s0 + 0.001); lbajardsilogic@0: int s1i = int(s1); lbajardsilogic@0: lbajardsilogic@0: int q0i = int(q0 + 0.001); lbajardsilogic@0: int q1i = int(q1); lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: lbajardsilogic@0: size_t windowSize = m_windowSize; lbajardsilogic@0: size_t windowIncrement = getWindowIncrement(); lbajardsilogic@0: lbajardsilogic@0: bool haveAdj = false; lbajardsilogic@0: lbajardsilogic@0: bool peaksOnly = (m_binDisplay == PeakBins || lbajardsilogic@0: m_binDisplay == PeakFrequencies); lbajardsilogic@0: lbajardsilogic@0: for (int q = q0i; q <= q1i; ++q) { lbajardsilogic@0: lbajardsilogic@0: for (int s = s0i; s <= s1i; ++s) { lbajardsilogic@0: lbajardsilogic@0: if (!fft->isColumnAvailable(s)) continue; lbajardsilogic@0: lbajardsilogic@0: float binfreq = (sr * q) / m_windowSize; lbajardsilogic@0: if (q == q0i) freqMin = binfreq; lbajardsilogic@0: if (q == q1i) freqMax = binfreq; lbajardsilogic@0: lbajardsilogic@0: if (peaksOnly && !fft->isLocalPeak(s, q)) continue; lbajardsilogic@0: lbajardsilogic@0: if (!fft->isOverThreshold(s, q, m_threshold)) continue; lbajardsilogic@0: lbajardsilogic@0: float freq = binfreq; lbajardsilogic@0: bool steady = false; lbajardsilogic@0: lbajardsilogic@0: if (s < int(fft->getWidth()) - 1) { lbajardsilogic@0: lbajardsilogic@0: freq = calculateFrequency(q, lbajardsilogic@0: windowSize, lbajardsilogic@0: windowIncrement, lbajardsilogic@0: sr, lbajardsilogic@0: fft->getPhaseAt(s, q), lbajardsilogic@0: fft->getPhaseAt(s+1, q), lbajardsilogic@0: steady); lbajardsilogic@0: lbajardsilogic@0: if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; lbajardsilogic@0: if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; lbajardsilogic@0: lbajardsilogic@0: haveAdj = true; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!haveAdj) { lbajardsilogic@0: adjFreqMin = adjFreqMax = 0.0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return haveAdj; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y, lbajardsilogic@0: float &min, float &max, lbajardsilogic@0: float &phaseMin, float &phaseMax) const lbajardsilogic@0: { lbajardsilogic@0: float q0 = 0, q1 = 0; lbajardsilogic@0: if (!getYBinRange(v, y, q0, q1)) return false; lbajardsilogic@0: lbajardsilogic@0: float s0 = 0, s1 = 0; lbajardsilogic@0: if (!getXBinRange(v, x, s0, s1)) return false; lbajardsilogic@0: lbajardsilogic@0: int q0i = int(q0 + 0.001); lbajardsilogic@0: int q1i = int(q1); lbajardsilogic@0: lbajardsilogic@0: int s0i = int(s0 + 0.001); lbajardsilogic@0: int s1i = int(s1); lbajardsilogic@0: lbajardsilogic@0: bool rv = false; lbajardsilogic@0: lbajardsilogic@0: size_t zp = getZeroPadLevel(v); lbajardsilogic@0: q0i *= zp + 1; lbajardsilogic@0: q1i *= zp + 1; lbajardsilogic@0: lbajardsilogic@0: FFTModel *fft = getFFTModel(v); lbajardsilogic@0: lbajardsilogic@0: if (fft) { lbajardsilogic@0: lbajardsilogic@0: int cw = fft->getWidth(); lbajardsilogic@0: int ch = fft->getHeight(); lbajardsilogic@0: lbajardsilogic@0: min = 0.0; lbajardsilogic@0: max = 0.0; lbajardsilogic@0: phaseMin = 0.0; lbajardsilogic@0: phaseMax = 0.0; lbajardsilogic@0: bool have = false; lbajardsilogic@0: lbajardsilogic@0: for (int q = q0i; q <= q1i; ++q) { lbajardsilogic@0: for (int s = s0i; s <= s1i; ++s) { lbajardsilogic@0: if (s >= 0 && q >= 0 && s < cw && q < ch) { lbajardsilogic@0: lbajardsilogic@0: if (!fft->isColumnAvailable(s)) continue; lbajardsilogic@0: lbajardsilogic@0: float value; lbajardsilogic@0: lbajardsilogic@0: value = fft->getPhaseAt(s, q); lbajardsilogic@0: if (!have || value < phaseMin) { phaseMin = value; } lbajardsilogic@0: if (!have || value > phaseMax) { phaseMax = value; } lbajardsilogic@0: lbajardsilogic@0: value = fft->getMagnitudeAt(s, q); lbajardsilogic@0: if (!have || value < min) { min = value; } lbajardsilogic@0: if (!have || value > max) { max = value; } lbajardsilogic@0: lbajardsilogic@0: have = true; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (have) { lbajardsilogic@0: rv = true; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return rv; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getZeroPadLevel(const View *v) const lbajardsilogic@0: { lbajardsilogic@0: //!!! tidy all this stuff lbajardsilogic@0: lbajardsilogic@0: if (m_binDisplay != AllBins) return 0; lbajardsilogic@0: lbajardsilogic@0: Preferences::SpectrogramSmoothing smoothing = lbajardsilogic@0: Preferences::getInstance()->getSpectrogramSmoothing(); lbajardsilogic@0: lbajardsilogic@0: if (smoothing == Preferences::NoSpectrogramSmoothing || lbajardsilogic@0: smoothing == Preferences::SpectrogramInterpolated) return 0; lbajardsilogic@0: lbajardsilogic@0: if (m_frequencyScale == LogFrequencyScale) return 3; lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: lbajardsilogic@0: size_t maxbin = m_fftSize / 2; lbajardsilogic@0: if (m_maxFrequency > 0) { lbajardsilogic@0: maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); lbajardsilogic@0: if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t minbin = 1; lbajardsilogic@0: if (m_minFrequency > 0) { lbajardsilogic@0: minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1); lbajardsilogic@0: if (minbin < 1) minbin = 1; lbajardsilogic@0: if (minbin >= maxbin) minbin = maxbin - 1; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float perPixel = lbajardsilogic@0: float(v->height()) / lbajardsilogic@0: float((maxbin - minbin) / (m_zeroPadLevel + 1)); lbajardsilogic@0: lbajardsilogic@0: if (perPixel > 2.8) { lbajardsilogic@0: return 3; // 4x oversampling lbajardsilogic@0: } else if (perPixel > 1.5) { lbajardsilogic@0: return 1; // 2x lbajardsilogic@0: } else { lbajardsilogic@0: return 0; // 1x lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: SpectrogramLayer::getFFTSize(const View *v) const lbajardsilogic@0: { lbajardsilogic@0: return m_fftSize * (getZeroPadLevel(v) + 1); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTModel * lbajardsilogic@0: SpectrogramLayer::getFFTModel(const View *v) const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return 0; lbajardsilogic@0: lbajardsilogic@0: size_t fftSize = getFFTSize(v); lbajardsilogic@0: lbajardsilogic@0: if (m_fftModels.find(v) != m_fftModels.end()) { lbajardsilogic@0: if (m_fftModels[v].first == 0) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: delete m_fftModels[v].first; lbajardsilogic@0: m_fftModels.erase(v); lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: return m_fftModels[v].first; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_fftModels.find(v) == m_fftModels.end()) { lbajardsilogic@0: lbajardsilogic@0: FFTModel *model = new FFTModel(m_model, lbajardsilogic@0: m_channel, lbajardsilogic@0: m_windowType, lbajardsilogic@0: m_windowSize, lbajardsilogic@0: getWindowIncrement(), lbajardsilogic@0: fftSize, lbajardsilogic@0: true, lbajardsilogic@0: m_candidateFillStartFrame); lbajardsilogic@0: lbajardsilogic@0: if (!model->isOK()) { lbajardsilogic@0: QMessageBox::critical lbajardsilogic@0: (0, tr("FFT cache failed"), lbajardsilogic@0: tr("Failed to create the FFT model for this spectrogram.\n" lbajardsilogic@0: "There may be insufficient memory or disc space to continue.")); lbajardsilogic@0: delete model; lbajardsilogic@0: m_fftModels[v] = FFTFillPair(0, 0); lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!m_sliceableModel) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM lbajardsilogic@0: std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); lbajardsilogic@0: m_sliceableModel = model; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_fftModels[v] = FFTFillPair(model, 0); lbajardsilogic@0: lbajardsilogic@0: model->resume(); lbajardsilogic@0: lbajardsilogic@0: delete m_updateTimer; lbajardsilogic@0: m_updateTimer = new QTimer((SpectrogramLayer *)this); lbajardsilogic@0: connect(m_updateTimer, SIGNAL(timeout()), lbajardsilogic@0: this, SLOT(fillTimerTimedOut())); lbajardsilogic@0: m_updateTimer->start(200); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return m_fftModels[v].first; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: const Model * lbajardsilogic@0: SpectrogramLayer::getSliceableModel() const lbajardsilogic@0: { lbajardsilogic@0: if (m_sliceableModel) return m_sliceableModel; lbajardsilogic@0: if (m_fftModels.empty()) return 0; lbajardsilogic@0: m_sliceableModel = m_fftModels.begin()->second.first; lbajardsilogic@0: return m_sliceableModel; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::invalidateFFTModels() lbajardsilogic@0: { lbajardsilogic@0: for (ViewFFTMap::iterator i = m_fftModels.begin(); lbajardsilogic@0: i != m_fftModels.end(); ++i) { lbajardsilogic@0: delete i->second.first; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_fftModels.clear(); lbajardsilogic@0: lbajardsilogic@0: if (m_sliceableModel) { lbajardsilogic@0: std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << std::endl; lbajardsilogic@0: emit sliceableModelReplaced(m_sliceableModel, 0); lbajardsilogic@0: m_sliceableModel = 0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::invalidateMagnitudes() lbajardsilogic@0: { lbajardsilogic@0: m_viewMags.clear(); lbajardsilogic@0: for (std::vector::iterator i = m_columnMags.begin(); lbajardsilogic@0: i != m_columnMags.end(); ++i) { lbajardsilogic@0: *i = MagnitudeRange(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::updateViewMagnitudes(View *v) const lbajardsilogic@0: { lbajardsilogic@0: MagnitudeRange mag; lbajardsilogic@0: lbajardsilogic@0: int x0 = 0, x1 = v->width(); lbajardsilogic@0: float s00 = 0, s01 = 0, s10 = 0, s11 = 0; lbajardsilogic@0: lbajardsilogic@0: if (!getXBinRange(v, x0, s00, s01)) { lbajardsilogic@0: s00 = s01 = m_model->getStartFrame() / getWindowIncrement(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!getXBinRange(v, x1, s10, s11)) { lbajardsilogic@0: s10 = s11 = m_model->getEndFrame() / getWindowIncrement(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@190: int s0 = int(MIN(s00, s10) + 0.0001); lbajardsilogic@190: int s1 = int(MAX(s01, s11) + 0.0001); lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (int(m_columnMags.size()) <= s1) { lbajardsilogic@0: m_columnMags.resize(s1 + 1); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (int s = s0; s <= s1; ++s) { lbajardsilogic@0: if (m_columnMags[s].isSet()) { lbajardsilogic@0: mag.sample(m_columnMags[s]); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " lbajardsilogic@0: << s0 << " -> " << s1 << " inclusive" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (!mag.isSet()) return false; lbajardsilogic@0: if (mag == m_viewMags[v]) return false; lbajardsilogic@0: m_viewMags[v] = mag; lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("SpectrogramLayer::paint", true); lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl; lbajardsilogic@0: lbajardsilogic@0: std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: long startFrame = v->getStartFrame(); lbajardsilogic@0: if (startFrame < 0) m_candidateFillStartFrame = 0; lbajardsilogic@0: else m_candidateFillStartFrame = startFrame; lbajardsilogic@0: lbajardsilogic@0: if (!m_model || !m_model->isOK() || !m_model->isReady()) { lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (isLayerDormant(v)) { lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // Need to do this even if !isLayerDormant, as that could mean v lbajardsilogic@0: // is not in the dormancy map at all -- we need it to be present lbajardsilogic@0: // and accountable for when determining whether we need the cache lbajardsilogic@0: // in the cache-fill thread above. lbajardsilogic@0: //!!! no longer use cache-fill thread lbajardsilogic@0: const_cast(this)->Layer::setLayerDormant(v, false); lbajardsilogic@0: lbajardsilogic@0: size_t fftSize = getFFTSize(v); lbajardsilogic@0: FFTModel *fft = getFFTModel(v); lbajardsilogic@0: if (!fft) { lbajardsilogic@0: std::cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << std::endl; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: PixmapCache &cache = m_pixmapCaches[v]; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: bool stillCacheing = (m_updateTimer != 0); lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: int zoomLevel = v->getZoomLevel(); lbajardsilogic@0: lbajardsilogic@0: int x0 = 0; lbajardsilogic@0: int x1 = v->width(); lbajardsilogic@0: lbajardsilogic@0: bool recreateWholePixmapCache = true; lbajardsilogic@0: lbajardsilogic@0: x0 = rect.left(); lbajardsilogic@0: x1 = rect.right() + 1; lbajardsilogic@0: lbajardsilogic@0: if (cache.validArea.width() > 0) { lbajardsilogic@0: lbajardsilogic@0: if (int(cache.zoomLevel) == zoomLevel && lbajardsilogic@0: cache.pixmap.width() == v->width() && lbajardsilogic@0: cache.pixmap.height() == v->height()) { lbajardsilogic@0: lbajardsilogic@0: if (v->getXForFrame(cache.startFrame) == lbajardsilogic@0: v->getXForFrame(startFrame) && lbajardsilogic@0: cache.validArea.x() <= x0 && lbajardsilogic@0: cache.validArea.x() + cache.validArea.width() >= x1) { lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: paint.drawPixmap(rect, cache.pixmap, rect); lbajardsilogic@0: illuminateLocalFeatures(v, paint); lbajardsilogic@0: return; lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: recreateWholePixmapCache = false; lbajardsilogic@0: lbajardsilogic@0: int dx = v->getXForFrame(cache.startFrame) - lbajardsilogic@0: v->getXForFrame(startFrame); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (dx != 0 && lbajardsilogic@0: dx > -cache.pixmap.width() && lbajardsilogic@0: dx < cache.pixmap.width()) { lbajardsilogic@0: lbajardsilogic@0: #if defined(Q_WS_WIN32) || defined(Q_WS_MAC) lbajardsilogic@0: // Copying a pixmap to itself doesn't work lbajardsilogic@0: // properly on Windows or Mac (it only works when lbajardsilogic@0: // moving in one direction). lbajardsilogic@0: lbajardsilogic@0: //!!! Need a utility function for this lbajardsilogic@0: lbajardsilogic@0: static QPixmap *tmpPixmap = 0; lbajardsilogic@0: if (!tmpPixmap || lbajardsilogic@0: tmpPixmap->width() != cache.pixmap.width() || lbajardsilogic@0: tmpPixmap->height() != cache.pixmap.height()) { lbajardsilogic@0: delete tmpPixmap; lbajardsilogic@0: tmpPixmap = new QPixmap(cache.pixmap.width(), lbajardsilogic@0: cache.pixmap.height()); lbajardsilogic@0: } lbajardsilogic@0: QPainter cachePainter; lbajardsilogic@0: cachePainter.begin(tmpPixmap); lbajardsilogic@0: cachePainter.drawPixmap(0, 0, cache.pixmap); lbajardsilogic@0: cachePainter.end(); lbajardsilogic@0: cachePainter.begin(&cache.pixmap); lbajardsilogic@0: cachePainter.drawPixmap(dx, 0, *tmpPixmap); lbajardsilogic@0: cachePainter.end(); lbajardsilogic@0: #else lbajardsilogic@0: QPainter cachePainter(&cache.pixmap); lbajardsilogic@0: cachePainter.drawPixmap(dx, 0, cache.pixmap); lbajardsilogic@0: cachePainter.end(); lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: int px = cache.validArea.x(); lbajardsilogic@0: int pw = cache.validArea.width(); lbajardsilogic@0: lbajardsilogic@0: if (dx < 0) { lbajardsilogic@0: x0 = cache.pixmap.width() + dx; lbajardsilogic@0: x1 = cache.pixmap.width(); lbajardsilogic@0: px += dx; lbajardsilogic@0: if (px < 0) { lbajardsilogic@0: pw += px; lbajardsilogic@0: px = 0; lbajardsilogic@0: if (pw < 0) pw = 0; lbajardsilogic@0: } lbajardsilogic@0: } else { lbajardsilogic@0: x0 = 0; lbajardsilogic@0: x1 = dx; lbajardsilogic@0: px += dx; lbajardsilogic@0: if (px + pw > cache.pixmap.width()) { lbajardsilogic@0: pw = int(cache.pixmap.width()) - px; lbajardsilogic@0: if (pw < 0) pw = 0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: cache.validArea = lbajardsilogic@0: QRect(px, cache.validArea.y(), lbajardsilogic@0: pw, cache.validArea.height()); lbajardsilogic@0: lbajardsilogic@0: paint.drawPixmap(rect & cache.validArea, lbajardsilogic@0: cache.pixmap, lbajardsilogic@0: rect & cache.validArea); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl; lbajardsilogic@0: if (int(cache.zoomLevel) != zoomLevel) { lbajardsilogic@0: std::cerr << "(cache zoomLevel " << cache.zoomLevel lbajardsilogic@0: << " != " << zoomLevel << ")" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: if (cache.pixmap.width() != v->width()) { lbajardsilogic@0: std::cerr << "(cache width " << cache.pixmap.width() lbajardsilogic@0: << " != " << v->width(); lbajardsilogic@0: } lbajardsilogic@0: if (cache.pixmap.height() != v->height()) { lbajardsilogic@0: std::cerr << "(cache height " << cache.pixmap.height() lbajardsilogic@0: << " != " << v->height(); lbajardsilogic@0: } lbajardsilogic@0: #endif lbajardsilogic@0: cache.validArea = QRect(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (updateViewMagnitudes(v)) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: recreateWholePixmapCache = true; lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (recreateWholePixmapCache) { lbajardsilogic@0: x0 = 0; lbajardsilogic@0: x1 = v->width(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: struct timeval tv; lbajardsilogic@0: (void)gettimeofday(&tv, 0); lbajardsilogic@0: RealTime mainPaintStart = RealTime::fromTimeval(tv); lbajardsilogic@0: lbajardsilogic@0: int paintBlockWidth = m_lastPaintBlockWidth; lbajardsilogic@0: lbajardsilogic@0: if (paintBlockWidth == 0) { lbajardsilogic@0: paintBlockWidth = (300000 / zoomLevel); lbajardsilogic@0: } else { lbajardsilogic@0: RealTime lastTime = m_lastPaintTime; lbajardsilogic@0: while (lastTime > RealTime::fromMilliseconds(200) && lbajardsilogic@0: paintBlockWidth > 50) { lbajardsilogic@0: paintBlockWidth /= 2; lbajardsilogic@0: lastTime = lastTime / 2; lbajardsilogic@0: } lbajardsilogic@0: while (lastTime < RealTime::fromMilliseconds(90) && lbajardsilogic@0: paintBlockWidth < 1500) { lbajardsilogic@0: paintBlockWidth *= 2; lbajardsilogic@0: lastTime = lastTime * 2; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (paintBlockWidth < 20) paintBlockWidth = 20; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: // We always paint the full height when refreshing the cache. lbajardsilogic@0: // Smaller heights can be used when painting direct from cache lbajardsilogic@0: // (further up in this function), but we want to ensure the cache lbajardsilogic@0: // is coherent without having to worry about vertical matching of lbajardsilogic@0: // required and valid areas as well as horizontal. lbajardsilogic@0: lbajardsilogic@0: int h = v->height(); lbajardsilogic@0: lbajardsilogic@0: if (cache.validArea.width() > 0) { lbajardsilogic@0: lbajardsilogic@0: int vx0 = 0, vx1 = 0; lbajardsilogic@0: vx0 = cache.validArea.x(); lbajardsilogic@0: vx1 = cache.validArea.x() + cache.validArea.width(); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: if (x0 < vx0) { lbajardsilogic@0: if (x0 + paintBlockWidth < vx0) { lbajardsilogic@0: x0 = vx0 - paintBlockWidth; lbajardsilogic@0: } else { lbajardsilogic@0: x0 = 0; lbajardsilogic@0: } lbajardsilogic@0: } else if (x0 > vx1) { lbajardsilogic@0: x0 = vx1; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (x1 < vx0) { lbajardsilogic@0: x1 = vx0; lbajardsilogic@0: } else if (x1 > vx1) { lbajardsilogic@0: if (vx1 + paintBlockWidth < x1) { lbajardsilogic@0: x1 = vx1 + paintBlockWidth; lbajardsilogic@0: } else { lbajardsilogic@0: x1 = v->width(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: cache.validArea = QRect lbajardsilogic@190: (MIN(vx0, x0), cache.validArea.y(), lbajardsilogic@190: MAX(vx1 - MIN(vx0, x0), lbajardsilogic@190: x1 - MIN(vx0, x0)), lbajardsilogic@0: cache.validArea.height()); lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: if (x1 > x0 + paintBlockWidth) { lbajardsilogic@0: int sfx = x1; lbajardsilogic@0: if (startFrame < 0) sfx = v->getXForFrame(0); lbajardsilogic@0: if (sfx >= x0 && sfx + paintBlockWidth <= x1) { lbajardsilogic@0: x0 = sfx; lbajardsilogic@0: x1 = x0 + paintBlockWidth; lbajardsilogic@0: } else { lbajardsilogic@0: int mid = (x1 + x0) / 2; lbajardsilogic@0: x0 = mid - paintBlockWidth/2; lbajardsilogic@0: x1 = x0 + paintBlockWidth; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: cache.validArea = QRect(x0, 0, x1 - x0, h); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int w = x1 - x0; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) { lbajardsilogic@0: m_drawBuffer = QImage(w, h, QImage::Format_RGB32); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_drawBuffer.fill(m_palette.getColour(0).rgb()); lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: lbajardsilogic@0: // Set minFreq and maxFreq to the frequency extents of the possibly lbajardsilogic@0: // zero-padded visible bin range, and displayMinFreq and displayMaxFreq lbajardsilogic@0: // to the actual scale frequency extents (presumably not zero padded). lbajardsilogic@0: lbajardsilogic@0: size_t maxbin = fftSize / 2; lbajardsilogic@0: if (m_maxFrequency > 0) { lbajardsilogic@0: maxbin = int((double(m_maxFrequency) * fftSize) / sr + 0.1); lbajardsilogic@0: if (maxbin > fftSize / 2) maxbin = fftSize / 2; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t minbin = 1; lbajardsilogic@0: if (m_minFrequency > 0) { lbajardsilogic@0: minbin = int((double(m_minFrequency) * fftSize) / sr + 0.1); lbajardsilogic@0: if (minbin < 1) minbin = 1; lbajardsilogic@0: if (minbin >= maxbin) minbin = maxbin - 1; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float minFreq = (float(minbin) * sr) / fftSize; lbajardsilogic@0: float maxFreq = (float(maxbin) * sr) / fftSize; lbajardsilogic@0: lbajardsilogic@0: float displayMinFreq = minFreq; lbajardsilogic@0: float displayMaxFreq = maxFreq; lbajardsilogic@0: lbajardsilogic@0: if (fftSize != m_fftSize) { lbajardsilogic@0: displayMinFreq = getEffectiveMinFrequency(); lbajardsilogic@0: displayMaxFreq = getEffectiveMaxFrequency(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: /*float ymag[h]; lbajardsilogic@0: float ydiv[h]; lbajardsilogic@0: float yval[maxbin + 1]; //!!! cache this?*/ lbajardsilogic@191: float *ymag = (float*) malloc(h*sizeof(float)); lbajardsilogic@191: float *ydiv = (float*) malloc(h*sizeof(float)); lbajardsilogic@0: float *yval = (float*) malloc((maxbin + 1)*sizeof(float)); lbajardsilogic@0: lbajardsilogic@0: size_t increment = getWindowIncrement(); lbajardsilogic@0: lbajardsilogic@0: bool logarithmic = (m_frequencyScale == LogFrequencyScale); lbajardsilogic@0: lbajardsilogic@0: for (size_t q = minbin; q <= maxbin; ++q) { lbajardsilogic@0: float f0 = (float(q) * sr) / fftSize; lbajardsilogic@0: yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, lbajardsilogic@0: logarithmic); lbajardsilogic@0: // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: MagnitudeRange overallMag = m_viewMags[v]; lbajardsilogic@0: bool overallMagChanged = false; lbajardsilogic@0: lbajardsilogic@0: bool fftSuspended = false; lbajardsilogic@0: lbajardsilogic@0: bool interpolate = false; lbajardsilogic@0: Preferences::SpectrogramSmoothing smoothing = lbajardsilogic@0: Preferences::getInstance()->getSpectrogramSmoothing(); lbajardsilogic@0: if (smoothing == Preferences::SpectrogramInterpolated || lbajardsilogic@0: smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { lbajardsilogic@0: if (m_binDisplay != PeakBins && lbajardsilogic@0: m_binDisplay != PeakFrequencies) { lbajardsilogic@0: interpolate = true; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: bool runOutOfData = false; lbajardsilogic@0: lbajardsilogic@0: for (int x = 0; x < w; ++x) { lbajardsilogic@0: lbajardsilogic@0: if (runOutOfData) break; lbajardsilogic@0: lbajardsilogic@0: for (int y = 0; y < h; ++y) { lbajardsilogic@0: ymag[y] = 0.f; lbajardsilogic@0: ydiv[y] = 0.f; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float s0 = 0, s1 = 0; lbajardsilogic@0: lbajardsilogic@0: if (!getXBinRange(v, x0 + x, s0, s1)) { lbajardsilogic@0: assert(x <= m_drawBuffer.width()); lbajardsilogic@0: continue; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int s0i = int(s0 + 0.001); lbajardsilogic@0: int s1i = int(s1); lbajardsilogic@0: lbajardsilogic@0: if (s1i >= int(fft->getWidth())) { lbajardsilogic@0: if (s0i >= int(fft->getWidth())) { lbajardsilogic@0: continue; lbajardsilogic@0: } else { lbajardsilogic@0: s1i = s0i; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (int s = s0i; s <= s1i; ++s) { lbajardsilogic@0: lbajardsilogic@0: if (!fft->isColumnAvailable(s)) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "Met unavailable column at col " << s << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: // continue; lbajardsilogic@0: runOutOfData = true; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!fftSuspended) { lbajardsilogic@0: fft->suspendWrites(); lbajardsilogic@0: fftSuspended = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: MagnitudeRange mag; lbajardsilogic@0: lbajardsilogic@0: for (size_t q = minbin; q < maxbin; ++q) { lbajardsilogic@0: lbajardsilogic@0: float y0 = yval[q + 1]; lbajardsilogic@0: float y1 = yval[q]; lbajardsilogic@0: lbajardsilogic@0: if (m_binDisplay == PeakBins || lbajardsilogic@0: m_binDisplay == PeakFrequencies) { lbajardsilogic@0: if (!fft->isLocalPeak(s, q)) continue; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_threshold != 0.f && lbajardsilogic@0: !fft->isOverThreshold(s, q, m_threshold)) { lbajardsilogic@0: continue; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float sprop = 1.0; lbajardsilogic@0: if (s == s0i) sprop *= (s + 1) - s0; lbajardsilogic@0: if (s == s1i) sprop *= s1 - s; lbajardsilogic@0: lbajardsilogic@0: if (m_binDisplay == PeakFrequencies && lbajardsilogic@0: s < int(fft->getWidth()) - 1) { lbajardsilogic@0: lbajardsilogic@0: bool steady = false; lbajardsilogic@0: float f = calculateFrequency(q, lbajardsilogic@0: m_windowSize, lbajardsilogic@0: increment, lbajardsilogic@0: sr, lbajardsilogic@0: fft->getPhaseAt(s, q), lbajardsilogic@0: fft->getPhaseAt(s+1, q), lbajardsilogic@0: steady); lbajardsilogic@0: lbajardsilogic@0: y0 = y1 = v->getYForFrequency lbajardsilogic@0: (f, displayMinFreq, displayMaxFreq, logarithmic); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int y0i = int(y0 + 0.001); lbajardsilogic@0: int y1i = int(y1); lbajardsilogic@0: lbajardsilogic@0: float value; lbajardsilogic@0: lbajardsilogic@0: if (m_colourScale == PhaseColourScale) { lbajardsilogic@0: value = fft->getPhaseAt(s, q); lbajardsilogic@0: } else if (m_normalizeColumns) { lbajardsilogic@0: value = fft->getNormalizedMagnitudeAt(s, q); lbajardsilogic@0: mag.sample(value); lbajardsilogic@0: value *= m_gain; lbajardsilogic@0: } else { lbajardsilogic@0: value = fft->getMagnitudeAt(s, q); lbajardsilogic@0: mag.sample(value); lbajardsilogic@0: value *= m_gain; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (interpolate) { lbajardsilogic@0: lbajardsilogic@0: int ypi = y0i; lbajardsilogic@0: if (q < maxbin - 1) ypi = int(yval[q + 2]); lbajardsilogic@0: lbajardsilogic@0: for (int y = ypi; y <= y1i; ++y) { lbajardsilogic@0: lbajardsilogic@0: if (y < 0 || y >= h) continue; lbajardsilogic@0: lbajardsilogic@0: float yprop = sprop; lbajardsilogic@0: float iprop = yprop; lbajardsilogic@0: lbajardsilogic@0: if (ypi < y0i && y <= y0i) { lbajardsilogic@0: lbajardsilogic@0: float half = float(y0i - ypi) / 2; lbajardsilogic@0: float dist = y - (ypi + half); lbajardsilogic@0: lbajardsilogic@0: if (dist >= 0) { lbajardsilogic@0: iprop = (iprop * dist) / half; lbajardsilogic@0: ymag[y] += iprop * value; lbajardsilogic@0: } lbajardsilogic@0: } else { lbajardsilogic@0: if (y1i > y0i) { lbajardsilogic@0: lbajardsilogic@0: float half = float(y1i - y0i) / 2; lbajardsilogic@0: float dist = y - (y0i + half); lbajardsilogic@0: lbajardsilogic@0: if (dist >= 0) { lbajardsilogic@0: iprop = (iprop * (half - dist)) / half; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: ymag[y] += iprop * value; lbajardsilogic@0: ydiv[y] += yprop; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: for (int y = y0i; y <= y1i; ++y) { lbajardsilogic@0: lbajardsilogic@0: if (y < 0 || y >= h) continue; lbajardsilogic@0: lbajardsilogic@0: float yprop = sprop; lbajardsilogic@0: if (y == y0i) yprop *= (y + 1) - y0; lbajardsilogic@0: if (y == y1i) yprop *= y1 - y; lbajardsilogic@0: lbajardsilogic@0: for (int y = y0i; y <= y1i; ++y) { lbajardsilogic@0: lbajardsilogic@0: if (y < 0 || y >= h) continue; lbajardsilogic@0: lbajardsilogic@0: float yprop = sprop; lbajardsilogic@0: if (y == y0i) yprop *= (y + 1) - y0; lbajardsilogic@0: if (y == y1i) yprop *= y1 - y; lbajardsilogic@0: ymag[y] += yprop * value; lbajardsilogic@0: ydiv[y] += yprop; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (mag.isSet()) { lbajardsilogic@0: lbajardsilogic@0: if (s >= int(m_columnMags.size())) { lbajardsilogic@0: std::cerr << "INTERNAL ERROR: " << s << " >= " lbajardsilogic@0: << m_columnMags.size() << " at SpectrogramLayer.cpp:2087" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_columnMags[s].sample(mag); lbajardsilogic@0: lbajardsilogic@0: if (overallMag.sample(mag)) { lbajardsilogic@0: //!!! scaling would change here lbajardsilogic@0: overallMagChanged = true; lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (int y = 0; y < h; ++y) { lbajardsilogic@0: lbajardsilogic@0: if (ydiv[y] > 0.0) { lbajardsilogic@0: lbajardsilogic@0: unsigned char pixel = 0; lbajardsilogic@0: lbajardsilogic@0: float avg = ymag[y] / ydiv[y]; lbajardsilogic@0: pixel = getDisplayValue(v, avg); lbajardsilogic@0: lbajardsilogic@0: assert(x <= m_drawBuffer.width()); lbajardsilogic@0: QColor c = m_palette.getColour(pixel); lbajardsilogic@0: m_drawBuffer.setPixel(x, y, lbajardsilogic@0: qRgb(c.red(), c.green(), c.blue())); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (overallMagChanged) { lbajardsilogic@0: m_viewMags[v] = overallMag; lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Profiler profiler2("SpectrogramLayer::paint: draw image", true); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "Painting " << w << "x" << rect.height() lbajardsilogic@0: << " from draw buffer at " << 0 << "," << rect.y() lbajardsilogic@0: << " to window at " << x0 << "," << rect.y() << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height()); lbajardsilogic@0: lbajardsilogic@0: if (recreateWholePixmapCache) { lbajardsilogic@0: cache.pixmap = QPixmap(v->width(), h); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "Painting " << w << "x" << h lbajardsilogic@0: << " from draw buffer at " << 0 << "," << 0 lbajardsilogic@0: << " to cache at " << x0 << "," << 0 << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: QPainter cachePainter(&cache.pixmap); lbajardsilogic@0: cachePainter.drawImage(x0, 0, m_drawBuffer, 0, 0, w, h); lbajardsilogic@0: cachePainter.end(); lbajardsilogic@0: lbajardsilogic@0: if (!m_normalizeVisibleArea || !overallMagChanged) { lbajardsilogic@0: lbajardsilogic@0: cache.startFrame = startFrame; lbajardsilogic@0: cache.zoomLevel = zoomLevel; lbajardsilogic@0: lbajardsilogic@0: if (cache.validArea.x() > 0) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint() updating left (0, " lbajardsilogic@0: << cache.validArea.x() << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: v->update(0, 0, cache.validArea.x(), h); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (cache.validArea.x() + cache.validArea.width() < lbajardsilogic@0: cache.pixmap.width()) { lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint() updating right (" lbajardsilogic@0: << cache.validArea.x() + cache.validArea.width() lbajardsilogic@0: << ", " lbajardsilogic@0: << cache.pixmap.width() - (cache.validArea.x() + lbajardsilogic@0: cache.validArea.width()) lbajardsilogic@0: << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: v->update(cache.validArea.x() + cache.validArea.width(), lbajardsilogic@0: 0, lbajardsilogic@0: cache.pixmap.width() - (cache.validArea.x() + lbajardsilogic@0: cache.validArea.width()), lbajardsilogic@0: h); lbajardsilogic@0: } lbajardsilogic@0: } else { lbajardsilogic@0: // overallMagChanged lbajardsilogic@0: cache.validArea = QRect(); lbajardsilogic@0: v->update(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: illuminateLocalFeatures(v, paint); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::paint() returning" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: m_lastPaintBlockWidth = paintBlockWidth; lbajardsilogic@0: (void)gettimeofday(&tv, 0); lbajardsilogic@0: m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; lbajardsilogic@0: lbajardsilogic@0: if (fftSuspended) fft->resume(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const lbajardsilogic@0: { lbajardsilogic@0: QPoint localPos; lbajardsilogic@0: if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer: illuminateLocalFeatures(" lbajardsilogic@0: // << localPos.x() << "," << localPos.y() << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: float s0, s1; lbajardsilogic@0: float f0, f1; lbajardsilogic@0: lbajardsilogic@0: if (getXBinRange(v, localPos.x(), s0, s1) && lbajardsilogic@0: getYBinSourceRange(v, localPos.y(), f0, f1)) { lbajardsilogic@0: lbajardsilogic@0: int s0i = int(s0 + 0.001); lbajardsilogic@0: int s1i = int(s1); lbajardsilogic@0: lbajardsilogic@0: int x0 = v->getXForFrame(s0i * getWindowIncrement()); lbajardsilogic@0: int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement()); lbajardsilogic@0: lbajardsilogic@0: int y1 = int(getYForFrequency(v, f1)); lbajardsilogic@0: int y0 = int(getYForFrequency(v, f0)); lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer: illuminate " lbajardsilogic@0: // << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl; lbajardsilogic@0: lbajardsilogic@0: paint.setPen(Qt::white); lbajardsilogic@0: lbajardsilogic@0: //!!! should we be using paintCrosshairs for this? lbajardsilogic@0: lbajardsilogic@0: paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getYForFrequency(View *v, float frequency) const lbajardsilogic@0: { lbajardsilogic@0: return v->getYForFrequency(frequency, lbajardsilogic@0: getEffectiveMinFrequency(), lbajardsilogic@0: getEffectiveMaxFrequency(), lbajardsilogic@0: m_frequencyScale == LogFrequencyScale); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: SpectrogramLayer::getFrequencyForY(View *v, int y) const lbajardsilogic@0: { lbajardsilogic@0: return v->getFrequencyForY(y, lbajardsilogic@0: getEffectiveMinFrequency(), lbajardsilogic@0: getEffectiveMaxFrequency(), lbajardsilogic@0: m_frequencyScale == LogFrequencyScale); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getCompletion(View *v) const lbajardsilogic@0: { lbajardsilogic@0: if (m_updateTimer == 0) return 100; lbajardsilogic@0: if (m_fftModels.find(v) == m_fftModels.end()) return 100; lbajardsilogic@0: lbajardsilogic@0: size_t completion = m_fftModels[v].first->getCompletion(); lbajardsilogic@0: #ifdef DEBUG_SPECTROGRAM_REPAINT lbajardsilogic@0: std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: return completion; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getValueExtents(float &min, float &max, lbajardsilogic@0: bool &logarithmic, QString &unit) const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return false; lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: min = float(sr) / m_fftSize; lbajardsilogic@0: max = float(sr) / 2; lbajardsilogic@0: lbajardsilogic@0: logarithmic = (m_frequencyScale == LogFrequencyScale); lbajardsilogic@0: unit = "Hz"; lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getDisplayExtents(float &min, float &max) const lbajardsilogic@0: { lbajardsilogic@0: min = getEffectiveMinFrequency(); lbajardsilogic@0: max = getEffectiveMaxFrequency(); lbajardsilogic@0: // std::cerr << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << std::endl; lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::setDisplayExtents(float min, float max) lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return false; lbajardsilogic@0: lbajardsilogic@0: std::cerr << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (min < 0) min = 0; lbajardsilogic@0: if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2; lbajardsilogic@0: lbajardsilogic@0: size_t minf = lrintf(min); lbajardsilogic@0: size_t maxf = lrintf(max); lbajardsilogic@0: lbajardsilogic@0: if (m_minFrequency == minf && m_maxFrequency == maxf) return true; lbajardsilogic@0: lbajardsilogic@0: invalidatePixmapCaches(); lbajardsilogic@0: invalidateMagnitudes(); lbajardsilogic@0: lbajardsilogic@0: m_minFrequency = minf; lbajardsilogic@0: m_maxFrequency = maxf; lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: lbajardsilogic@0: int vs = getCurrentVerticalZoomStep(); lbajardsilogic@0: if (vs != m_lastEmittedZoomStep) { lbajardsilogic@0: emit verticalZoomChanged(); lbajardsilogic@0: m_lastEmittedZoomStep = vs; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::snapToFeatureFrame(View *, int &frame, lbajardsilogic@0: size_t &resolution, lbajardsilogic@0: SnapType snap) const lbajardsilogic@0: { lbajardsilogic@0: resolution = getWindowIncrement(); lbajardsilogic@0: int left = (frame / resolution) * resolution; lbajardsilogic@0: int right = left + resolution; lbajardsilogic@0: lbajardsilogic@0: switch (snap) { lbajardsilogic@0: case SnapLeft: frame = left; break; lbajardsilogic@0: case SnapRight: frame = right; break; lbajardsilogic@0: case SnapNearest: lbajardsilogic@0: case SnapNeighbouring: lbajardsilogic@0: if (frame - left > right - frame) frame = right; lbajardsilogic@0: else frame = left; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrogramLayer::getCrosshairExtents(View *v, QPainter &, lbajardsilogic@0: QPoint cursorPos, lbajardsilogic@0: std::vector &extents) const lbajardsilogic@0: { lbajardsilogic@0: QRect vertical(cursorPos.x() - 12, 0, 12, v->height()); lbajardsilogic@0: extents.push_back(vertical); lbajardsilogic@0: lbajardsilogic@0: QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); lbajardsilogic@0: extents.push_back(horizontal); lbajardsilogic@0: lbajardsilogic@0: return true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint, lbajardsilogic@0: QPoint cursorPos) const lbajardsilogic@0: { lbajardsilogic@0: paint.save(); lbajardsilogic@0: paint.setPen(m_crosshairColour); lbajardsilogic@0: lbajardsilogic@0: paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); lbajardsilogic@0: paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height()); lbajardsilogic@0: lbajardsilogic@0: float fundamental = getFrequencyForY(v, cursorPos.y()); lbajardsilogic@0: lbajardsilogic@0: int harmonic = 2; lbajardsilogic@0: lbajardsilogic@0: while (harmonic < 100) { lbajardsilogic@0: lbajardsilogic@0: float hy = lrintf(getYForFrequency(v, fundamental * harmonic)); lbajardsilogic@0: if (hy < 0 || hy > v->height()) break; lbajardsilogic@0: lbajardsilogic@0: int len = 7; lbajardsilogic@0: lbajardsilogic@0: if (harmonic % 2 == 0) { lbajardsilogic@0: if (harmonic % 4 == 0) { lbajardsilogic@0: len = 12; lbajardsilogic@0: } else { lbajardsilogic@0: len = 10; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: paint.drawLine(cursorPos.x() - len, lbajardsilogic@0: int(hy), lbajardsilogic@0: cursorPos.x(), lbajardsilogic@0: int(hy)); lbajardsilogic@0: lbajardsilogic@0: ++harmonic; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: paint.restore(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const lbajardsilogic@0: { lbajardsilogic@0: int x = pos.x(); lbajardsilogic@0: int y = pos.y(); lbajardsilogic@0: lbajardsilogic@0: if (!m_model || !m_model->isOK()) return ""; lbajardsilogic@0: lbajardsilogic@0: float magMin = 0, magMax = 0; lbajardsilogic@0: float phaseMin = 0, phaseMax = 0; lbajardsilogic@0: float freqMin = 0, freqMax = 0; lbajardsilogic@0: float adjFreqMin = 0, adjFreqMax = 0; lbajardsilogic@0: QString pitchMin, pitchMax; lbajardsilogic@0: RealTime rtMin, rtMax; lbajardsilogic@0: lbajardsilogic@0: bool haveValues = false; lbajardsilogic@0: lbajardsilogic@0: if (!getXBinSourceRange(v, x, rtMin, rtMax)) { lbajardsilogic@0: return ""; lbajardsilogic@0: } lbajardsilogic@0: if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) { lbajardsilogic@0: haveValues = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString adjFreqText = "", adjPitchText = ""; lbajardsilogic@0: lbajardsilogic@0: if (m_binDisplay == PeakFrequencies) { lbajardsilogic@0: lbajardsilogic@0: if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, lbajardsilogic@0: adjFreqMin, adjFreqMax)) { lbajardsilogic@0: return ""; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (adjFreqMin != adjFreqMax) { lbajardsilogic@0: adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") lbajardsilogic@0: .arg(adjFreqMin).arg(adjFreqMax); lbajardsilogic@0: } else { lbajardsilogic@0: adjFreqText = tr("Peak Frequency:\t%1 Hz\n") lbajardsilogic@0: .arg(adjFreqMin); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); lbajardsilogic@0: QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); lbajardsilogic@0: lbajardsilogic@0: if (pmin != pmax) { lbajardsilogic@0: adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); lbajardsilogic@0: } else { lbajardsilogic@0: adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString text; lbajardsilogic@0: lbajardsilogic@0: if (rtMin != rtMax) { lbajardsilogic@0: text += tr("Time:\t%1 - %2\n") lbajardsilogic@0: .arg(rtMin.toText(true).c_str()) lbajardsilogic@0: .arg(rtMax.toText(true).c_str()); lbajardsilogic@0: } else { lbajardsilogic@0: text += tr("Time:\t%1\n") lbajardsilogic@0: .arg(rtMin.toText(true).c_str()); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (freqMin != freqMax) { lbajardsilogic@0: text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") lbajardsilogic@0: .arg(adjFreqText) lbajardsilogic@0: .arg(freqMin) lbajardsilogic@0: .arg(freqMax) lbajardsilogic@0: .arg(adjPitchText) lbajardsilogic@0: .arg(Pitch::getPitchLabelForFrequency(freqMin)) lbajardsilogic@0: .arg(Pitch::getPitchLabelForFrequency(freqMax)); lbajardsilogic@0: } else { lbajardsilogic@0: text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") lbajardsilogic@0: .arg(adjFreqText) lbajardsilogic@0: .arg(freqMin) lbajardsilogic@0: .arg(adjPitchText) lbajardsilogic@0: .arg(Pitch::getPitchLabelForFrequency(freqMin)); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (haveValues) { lbajardsilogic@0: float dbMin = AudioLevel::multiplier_to_dB(magMin); lbajardsilogic@0: float dbMax = AudioLevel::multiplier_to_dB(magMax); lbajardsilogic@0: QString dbMinString; lbajardsilogic@0: QString dbMaxString; lbajardsilogic@0: if (dbMin == AudioLevel::DB_FLOOR) { lbajardsilogic@0: dbMinString = tr("-Inf"); lbajardsilogic@0: } else { lbajardsilogic@0: dbMinString = QString("%1").arg(lrintf(dbMin)); lbajardsilogic@0: } lbajardsilogic@0: if (dbMax == AudioLevel::DB_FLOOR) { lbajardsilogic@0: dbMaxString = tr("-Inf"); lbajardsilogic@0: } else { lbajardsilogic@0: dbMaxString = QString("%1").arg(lrintf(dbMax)); lbajardsilogic@0: } lbajardsilogic@0: if (lrintf(dbMin) != lrintf(dbMax)) { lbajardsilogic@0: text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); lbajardsilogic@0: } else { lbajardsilogic@0: text += tr("dB:\t%1").arg(dbMinString); lbajardsilogic@0: } lbajardsilogic@0: if (phaseMin != phaseMax) { lbajardsilogic@0: text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); lbajardsilogic@0: } else { lbajardsilogic@0: text += tr("\nPhase:\t%1").arg(phaseMin); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return text; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getColourScaleWidth(QPainter &paint) const lbajardsilogic@0: { lbajardsilogic@0: int cw; lbajardsilogic@0: lbajardsilogic@0: cw = paint.fontMetrics().width("-80dB"); lbajardsilogic@0: lbajardsilogic@0: return cw; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getVerticalScaleWidth(View *, QPainter &paint) const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model || !m_model->isOK()) return 0; lbajardsilogic@0: lbajardsilogic@0: int cw = getColourScaleWidth(paint); lbajardsilogic@0: lbajardsilogic@0: int tw = paint.fontMetrics().width(QString("%1") lbajardsilogic@0: .arg(m_maxFrequency > 0 ? lbajardsilogic@0: m_maxFrequency - 1 : lbajardsilogic@0: m_model->getSampleRate() / 2)); lbajardsilogic@0: lbajardsilogic@0: int fw = paint.fontMetrics().width(tr("43Hz")); lbajardsilogic@0: if (tw < fw) tw = fw; lbajardsilogic@0: lbajardsilogic@0: int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); lbajardsilogic@0: lbajardsilogic@0: return cw + tickw + tw + 13; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model || !m_model->isOK()) { lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Profiler profiler("SpectrogramLayer::paintVerticalScale", true); lbajardsilogic@0: lbajardsilogic@0: //!!! cache this? lbajardsilogic@0: lbajardsilogic@0: int h = rect.height(), w = rect.width(); lbajardsilogic@0: lbajardsilogic@0: int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); lbajardsilogic@0: int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0); lbajardsilogic@0: lbajardsilogic@0: size_t bins = m_fftSize / 2; lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: lbajardsilogic@0: if (m_maxFrequency > 0) { lbajardsilogic@0: bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); lbajardsilogic@0: if (bins > m_fftSize / 2) bins = m_fftSize / 2; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int cw = getColourScaleWidth(paint); lbajardsilogic@0: int cbw = paint.fontMetrics().width("dB"); lbajardsilogic@0: lbajardsilogic@0: int py = -1; lbajardsilogic@0: int textHeight = paint.fontMetrics().height(); lbajardsilogic@0: int toff = -textHeight + paint.fontMetrics().ascent() + 2; lbajardsilogic@0: lbajardsilogic@0: if (h > textHeight * 3 + 10) { lbajardsilogic@0: lbajardsilogic@0: int topLines = 2; lbajardsilogic@0: if (m_colourScale == PhaseColourScale) topLines = 1; lbajardsilogic@0: lbajardsilogic@0: int ch = h - textHeight * (topLines + 1) - 8; lbajardsilogic@0: // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); lbajardsilogic@0: paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); lbajardsilogic@0: lbajardsilogic@0: QString top, bottom; lbajardsilogic@0: float min = m_viewMags[v].getMin(); lbajardsilogic@0: float max = m_viewMags[v].getMax(); lbajardsilogic@0: lbajardsilogic@0: float dBmin = AudioLevel::multiplier_to_dB(min); lbajardsilogic@0: float dBmax = AudioLevel::multiplier_to_dB(max); lbajardsilogic@0: lbajardsilogic@0: if (dBmax < -60.f) dBmax = -60.f; lbajardsilogic@0: else top = QString("%1").arg(lrintf(dBmax)); lbajardsilogic@0: lbajardsilogic@0: if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; lbajardsilogic@0: bottom = QString("%1").arg(lrintf(dBmin)); lbajardsilogic@0: lbajardsilogic@0: //!!! & phase etc lbajardsilogic@0: lbajardsilogic@0: if (m_colourScale != PhaseColourScale) { lbajardsilogic@0: paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2, lbajardsilogic@0: 2 + textHeight + toff, "dBFS"); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2, lbajardsilogic@0: paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top), lbajardsilogic@0: 2 + textHeight * topLines + toff + textHeight/2, top); lbajardsilogic@0: lbajardsilogic@0: paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom), lbajardsilogic@0: h + toff - 3 - textHeight/2, bottom); lbajardsilogic@0: lbajardsilogic@0: paint.save(); lbajardsilogic@0: paint.setBrush(Qt::NoBrush); lbajardsilogic@0: lbajardsilogic@0: int lasty = 0; lbajardsilogic@0: int lastdb = 0; lbajardsilogic@0: lbajardsilogic@0: for (int i = 0; i < ch; ++i) { lbajardsilogic@0: lbajardsilogic@0: float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1)); lbajardsilogic@0: int idb = int(dBval); lbajardsilogic@0: lbajardsilogic@0: float value = AudioLevel::dB_to_multiplier(dBval); lbajardsilogic@0: int colour = getDisplayValue(v, value * m_gain); lbajardsilogic@0: lbajardsilogic@0: paint.setPen(m_palette.getColour(colour)); lbajardsilogic@0: lbajardsilogic@0: int y = textHeight * topLines + 4 + ch - i; lbajardsilogic@0: lbajardsilogic@0: paint.drawLine(5 + cw - cbw, y, cw + 2, y); lbajardsilogic@0: lbajardsilogic@0: if (i == 0) { lbajardsilogic@0: lasty = y; lbajardsilogic@0: lastdb = idb; lbajardsilogic@0: } else if (i < ch - paint.fontMetrics().ascent() && lbajardsilogic@0: idb != lastdb && lbajardsilogic@0: ((abs(y - lasty) > textHeight && lbajardsilogic@0: idb % 10 == 0) || lbajardsilogic@0: (abs(y - lasty) > paint.fontMetrics().ascent() && lbajardsilogic@0: idb % 5 == 0))) { lbajardsilogic@0: paint.setPen(Qt::black); lbajardsilogic@0: QString text = QString("%1").arg(idb); lbajardsilogic@0: paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text), lbajardsilogic@0: y + toff + textHeight/2, text); lbajardsilogic@0: paint.setPen(Qt::white); lbajardsilogic@0: paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y); lbajardsilogic@0: lasty = y; lbajardsilogic@0: lastdb = idb; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: paint.restore(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: paint.drawLine(cw + 7, 0, cw + 7, h); lbajardsilogic@0: lbajardsilogic@0: int bin = -1; lbajardsilogic@0: lbajardsilogic@0: for (int y = 0; y < v->height(); ++y) { lbajardsilogic@0: lbajardsilogic@0: float q0, q1; lbajardsilogic@0: if (!getYBinRange(v, v->height() - y, q0, q1)) continue; lbajardsilogic@0: lbajardsilogic@0: int vy; lbajardsilogic@0: lbajardsilogic@0: if (int(q0) > bin) { lbajardsilogic@0: vy = y; lbajardsilogic@0: bin = int(q0); lbajardsilogic@0: } else { lbajardsilogic@0: continue; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int freq = (sr * bin) / m_fftSize; lbajardsilogic@0: lbajardsilogic@0: if (py >= 0 && (vy - py) < textHeight - 1) { lbajardsilogic@0: if (m_frequencyScale == LinearFrequencyScale) { lbajardsilogic@0: paint.drawLine(w - tickw, h - vy, w, h - vy); lbajardsilogic@0: } lbajardsilogic@0: continue; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString text = QString("%1").arg(freq); lbajardsilogic@0: if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC lbajardsilogic@0: paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); lbajardsilogic@0: lbajardsilogic@0: if (h - vy - textHeight >= -2) { lbajardsilogic@191: int tx = w - 3 - paint.fontMetrics().width(text) - MAX(tickw, pkw); lbajardsilogic@0: paint.drawText(tx, h - vy + toff, text); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: py = vy; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_frequencyScale == LogFrequencyScale) { lbajardsilogic@0: lbajardsilogic@0: paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h); lbajardsilogic@0: lbajardsilogic@0: float minf = getEffectiveMinFrequency(); lbajardsilogic@0: float maxf = getEffectiveMaxFrequency(); lbajardsilogic@0: lbajardsilogic@0: int py = h, ppy = h; lbajardsilogic@0: paint.setBrush(paint.pen().color()); lbajardsilogic@0: lbajardsilogic@0: for (int i = 0; i < 128; ++i) { lbajardsilogic@0: lbajardsilogic@0: float f = Pitch::getFrequencyForPitch(i); lbajardsilogic@0: int y = lrintf(v->getYForFrequency(f, minf, maxf, true)); lbajardsilogic@0: lbajardsilogic@0: if (y < -2) break; lbajardsilogic@0: if (y > h + 2) { lbajardsilogic@0: continue; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int n = (i % 12); lbajardsilogic@0: lbajardsilogic@0: if (n == 1) { lbajardsilogic@0: // C# -- fill the C from here lbajardsilogic@0: if (ppy - y > 2) { lbajardsilogic@0: paint.fillRect(w - pkw, lbajardsilogic@0: // y - (py - y) / 2 - (py - y) / 4, lbajardsilogic@0: y, lbajardsilogic@0: pkw, lbajardsilogic@0: (py + ppy) / 2 - y, lbajardsilogic@0: // py - y + 1, lbajardsilogic@0: Qt::gray); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { lbajardsilogic@0: // black notes lbajardsilogic@0: paint.drawLine(w - pkw, y, w, y); lbajardsilogic@0: int rh = ((py - y) / 4) * 2; lbajardsilogic@0: if (rh < 2) rh = 2; lbajardsilogic@0: paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh); lbajardsilogic@0: } else if (n == 0 || n == 5) { lbajardsilogic@0: // C, F lbajardsilogic@0: if (py < h) { lbajardsilogic@0: paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: ppy = py; lbajardsilogic@0: py = y; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: class SpectrogramRangeMapper : public RangeMapper lbajardsilogic@0: { lbajardsilogic@0: public: lbajardsilogic@0: SpectrogramRangeMapper(int sr, int /* fftsize */) : lbajardsilogic@0: m_dist(float(sr) / 2), lbajardsilogic@0: m_s2(sqrtf(sqrtf(2))) { } lbajardsilogic@0: ~SpectrogramRangeMapper() { } lbajardsilogic@0: lbajardsilogic@0: virtual int getPositionForValue(float value) const { lbajardsilogic@0: lbajardsilogic@0: float dist = m_dist; lbajardsilogic@0: lbajardsilogic@0: int n = 0; lbajardsilogic@0: lbajardsilogic@0: while (dist > (value + 0.00001) && dist > 0.1f) { lbajardsilogic@0: dist /= m_s2; lbajardsilogic@0: ++n; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return n; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: virtual float getValueForPosition(int position) const { lbajardsilogic@0: lbajardsilogic@0: // Vertical zoom step 0 shows the entire range from DC -> lbajardsilogic@0: // Nyquist frequency. Step 1 shows 2^(1/4) of the range of lbajardsilogic@0: // step 0, and so on until the visible range is smaller than lbajardsilogic@0: // the frequency step between bins at the current fft size. lbajardsilogic@0: lbajardsilogic@0: float dist = m_dist; lbajardsilogic@0: lbajardsilogic@0: int n = 0; lbajardsilogic@0: while (n < position) { lbajardsilogic@0: dist /= m_s2; lbajardsilogic@0: ++n; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return dist; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: virtual QString getUnit() const { return "Hz"; } lbajardsilogic@0: lbajardsilogic@0: protected: lbajardsilogic@0: float m_dist; lbajardsilogic@0: float m_s2; lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return 0; lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: lbajardsilogic@0: SpectrogramRangeMapper mapper(sr, m_fftSize); lbajardsilogic@0: lbajardsilogic@0: // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001); lbajardsilogic@0: int maxStep = mapper.getPositionForValue(0); lbajardsilogic@0: int minStep = mapper.getPositionForValue(float(sr) / 2); lbajardsilogic@0: lbajardsilogic@0: defaultStep = mapper.getPositionForValue(m_initialMaxFrequency) - minStep; lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << m_initialMaxFrequency << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: return maxStep - minStep; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrogramLayer::getCurrentVerticalZoomStep() const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return 0; lbajardsilogic@0: lbajardsilogic@0: float dmin, dmax; lbajardsilogic@0: getDisplayExtents(dmin, dmax); lbajardsilogic@0: lbajardsilogic@0: SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize); lbajardsilogic@0: int n = mapper.getPositionForValue(dmax - dmin); lbajardsilogic@0: // std::cerr << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << std::endl; lbajardsilogic@0: return n; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setVerticalZoomStep(int step) lbajardsilogic@0: { lbajardsilogic@0: //!!! does not do the right thing for log scale lbajardsilogic@0: lbajardsilogic@0: if (!m_model) return; lbajardsilogic@0: lbajardsilogic@0: float dmin, dmax; lbajardsilogic@0: getDisplayExtents(dmin, dmax); lbajardsilogic@0: lbajardsilogic@0: int sr = m_model->getSampleRate(); lbajardsilogic@0: SpectrogramRangeMapper mapper(sr, m_fftSize); lbajardsilogic@0: float ddist = mapper.getValueForPosition(step); lbajardsilogic@0: lbajardsilogic@0: float dmid = (dmax + dmin) / 2; lbajardsilogic@0: float newmin = dmid - ddist / 2; lbajardsilogic@0: float newmax = dmid + ddist / 2; lbajardsilogic@0: lbajardsilogic@0: float mmin, mmax; lbajardsilogic@0: mmin = 0; lbajardsilogic@0: mmax = float(sr) / 2; lbajardsilogic@0: lbajardsilogic@0: if (newmin < mmin) { lbajardsilogic@0: newmax += (mmin - newmin); lbajardsilogic@0: newmin = mmin; lbajardsilogic@0: } lbajardsilogic@0: if (newmax > mmax) { lbajardsilogic@0: newmax = mmax; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << ddist << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: setMinFrequency(int(newmin)); lbajardsilogic@0: setMaxFrequency(int(newmax)); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: RangeMapper * lbajardsilogic@0: SpectrogramLayer::getNewVerticalZoomRangeMapper() const lbajardsilogic@0: { lbajardsilogic@0: if (!m_model) return 0; lbajardsilogic@0: return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const lbajardsilogic@0: { lbajardsilogic@0: QString s; lbajardsilogic@0: lbajardsilogic@0: s += QString("channel=\"%1\" " lbajardsilogic@0: "windowSize=\"%2\" " lbajardsilogic@0: "windowHopLevel=\"%3\" " lbajardsilogic@0: "gain=\"%4\" " lbajardsilogic@0: "threshold=\"%5\" ") lbajardsilogic@0: .arg(m_channel) lbajardsilogic@0: .arg(m_windowSize) lbajardsilogic@0: .arg(m_windowHopLevel) lbajardsilogic@0: .arg(m_gain) lbajardsilogic@0: .arg(m_threshold); lbajardsilogic@0: lbajardsilogic@0: s += QString("minFrequency=\"%1\" " lbajardsilogic@0: "maxFrequency=\"%2\" " lbajardsilogic@0: "colourScale=\"%3\" " lbajardsilogic@0: "colourScheme=\"%4\" " lbajardsilogic@0: "colourRotation=\"%5\" " lbajardsilogic@0: "frequencyScale=\"%6\" " lbajardsilogic@0: "binDisplay=\"%7\" " lbajardsilogic@0: "normalizeColumns=\"%8\" " lbajardsilogic@0: "normalizeVisibleArea=\"%9\"") lbajardsilogic@0: .arg(m_minFrequency) lbajardsilogic@0: .arg(m_maxFrequency) lbajardsilogic@0: .arg(m_colourScale) lbajardsilogic@0: .arg(m_colourMap) lbajardsilogic@0: .arg(m_colourRotation) lbajardsilogic@0: .arg(m_frequencyScale) lbajardsilogic@0: .arg(m_binDisplay) lbajardsilogic@0: .arg(m_normalizeColumns ? "true" : "false") lbajardsilogic@0: .arg(m_normalizeVisibleArea ? "true" : "false"); lbajardsilogic@0: lbajardsilogic@0: return Layer::toXmlString(indent, extraAttributes + " " + s); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@191: QString lbajardsilogic@191: SpectrogramLayer::toEasaierXmlString(QString indent, QString extraAttributes) const lbajardsilogic@191: { lbajardsilogic@191: QString s; lbajardsilogic@191: lbajardsilogic@191: s += QString("channel=\"%1\" " lbajardsilogic@191: "windowSize=\"%2\" " lbajardsilogic@191: "windowType=\"%3\" " lbajardsilogic@191: "windowHopLevel=\"%4\" " lbajardsilogic@191: "gain=\"%5\" " lbajardsilogic@191: "threshold=\"%6\" ") lbajardsilogic@191: .arg(m_channel) lbajardsilogic@191: .arg(m_windowSize) lbajardsilogic@191: .arg(m_windowType) lbajardsilogic@191: .arg(m_windowHopLevel) lbajardsilogic@191: .arg(m_gain) lbajardsilogic@191: .arg(m_threshold); lbajardsilogic@191: lbajardsilogic@191: lbajardsilogic@191: lbajardsilogic@18: s += QString("minFrequency=\"%1\" " lbajardsilogic@18: "maxFrequency=\"%2\" " lbajardsilogic@18: "colourScale=\"%3\" " lbajardsilogic@18: "colourScheme=\"%4\" " lbajardsilogic@18: "colourRotation=\"%5\" " lbajardsilogic@18: "frequencyScale=\"%6\" " lbajardsilogic@18: "binDisplay=\"%7\" " lbajardsilogic@18: "normalizeColumns=\"%8\" " lbajardsilogic@18: "normalizeVisibleArea=\"%9\"") lbajardsilogic@18: .arg(m_minFrequency) lbajardsilogic@18: .arg(m_maxFrequency) lbajardsilogic@18: .arg(m_colourScale) lbajardsilogic@18: .arg(m_colourMap) lbajardsilogic@18: .arg(m_colourRotation) lbajardsilogic@18: .arg(m_frequencyScale) lbajardsilogic@18: .arg(m_binDisplay) lbajardsilogic@18: .arg(m_normalizeColumns ? "true" : "false") lbajardsilogic@191: .arg(m_normalizeVisibleArea ? "true" : "false"); lbajardsilogic@191: lbajardsilogic@191: return Layer::toEasaierXmlString(indent, extraAttributes + " " + s); lbajardsilogic@18: } lbajardsilogic@18: lbajardsilogic@0: void lbajardsilogic@0: SpectrogramLayer::setProperties(const QXmlAttributes &attributes) lbajardsilogic@0: { lbajardsilogic@0: bool ok = false; lbajardsilogic@0: lbajardsilogic@0: int channel = attributes.value("channel").toInt(&ok); lbajardsilogic@0: if (ok) setChannel(channel); lbajardsilogic@0: lbajardsilogic@0: size_t windowSize = attributes.value("windowSize").toUInt(&ok); lbajardsilogic@0: if (ok) setWindowSize(windowSize); lbajardsilogic@0: lbajardsilogic@0: size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); lbajardsilogic@0: if (ok) setWindowHopLevel(windowHopLevel); lbajardsilogic@0: else { lbajardsilogic@0: size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok); lbajardsilogic@0: // a percentage value lbajardsilogic@0: if (ok) { lbajardsilogic@0: if (windowOverlap == 0) setWindowHopLevel(0); lbajardsilogic@0: else if (windowOverlap == 25) setWindowHopLevel(1); lbajardsilogic@0: else if (windowOverlap == 50) setWindowHopLevel(2); lbajardsilogic@0: else if (windowOverlap == 75) setWindowHopLevel(3); lbajardsilogic@0: else if (windowOverlap == 90) setWindowHopLevel(4); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float gain = attributes.value("gain").toFloat(&ok); lbajardsilogic@0: if (ok) setGain(gain); lbajardsilogic@0: lbajardsilogic@0: float threshold = attributes.value("threshold").toFloat(&ok); lbajardsilogic@0: if (ok) setThreshold(threshold); lbajardsilogic@0: lbajardsilogic@0: size_t minFrequency = attributes.value("minFrequency").toUInt(&ok); lbajardsilogic@0: if (ok) { lbajardsilogic@0: std::cerr << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << std::endl; lbajardsilogic@0: setMinFrequency(minFrequency); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok); lbajardsilogic@0: if (ok) { lbajardsilogic@0: std::cerr << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << std::endl; lbajardsilogic@0: setMaxFrequency(maxFrequency); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: ColourScale colourScale = (ColourScale) lbajardsilogic@0: attributes.value("colourScale").toInt(&ok); lbajardsilogic@0: if (ok) setColourScale(colourScale); lbajardsilogic@0: lbajardsilogic@0: int colourMap = attributes.value("colourScheme").toInt(&ok); lbajardsilogic@0: if (ok) setColourMap(colourMap); lbajardsilogic@0: lbajardsilogic@0: int colourRotation = attributes.value("colourRotation").toInt(&ok); lbajardsilogic@0: if (ok) setColourRotation(colourRotation); lbajardsilogic@0: lbajardsilogic@0: FrequencyScale frequencyScale = (FrequencyScale) lbajardsilogic@0: attributes.value("frequencyScale").toInt(&ok); lbajardsilogic@0: if (ok) setFrequencyScale(frequencyScale); lbajardsilogic@0: lbajardsilogic@0: BinDisplay binDisplay = (BinDisplay) lbajardsilogic@0: attributes.value("binDisplay").toInt(&ok); lbajardsilogic@0: if (ok) setBinDisplay(binDisplay); lbajardsilogic@0: lbajardsilogic@0: bool normalizeColumns = lbajardsilogic@0: (attributes.value("normalizeColumns").trimmed() == "true"); lbajardsilogic@0: setNormalizeColumns(normalizeColumns); lbajardsilogic@0: lbajardsilogic@0: bool normalizeVisibleArea = lbajardsilogic@0: (attributes.value("normalizeVisibleArea").trimmed() == "true"); lbajardsilogic@0: setNormalizeVisibleArea(normalizeVisibleArea); lbajardsilogic@0: } lbajardsilogic@0: