Mercurial > hg > svgui
view layer/SpectrumLayer.cpp @ 167:53b9c7656798
* Use RangeMappers in various places in plugin parameters, layer properties,
playback parameters &c
author | Chris Cannam |
---|---|
date | Mon, 16 Oct 2006 20:42:23 +0000 |
parents | aaa3a53dbb10 |
children | 42118892f428 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "SpectrumLayer.h" #include "data/model/FFTModel.h" #include "view/View.h" #include "base/AudioLevel.h" #include "base/Preferences.h" #include "base/RangeMapper.h" #include <QPainter> #include <QPainterPath> SpectrumLayer::SpectrumLayer() : m_model(0), m_channelMode(MixChannels), m_channel(-1), m_channelSet(false), m_colour(Qt::darkBlue), m_energyScale(dBScale), m_normalize(false), m_gain(1.0), m_windowSize(1024), m_windowType(HanningWindow), m_windowHopLevel(2) { Preferences *prefs = Preferences::getInstance(); connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); setWindowType(prefs->getWindowType()); } SpectrumLayer::~SpectrumLayer() { for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; } void SpectrumLayer::setModel(DenseTimeValueModel *model) { m_model = model; setupFFTs(); } void SpectrumLayer::setupFFTs() { for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; m_fft.clear(); int minChannel = m_channel, maxChannel = m_channel; if (m_channel == -1 && m_channelMode != MixChannels) { minChannel = 0; maxChannel = 0; if (m_model->getChannelCount() > 1) { maxChannel = m_model->getChannelCount() - 1; } } for (int c = minChannel; c <= maxChannel; ++c) { m_fft.push_back(new FFTModel(m_model, c, HanningWindow, m_windowSize, getWindowIncrement(), m_windowSize, true)); if (m_channelSet) m_fft[m_fft.size()-1]->resume(); } } void SpectrumLayer::setChannel(int channel) { m_channelSet = true; if (m_channel == channel) { for (size_t i = 0; i < m_fft.size(); ++i) { m_fft[i]->resume(); } return; } m_channel = channel; if (!m_fft.empty()) setupFFTs(); emit layerParametersChanged(); } void SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const { if (m_fft.empty()) return; if (!m_channelSet) { for (size_t i = 0; i < m_fft.size(); ++i) { m_fft[i]->resume(); } } FFTModel *fft = m_fft[0]; //!!! for now int windowIncrement = getWindowIncrement(); size_t f = v->getCentreFrame(); int w = (v->width() * 2) / 3; int xorigin = (v->width() / 2) - (w / 2); int h = (v->height() * 2) / 3; int yorigin = (v->height() / 2) + (h / 2); size_t column = f / windowIncrement; paint.save(); paint.setPen(m_colour); paint.setRenderHint(QPainter::Antialiasing, false); QPainterPath path; float thresh = -80.f; for (size_t bin = 0; bin < fft->getHeight(); ++bin) { float x = xorigin + (float(w) * bin) / fft->getHeight(); float mag; if (m_normalize) { mag = fft->getNormalizedMagnitudeAt(column, bin); } else { mag = fft->getMagnitudeAt(column, bin); } mag *= m_gain; float y = 0.f; switch (m_energyScale) { case dBScale: { float db = thresh; if (mag > 0.f) db = 10.f * log10f(mag); if (db < thresh) db = thresh; float val = (db - thresh) / -thresh; y = yorigin - (float(h) * val); break; } case MeterScale: y = yorigin - AudioLevel::multiplier_to_preview(mag, h); break; default: y = yorigin - (float(h) * mag); break; } if (bin == 0) { path.moveTo(x, y); } else { path.lineTo(x, y); } } paint.drawPath(path); paint.restore(); } Layer::PropertyList SpectrumLayer::getProperties() const { PropertyList list; list.push_back("Colour"); list.push_back("Scale"); list.push_back("Normalize"); list.push_back("Gain"); list.push_back("Window Size"); list.push_back("Window Increment"); if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) { list.push_back("Channels"); } return list; } QString SpectrumLayer::getPropertyLabel(const PropertyName &name) const { if (name == "Colour") return tr("Colour"); if (name == "Energy Scale") return tr("Scale"); if (name == "Channels") return tr("Channels"); if (name == "Window Size") return tr("Window Size"); if (name == "Window Increment") return tr("Window Overlap"); if (name == "Normalize") return tr("Normalize"); if (name == "Gain") return tr("Gain"); return ""; } Layer::PropertyType SpectrumLayer::getPropertyType(const PropertyName &name) const { if (name == "Gain") return RangeProperty; if (name == "Normalize") return ToggleProperty; return ValueProperty; } QString SpectrumLayer::getPropertyGroupName(const PropertyName &name) const { if (name == "Window Size" || name == "Window Increment") return tr("Window"); if (name == "Scale" || name == "Normalize" || name == "Gain") return tr("Energy Scale"); return QString(); } int SpectrumLayer::getPropertyRangeAndValue(const PropertyName &name, int *min, int *max) const { int deft = 0; int garbage0, garbage1; if (!min) min = &garbage0; if (!max) max = &garbage1; if (name == "Gain") { *min = -50; *max = 50; deft = lrint(log10(m_gain) * 20.0); if (deft < *min) deft = *min; if (deft > *max) deft = *max; } else if (name == "Normalize") { deft = (m_normalize ? 1 : 0); } else if (name == "Colour") { *min = 0; *max = 5; if (m_colour == Qt::black) deft = 0; else if (m_colour == Qt::darkRed) deft = 1; else if (m_colour == Qt::darkBlue) deft = 2; else if (m_colour == Qt::darkGreen) deft = 3; else if (m_colour == QColor(200, 50, 255)) deft = 4; else if (m_colour == QColor(255, 150, 50)) deft = 5; } else if (name == "Channels") { *min = 0; *max = 2; if (m_channelMode == MixChannels) deft = 1; else if (m_channelMode == OverlayChannels) deft = 2; else deft = 0; } else if (name == "Scale") { *min = 0; *max = 2; deft = (int)m_energyScale; } else if (name == "Window Size") { *min = 0; *max = 10; deft = 0; int ws = m_windowSize; while (ws > 32) { ws >>= 1; deft ++; } } else if (name == "Window Increment") { *min = 0; *max = 5; deft = m_windowHopLevel; } else { deft = Layer::getPropertyRangeAndValue(name, min, max); } return deft; } QString SpectrumLayer::getPropertyValueLabel(const PropertyName &name, int value) const { if (name == "Colour") { switch (value) { default: case 0: return tr("Black"); case 1: return tr("Red"); case 2: return tr("Blue"); case 3: return tr("Green"); case 4: return tr("Purple"); case 5: return tr("Orange"); } } if (name == "Scale") { switch (value) { default: case 0: return tr("Linear"); case 1: return tr("Meter"); case 2: return tr("dB"); } } if (name == "Channels") { switch (value) { default: case 0: return tr("Separate"); case 1: return tr("Mean"); case 2: return tr("Overlay"); } } if (name == "Window Size") { return QString("%1").arg(32 << value); } if (name == "Window Increment") { switch (value) { default: case 0: return tr("None"); case 1: return tr("25 %"); case 2: return tr("50 %"); case 3: return tr("75 %"); case 4: return tr("87.5 %"); case 5: return tr("93.75 %"); } } return tr("<unknown>"); } RangeMapper * SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const { if (name == "Gain") { return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); } return 0; } void SpectrumLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { setGain(pow(10, float(value)/20.0)); } else if (name == "Colour") { switch (value) { default: case 0: setBaseColour(Qt::black); break; case 1: setBaseColour(Qt::darkRed); break; case 2: setBaseColour(Qt::darkBlue); break; case 3: setBaseColour(Qt::darkGreen); break; case 4: setBaseColour(QColor(200, 50, 255)); break; case 5: setBaseColour(QColor(255, 150, 50)); break; } } else if (name == "Channels") { if (value == 1) setChannelMode(MixChannels); else if (value == 2) setChannelMode(OverlayChannels); else setChannelMode(SeparateChannels); } else if (name == "Scale") { switch (value) { default: case 0: setEnergyScale(LinearScale); break; case 1: setEnergyScale(MeterScale); break; case 2: setEnergyScale(dBScale); break; } } else if (name == "Window Size") { setWindowSize(32 << value); } else if (name == "Window Increment") { setWindowHopLevel(value); } else if (name == "Normalize") { setNormalize(value ? true : false); } } void SpectrumLayer::setBaseColour(QColor colour) { if (m_colour == colour) return; m_colour = colour; emit layerParametersChanged(); } void SpectrumLayer::setChannelMode(ChannelMode channelMode) { if (m_channelMode == channelMode) return; m_channelMode = channelMode; emit layerParametersChanged(); } void SpectrumLayer::setEnergyScale(EnergyScale scale) { if (m_energyScale == scale) return; m_energyScale = scale; emit layerParametersChanged(); } void SpectrumLayer::setWindowSize(size_t ws) { if (m_windowSize == ws) return; m_windowSize = ws; setupFFTs(); emit layerParametersChanged(); } void SpectrumLayer::setWindowHopLevel(size_t v) { if (m_windowHopLevel == v) return; m_windowHopLevel = v; setupFFTs(); emit layerParametersChanged(); } void SpectrumLayer::setWindowType(WindowType w) { if (m_windowType == w) return; m_windowType = w; setupFFTs(); emit layerParametersChanged(); } void SpectrumLayer::setNormalize(bool n) { if (m_normalize == n) return; m_normalize = n; emit layerParametersChanged(); } void SpectrumLayer::setGain(float gain) { if (m_gain == gain) return; m_gain = gain; emit layerParametersChanged(); } void SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name) { if (name == "Window Type") { setWindowType(Preferences::getInstance()->getWindowType()); return; } } QString SpectrumLayer::toXmlString(QString indent, QString extraAttributes) const { QString s; s += QString("colour=\"%1\" " "channelMode=\"%2\" " "channel=\"%3\" " "energyScale=\"%4\" " "windowSize=\"%5\" " "windowHopLevel=\"%6\" " "gain=\"%7\" " "normalize=\"%8\"") .arg(encodeColour(m_colour)) .arg(m_channelMode) .arg(m_channel) .arg(m_energyScale) .arg(m_windowSize) .arg(m_windowHopLevel) .arg(m_gain) .arg(m_normalize ? "true" : "false"); return Layer::toXmlString(indent, extraAttributes + " " + s); } void SpectrumLayer::setProperties(const QXmlAttributes &attributes) { bool ok = false; QString colourSpec = attributes.value("colour"); if (colourSpec != "") { QColor colour(colourSpec); if (colour.isValid()) { setBaseColour(QColor(colourSpec)); } } ChannelMode channelMode = (ChannelMode) attributes.value("channelMode").toInt(&ok); if (ok) setChannelMode(channelMode); int channel = attributes.value("channel").toInt(&ok); if (ok) setChannel(channel); EnergyScale scale = (EnergyScale) attributes.value("energyScale").toInt(&ok); if (ok) setEnergyScale(scale); size_t windowSize = attributes.value("windowSize").toUInt(&ok); if (ok) setWindowSize(windowSize); size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); if (ok) setWindowHopLevel(windowHopLevel); float gain = attributes.value("gain").toFloat(&ok); if (ok) setGain(gain); bool normalize = (attributes.value("normalize").trimmed() == "true"); setNormalize(normalize); } bool SpectrumLayer::getValueExtents(float &min, float &max, bool &logarithmic, QString &units) const { return false; }