lbajardsilogic@0: 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-2007 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@0: #include "SpectrumLayer.h" lbajardsilogic@0: lbajardsilogic@0: #include "system/System.h" lbajardsilogic@0: #include "data/model/FFTModel.h" lbajardsilogic@0: #include "view/View.h" lbajardsilogic@0: #include "base/AudioLevel.h" lbajardsilogic@0: #include "base/Preferences.h" lbajardsilogic@0: #include "base/RangeMapper.h" lbajardsilogic@0: lbajardsilogic@0: SpectrumLayer::SpectrumLayer() : lbajardsilogic@0: m_originModel(0), lbajardsilogic@0: m_channel(-1), lbajardsilogic@0: m_channelSet(false), lbajardsilogic@0: m_windowSize(1024), lbajardsilogic@0: m_windowType(HanningWindow), lbajardsilogic@0: m_windowHopLevel(2) 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: setBinScale(LogBins); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: SpectrumLayer::~SpectrumLayer() lbajardsilogic@0: { lbajardsilogic@0: //!!! delete parent's model lbajardsilogic@0: // for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setModel(DenseTimeValueModel *model) lbajardsilogic@0: { lbajardsilogic@0: if (m_originModel == model) return; lbajardsilogic@0: m_originModel = model; lbajardsilogic@0: setupFFT(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setupFFT() lbajardsilogic@0: { lbajardsilogic@0: FFTModel *oldFFT = dynamic_cast lbajardsilogic@0: (const_cast(m_sliceableModel)); lbajardsilogic@0: lbajardsilogic@0: if (oldFFT) { lbajardsilogic@0: setSliceableModel(0); lbajardsilogic@0: delete oldFFT; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTModel *newFFT = new FFTModel(m_originModel, lbajardsilogic@0: m_channel, lbajardsilogic@0: m_windowType, lbajardsilogic@0: m_windowSize, lbajardsilogic@0: getWindowIncrement(), lbajardsilogic@0: m_windowSize, lbajardsilogic@0: true); lbajardsilogic@0: lbajardsilogic@0: setSliceableModel(newFFT); lbajardsilogic@0: lbajardsilogic@0: newFFT->resume(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setChannel(int channel) lbajardsilogic@0: { lbajardsilogic@0: m_channelSet = true; lbajardsilogic@0: lbajardsilogic@0: FFTModel *fft = dynamic_cast lbajardsilogic@0: (const_cast(m_sliceableModel)); lbajardsilogic@0: lbajardsilogic@0: if (m_channel == channel) { lbajardsilogic@0: if (fft) fft->resume(); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_channel = channel; lbajardsilogic@0: lbajardsilogic@0: if (!fft) setupFFT(); lbajardsilogic@0: lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Layer::PropertyList lbajardsilogic@0: SpectrumLayer::getProperties() const lbajardsilogic@0: { lbajardsilogic@0: PropertyList list = SliceLayer::getProperties(); lbajardsilogic@0: list.push_back("Window Size"); lbajardsilogic@0: list.push_back("Window Increment"); lbajardsilogic@0: return list; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrumLayer::getPropertyLabel(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Window Size") return tr("Window Size"); lbajardsilogic@0: if (name == "Window Increment") return tr("Window Overlap"); lbajardsilogic@0: return SliceLayer::getPropertyLabel(name); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: Layer::PropertyType lbajardsilogic@0: SpectrumLayer::getPropertyType(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Window Size") return ValueProperty; lbajardsilogic@0: if (name == "Window Increment") return ValueProperty; lbajardsilogic@0: return SliceLayer::getPropertyType(name); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrumLayer::getPropertyGroupName(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: if (name == "Window Size" || lbajardsilogic@0: name == "Window Increment") return tr("Window"); lbajardsilogic@0: return SliceLayer::getPropertyGroupName(name); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: SpectrumLayer::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 == "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 { lbajardsilogic@0: lbajardsilogic@0: val = SliceLayer::getPropertyRangeAndValue(name, min, max, deflt); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return val; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrumLayer::getPropertyValueLabel(const PropertyName &name, lbajardsilogic@0: int value) const 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: return SliceLayer::getPropertyValueLabel(name, value); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: RangeMapper * lbajardsilogic@0: SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const lbajardsilogic@0: { lbajardsilogic@0: return SliceLayer::getNewPropertyRangeMapper(name); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setProperty(const PropertyName &name, int value) lbajardsilogic@0: { lbajardsilogic@0: if (name == "Window Size") { lbajardsilogic@0: setWindowSize(32 << value); lbajardsilogic@0: } else if (name == "Window Increment") { lbajardsilogic@0: setWindowHopLevel(value); lbajardsilogic@0: } else { lbajardsilogic@0: SliceLayer::setProperty(name, value); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setWindowSize(size_t ws) lbajardsilogic@0: { lbajardsilogic@0: if (m_windowSize == ws) return; lbajardsilogic@0: m_windowSize = ws; lbajardsilogic@0: setupFFT(); lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setWindowHopLevel(size_t v) lbajardsilogic@0: { lbajardsilogic@0: if (m_windowHopLevel == v) return; lbajardsilogic@0: m_windowHopLevel = v; lbajardsilogic@0: setupFFT(); lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setWindowType(WindowType w) lbajardsilogic@0: { lbajardsilogic@0: if (m_windowType == w) return; lbajardsilogic@0: m_windowType = w; lbajardsilogic@0: setupFFT(); lbajardsilogic@0: emit layerParametersChanged(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name) lbajardsilogic@0: { lbajardsilogic@0: if (name == "Window Type") { lbajardsilogic@0: setWindowType(Preferences::getInstance()->getWindowType()); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: SpectrumLayer::getValueExtents(float &, float &, bool &, QString &) const lbajardsilogic@0: { lbajardsilogic@0: return false; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrumLayer::getFeatureDescription(View *v, QPoint &p) const lbajardsilogic@0: { lbajardsilogic@0: if (!m_sliceableModel) return ""; lbajardsilogic@0: lbajardsilogic@0: int minbin = 0, maxbin = 0, range = 0; lbajardsilogic@0: QString genericDesc = SliceLayer::getFeatureDescription lbajardsilogic@0: (v, p, false, minbin, maxbin, range); lbajardsilogic@0: lbajardsilogic@0: if (genericDesc == "") return ""; lbajardsilogic@0: lbajardsilogic@0: float minvalue = 0.f; lbajardsilogic@0: if (minbin < int(m_values.size())) minvalue = m_values[minbin]; lbajardsilogic@0: lbajardsilogic@0: float maxvalue = minvalue; lbajardsilogic@0: if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; lbajardsilogic@0: lbajardsilogic@0: if (minvalue > maxvalue) std::swap(minvalue, maxvalue); lbajardsilogic@0: lbajardsilogic@0: QString binstr; lbajardsilogic@0: QString hzstr; lbajardsilogic@0: int minfreq = lrintf((minbin * m_sliceableModel->getSampleRate()) / lbajardsilogic@0: m_windowSize); lbajardsilogic@191: int maxfreq = lrintf((MAX(maxbin, minbin+1) lbajardsilogic@0: * m_sliceableModel->getSampleRate()) / lbajardsilogic@0: m_windowSize); lbajardsilogic@0: lbajardsilogic@0: if (maxbin != minbin) { lbajardsilogic@0: binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); lbajardsilogic@0: } else { lbajardsilogic@0: binstr = QString("%1").arg(minbin+1); lbajardsilogic@0: } lbajardsilogic@0: if (minfreq != maxfreq) { lbajardsilogic@0: hzstr = tr("%1 - %2 Hz").arg(minfreq).arg(maxfreq); lbajardsilogic@0: } else { lbajardsilogic@0: hzstr = tr("%1 Hz").arg(minfreq); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString valuestr; lbajardsilogic@0: if (maxvalue != minvalue) { lbajardsilogic@0: valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue); lbajardsilogic@0: } else { lbajardsilogic@0: valuestr = QString("%1").arg(minvalue); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString dbstr; lbajardsilogic@0: float mindb = AudioLevel::multiplier_to_dB(minvalue); lbajardsilogic@0: float maxdb = AudioLevel::multiplier_to_dB(maxvalue); lbajardsilogic@0: QString mindbstr; lbajardsilogic@0: QString maxdbstr; lbajardsilogic@0: if (mindb == AudioLevel::DB_FLOOR) { lbajardsilogic@0: mindbstr = tr("-Inf"); lbajardsilogic@0: } else { lbajardsilogic@0: mindbstr = QString("%1").arg(lrintf(mindb)); lbajardsilogic@0: } lbajardsilogic@0: if (maxdb == AudioLevel::DB_FLOOR) { lbajardsilogic@0: maxdbstr = tr("-Inf"); lbajardsilogic@0: } else { lbajardsilogic@0: maxdbstr = QString("%1").arg(lrintf(maxdb)); lbajardsilogic@0: } lbajardsilogic@0: if (lrintf(mindb) != lrintf(maxdb)) { lbajardsilogic@0: dbstr = tr("%1 - %2").arg(mindbstr).arg(maxdbstr); lbajardsilogic@0: } else { lbajardsilogic@0: dbstr = tr("%1").arg(mindbstr); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString description; lbajardsilogic@0: lbajardsilogic@0: if (range > int(m_sliceableModel->getResolution())) { lbajardsilogic@0: description = tr("%1\nBin:\t%2 (%3)\n%4 value:\t%5\ndB:\t%6") lbajardsilogic@0: .arg(genericDesc) lbajardsilogic@0: .arg(binstr) lbajardsilogic@0: .arg(hzstr) lbajardsilogic@0: .arg(m_samplingMode == NearestSample ? tr("First") : lbajardsilogic@0: m_samplingMode == SampleMean ? tr("Mean") : tr("Peak")) lbajardsilogic@0: .arg(valuestr) lbajardsilogic@0: .arg(dbstr); lbajardsilogic@0: } else { lbajardsilogic@0: description = tr("%1\nBin:\t%2 (%3)\nValue:\t%4\ndB:\t%5") lbajardsilogic@0: .arg(genericDesc) lbajardsilogic@0: .arg(binstr) lbajardsilogic@0: .arg(hzstr) lbajardsilogic@0: .arg(valuestr) lbajardsilogic@0: .arg(dbstr); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return description; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: SpectrumLayer::toXmlString(QString indent, QString extraAttributes) const lbajardsilogic@0: { lbajardsilogic@0: QString s; lbajardsilogic@0: lbajardsilogic@0: s += QString("windowSize=\"%1\" " lbajardsilogic@0: "windowHopLevel=\"%2\"") lbajardsilogic@0: .arg(m_windowSize) lbajardsilogic@0: .arg(m_windowHopLevel); lbajardsilogic@0: lbajardsilogic@0: return SliceLayer::toXmlString(indent, extraAttributes + " " + s); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: SpectrumLayer::setProperties(const QXmlAttributes &attributes) lbajardsilogic@0: { lbajardsilogic@0: SliceLayer::setProperties(attributes); lbajardsilogic@0: lbajardsilogic@0: bool ok = false; 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: } lbajardsilogic@0: lbajardsilogic@0: