Chris@133: Chris@133: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@133: Chris@133: /* Chris@133: Sonic Visualiser Chris@133: An audio file viewer and annotation editor. Chris@133: Centre for Digital Music, Queen Mary, University of London. Chris@195: This file copyright 2006-2007 QMUL. Chris@133: Chris@133: This program is free software; you can redistribute it and/or Chris@133: modify it under the terms of the GNU General Public License as Chris@133: published by the Free Software Foundation; either version 2 of the Chris@133: License, or (at your option) any later version. See the file Chris@133: COPYING included with this distribution for more information. Chris@133: */ Chris@133: Chris@133: #include "SpectrumLayer.h" Chris@133: Chris@133: #include "data/model/FFTModel.h" Chris@133: #include "view/View.h" Chris@153: #include "base/AudioLevel.h" Chris@153: #include "base/Preferences.h" Chris@167: #include "base/RangeMapper.h" Chris@133: Chris@133: SpectrumLayer::SpectrumLayer() : Chris@193: m_originModel(0), Chris@153: m_channel(-1), Chris@153: m_channelSet(false), Chris@153: m_windowSize(1024), Chris@153: m_windowType(HanningWindow), Chris@153: m_windowHopLevel(2) Chris@133: { Chris@153: Preferences *prefs = Preferences::getInstance(); Chris@153: connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), Chris@153: this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); Chris@153: setWindowType(prefs->getWindowType()); Chris@195: Chris@195: setBinScale(LogBins); Chris@133: } Chris@133: Chris@133: SpectrumLayer::~SpectrumLayer() Chris@133: { Chris@193: //!!! delete parent's model Chris@193: // for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; Chris@133: } Chris@133: Chris@133: void Chris@133: SpectrumLayer::setModel(DenseTimeValueModel *model) Chris@133: { Chris@193: if (m_originModel == model) return; Chris@193: m_originModel = model; Chris@193: setupFFT(); Chris@153: } Chris@153: Chris@153: void Chris@193: SpectrumLayer::setupFFT() Chris@153: { Chris@193: FFTModel *oldFFT = dynamic_cast Chris@193: (const_cast(m_sliceableModel)); Chris@153: Chris@193: if (oldFFT) { Chris@193: setSliceableModel(0); Chris@193: delete oldFFT; Chris@153: } Chris@153: Chris@193: FFTModel *newFFT = new FFTModel(m_originModel, Chris@193: m_channel, Chris@193: m_windowType, Chris@193: m_windowSize, Chris@193: getWindowIncrement(), Chris@193: m_windowSize, Chris@193: true); Chris@153: Chris@193: setSliceableModel(newFFT); Chris@193: Chris@193: newFFT->resume(); Chris@153: } Chris@153: Chris@153: void Chris@153: SpectrumLayer::setChannel(int channel) Chris@153: { Chris@153: m_channelSet = true; Chris@153: Chris@193: FFTModel *fft = dynamic_cast Chris@193: (const_cast(m_sliceableModel)); Chris@193: Chris@153: if (m_channel == channel) { Chris@193: if (fft) fft->resume(); Chris@153: return; Chris@153: } Chris@153: Chris@153: m_channel = channel; Chris@153: Chris@193: if (!fft) setupFFT(); Chris@153: Chris@153: emit layerParametersChanged(); Chris@133: } Chris@133: Chris@153: Layer::PropertyList Chris@153: SpectrumLayer::getProperties() const Chris@153: { Chris@193: PropertyList list = SliceLayer::getProperties(); Chris@153: list.push_back("Window Size"); Chris@153: list.push_back("Window Increment"); Chris@153: return list; Chris@153: } Chris@153: Chris@153: QString Chris@153: SpectrumLayer::getPropertyLabel(const PropertyName &name) const Chris@153: { Chris@153: if (name == "Window Size") return tr("Window Size"); Chris@153: if (name == "Window Increment") return tr("Window Overlap"); Chris@193: return SliceLayer::getPropertyLabel(name); Chris@153: } Chris@153: Chris@153: Layer::PropertyType Chris@153: SpectrumLayer::getPropertyType(const PropertyName &name) const Chris@153: { Chris@193: if (name == "Window Size") return ValueProperty; Chris@193: if (name == "Window Increment") return ValueProperty; Chris@193: return SliceLayer::getPropertyType(name); Chris@153: } Chris@153: Chris@153: QString Chris@153: SpectrumLayer::getPropertyGroupName(const PropertyName &name) const Chris@153: { Chris@153: if (name == "Window Size" || Chris@153: name == "Window Increment") return tr("Window"); Chris@193: return SliceLayer::getPropertyGroupName(name); Chris@153: } Chris@153: Chris@153: int Chris@153: SpectrumLayer::getPropertyRangeAndValue(const PropertyName &name, Chris@216: int *min, int *max, int *deflt) const Chris@153: { Chris@216: int val = 0; Chris@153: Chris@216: int garbage0, garbage1, garbage2; Chris@153: if (!min) min = &garbage0; Chris@153: if (!max) max = &garbage1; Chris@216: if (!deflt) deflt = &garbage2; Chris@153: Chris@193: if (name == "Window Size") { Chris@153: Chris@153: *min = 0; Chris@153: *max = 10; Chris@216: *deflt = 5; Chris@153: Chris@216: val = 0; Chris@153: int ws = m_windowSize; Chris@216: while (ws > 32) { ws >>= 1; val ++; } Chris@153: Chris@153: } else if (name == "Window Increment") { Chris@153: Chris@153: *min = 0; Chris@153: *max = 5; Chris@216: *deflt = 2; Chris@153: Chris@216: val = m_windowHopLevel; Chris@153: Chris@153: } else { Chris@193: Chris@216: val = SliceLayer::getPropertyRangeAndValue(name, min, max, deflt); Chris@153: } Chris@153: Chris@216: return val; Chris@153: } Chris@153: Chris@153: QString Chris@153: SpectrumLayer::getPropertyValueLabel(const PropertyName &name, Chris@153: int value) const Chris@153: { Chris@153: if (name == "Window Size") { Chris@153: return QString("%1").arg(32 << value); Chris@153: } Chris@153: if (name == "Window Increment") { Chris@153: switch (value) { Chris@153: default: Chris@153: case 0: return tr("None"); Chris@153: case 1: return tr("25 %"); Chris@153: case 2: return tr("50 %"); Chris@153: case 3: return tr("75 %"); Chris@153: case 4: return tr("87.5 %"); Chris@153: case 5: return tr("93.75 %"); Chris@153: } Chris@153: } Chris@193: return SliceLayer::getPropertyValueLabel(name, value); Chris@153: } Chris@153: Chris@167: RangeMapper * Chris@167: SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const Chris@167: { Chris@193: return SliceLayer::getNewPropertyRangeMapper(name); Chris@167: } Chris@167: Chris@133: void Chris@153: SpectrumLayer::setProperty(const PropertyName &name, int value) Chris@133: { Chris@193: if (name == "Window Size") { Chris@153: setWindowSize(32 << value); Chris@153: } else if (name == "Window Increment") { Chris@153: setWindowHopLevel(value); Chris@193: } else { Chris@193: SliceLayer::setProperty(name, value); Chris@153: } Chris@153: } Chris@153: Chris@153: void Chris@153: SpectrumLayer::setWindowSize(size_t ws) Chris@153: { Chris@153: if (m_windowSize == ws) return; Chris@153: m_windowSize = ws; Chris@193: setupFFT(); Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@153: SpectrumLayer::setWindowHopLevel(size_t v) Chris@153: { Chris@153: if (m_windowHopLevel == v) return; Chris@153: m_windowHopLevel = v; Chris@193: setupFFT(); Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@153: SpectrumLayer::setWindowType(WindowType w) Chris@153: { Chris@153: if (m_windowType == w) return; Chris@153: m_windowType = w; Chris@193: setupFFT(); Chris@153: emit layerParametersChanged(); Chris@153: } Chris@153: Chris@153: void Chris@153: SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name) Chris@153: { Chris@153: if (name == "Window Type") { Chris@153: setWindowType(Preferences::getInstance()->getWindowType()); Chris@153: return; Chris@153: } Chris@153: } Chris@153: Chris@153: QString Chris@153: SpectrumLayer::toXmlString(QString indent, QString extraAttributes) const Chris@153: { Chris@153: QString s; Chris@153: Chris@193: s += QString("windowSize=\"%1\" " Chris@193: "windowHopLevel=\"%2\"") Chris@153: .arg(m_windowSize) Chris@193: .arg(m_windowHopLevel); Chris@153: Chris@193: return SliceLayer::toXmlString(indent, extraAttributes + " " + s); Chris@153: } Chris@153: Chris@153: void Chris@153: SpectrumLayer::setProperties(const QXmlAttributes &attributes) Chris@153: { Chris@153: bool ok = false; Chris@153: Chris@153: size_t windowSize = attributes.value("windowSize").toUInt(&ok); Chris@153: if (ok) setWindowSize(windowSize); Chris@153: Chris@153: size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); Chris@153: if (ok) setWindowHopLevel(windowHopLevel); Chris@133: } Chris@133: Chris@133: bool Chris@133: SpectrumLayer::getValueExtents(float &min, float &max, bool &logarithmic, Chris@133: QString &units) const Chris@133: { Chris@133: return false; Chris@133: } Chris@133: Chris@199: QString Chris@199: SpectrumLayer::getFeatureDescription(View *v, QPoint &p) const Chris@199: { Chris@199: if (!m_sliceableModel) return ""; Chris@199: Chris@199: int minbin = 0, maxbin = 0, range = 0; Chris@199: QString genericDesc = SliceLayer::getFeatureDescription Chris@199: (v, p, false, minbin, maxbin, range); Chris@199: Chris@199: if (genericDesc == "") return ""; Chris@199: Chris@199: float minvalue = 0.f; Chris@199: if (minbin < m_values.size()) minvalue = m_values[minbin]; Chris@199: Chris@199: float maxvalue = minvalue; Chris@199: if (maxbin < m_values.size()) maxvalue = m_values[maxbin]; Chris@199: Chris@199: if (minvalue > maxvalue) std::swap(minvalue, maxvalue); Chris@199: Chris@199: QString binstr; Chris@199: QString hzstr; Chris@199: int minfreq = lrintf((minbin * m_sliceableModel->getSampleRate()) / Chris@199: m_windowSize); Chris@199: int maxfreq = lrintf((std::max(maxbin, minbin+1) Chris@199: * m_sliceableModel->getSampleRate()) / Chris@199: m_windowSize); Chris@199: Chris@199: if (maxbin != minbin) { Chris@199: binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); Chris@199: } else { Chris@199: binstr = QString("%1").arg(minbin+1); Chris@199: } Chris@199: if (minfreq != maxfreq) { Chris@199: hzstr = tr("%1 - %2 Hz").arg(minfreq).arg(maxfreq); Chris@199: } else { Chris@199: hzstr = tr("%1 Hz").arg(minfreq); Chris@199: } Chris@199: Chris@199: QString valuestr; Chris@199: if (maxvalue != minvalue) { Chris@199: valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue); Chris@199: } else { Chris@199: valuestr = QString("%1").arg(minvalue); Chris@199: } Chris@199: Chris@199: QString dbstr; Chris@199: float mindb = AudioLevel::multiplier_to_dB(minvalue); Chris@199: float maxdb = AudioLevel::multiplier_to_dB(maxvalue); Chris@199: QString mindbstr; Chris@199: QString maxdbstr; Chris@199: if (mindb == AudioLevel::DB_FLOOR) { Chris@199: mindbstr = tr("-Inf"); Chris@199: } else { Chris@199: mindbstr = QString("%1").arg(lrintf(mindb)); Chris@199: } Chris@199: if (maxdb == AudioLevel::DB_FLOOR) { Chris@199: maxdbstr = tr("-Inf"); Chris@199: } else { Chris@199: maxdbstr = QString("%1").arg(lrintf(maxdb)); Chris@199: } Chris@199: if (lrintf(mindb) != lrintf(maxdb)) { Chris@199: dbstr = tr("%1 - %2").arg(mindbstr).arg(maxdbstr); Chris@199: } else { Chris@199: dbstr = tr("%1").arg(mindbstr); Chris@199: } Chris@199: Chris@199: QString description; Chris@199: Chris@199: if (range > m_sliceableModel->getResolution()) { Chris@199: description = tr("%1\nBin:\t%2 (%3)\n%4 value:\t%5\ndB:\t%6") Chris@199: .arg(genericDesc) Chris@199: .arg(binstr) Chris@199: .arg(hzstr) Chris@199: .arg(m_samplingMode == NearestSample ? tr("First") : Chris@199: m_samplingMode == SampleMean ? tr("Mean") : tr("Peak")) Chris@199: .arg(valuestr) Chris@199: .arg(dbstr); Chris@199: } else { Chris@199: description = tr("%1\nBin:\t%2 (%3)\nValue:\t%4\ndB:\t%5") Chris@199: .arg(genericDesc) Chris@199: .arg(binstr) Chris@199: .arg(hzstr) Chris@199: .arg(valuestr) Chris@199: .arg(dbstr); Chris@199: } Chris@199: Chris@199: return description; Chris@199: } Chris@199: Chris@199: Chris@199: