New Spectrum Analyzer (#4950)
Replace old spectrum analyzer by new one with higher resolution and many new features. Resolves #2847.
@@ -40,6 +40,8 @@ public:
|
||||
EffectControlDialog( EffectControls * _controls );
|
||||
virtual ~EffectControlDialog();
|
||||
|
||||
virtual bool isResizable() const {return false;}
|
||||
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QMdiSubWindow>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* fft_helpers.h - some functions around FFT analysis
|
||||
*
|
||||
* Copyright (c) 2008-2012 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2019 Martin Pavelek <he29.HS/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -28,57 +29,90 @@
|
||||
|
||||
#include "lmms_export.h"
|
||||
|
||||
#include <vector>
|
||||
#include <fftw3.h>
|
||||
|
||||
const int FFT_BUFFER_SIZE = 2048;
|
||||
// NOTE: FFT_BUFFER_SIZE should be considered deprecated!
|
||||
// It is used by Eq plugin and some older code here, but this should be a user
|
||||
// switchable parameter, not a constant. Use a value from FFT_BLOCK_SIZES
|
||||
const unsigned int FFT_BUFFER_SIZE = 2048;
|
||||
|
||||
enum WINDOWS
|
||||
// Allowed FFT block sizes. Ranging from barely useful to barely acceptable
|
||||
// because of performance and latency reasons.
|
||||
const std::vector<unsigned int> FFT_BLOCK_SIZES = {256, 512, 1024, 2048, 4096, 8192, 16384};
|
||||
|
||||
// List of FFT window functions supported by precomputeWindow()
|
||||
enum FFT_WINDOWS
|
||||
{
|
||||
KAISER=1,
|
||||
RECTANGLE,
|
||||
HANNING,
|
||||
HAMMING
|
||||
RECTANGULAR = 0,
|
||||
BLACKMAN_HARRIS,
|
||||
HAMMING,
|
||||
HANNING
|
||||
};
|
||||
|
||||
/* returns biggest value from abs_spectrum[spec_size] array
|
||||
|
||||
/** Returns biggest value from abs_spectrum[spec_size] array.
|
||||
*
|
||||
* returns -1 on error
|
||||
* @return -1 on error, 0 on success
|
||||
*/
|
||||
float LMMS_EXPORT maximum( float * _abs_spectrum, unsigned int _spec_size );
|
||||
float LMMS_EXPORT maximum(const float *abs_spectrum, unsigned int spec_size);
|
||||
float LMMS_EXPORT maximum(const std::vector<float> &abs_spectrum);
|
||||
|
||||
/* apply hanning or hamming window to channel
|
||||
|
||||
/** Normalize the abs_spectrum array of absolute values to a 0..1 range
|
||||
* based on supplied energy and stores it in the norm_spectrum array.
|
||||
*
|
||||
* returns -1 on error
|
||||
* @return -1 on error
|
||||
*/
|
||||
int LMMS_EXPORT hanming( float * _timebuffer, int _length, WINDOWS _type );
|
||||
int LMMS_EXPORT normalize(const float *abs_spectrum, float *norm_spectrum, unsigned int bin_count, unsigned int block_size);
|
||||
int LMMS_EXPORT normalize(const std::vector<float> &abs_spectrum, std::vector<float> &norm_spectrum, unsigned int block_size);
|
||||
|
||||
/* compute absolute values of complex_buffer, save to absspec_buffer
|
||||
* take care that - compl_len is not bigger than complex_buffer!
|
||||
* - absspec buffer is big enough!
|
||||
|
||||
/** Check if the spectrum contains any non-zero value.
|
||||
*
|
||||
* returns 0 on success, else -1
|
||||
* @return 1 if spectrum contains any non-zero value
|
||||
* @return 0 otherwise
|
||||
*/
|
||||
int LMMS_EXPORT absspec( fftwf_complex * _complex_buffer, float * _absspec_buffer,
|
||||
int _compl_length );
|
||||
int LMMS_EXPORT notEmpty(const std::vector<float> &spectrum);
|
||||
|
||||
/* build fewer subbands from many absolute spectrum values
|
||||
* take care that - compressedbands[] array num_new elements long
|
||||
* - num_old > num_new
|
||||
|
||||
/** Precompute a window function for later real-time use.
|
||||
* Set normalized to false if you do not want to apply amplitude correction.
|
||||
*
|
||||
* returns 0 on success, else -1
|
||||
* @return -1 on error
|
||||
*/
|
||||
int LMMS_EXPORT compressbands( float * _absspec_buffer, float * _compressedband,
|
||||
int _num_old, int _num_new, int _bottom, int _top );
|
||||
int LMMS_EXPORT precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool normalized = true);
|
||||
|
||||
|
||||
int LMMS_EXPORT calc13octaveband31( float * _absspec_buffer, float * _subbands,
|
||||
int _num_spec, float _max_frequency );
|
||||
|
||||
/* compute power of finite time sequence
|
||||
* take care num_values is length of timesignal[]
|
||||
/** Compute absolute values of complex_buffer, save to absspec_buffer.
|
||||
* Take care that - compl_len is not bigger than complex_buffer!
|
||||
* - absspec buffer is big enough!
|
||||
*
|
||||
* returns power on success, else -1
|
||||
* @return 0 on success, else -1
|
||||
*/
|
||||
float LMMS_EXPORT signalpower(float *timesignal, int num_values);
|
||||
int LMMS_EXPORT absspec(const fftwf_complex *complex_buffer, float *absspec_buffer,
|
||||
unsigned int compl_length);
|
||||
|
||||
|
||||
/** Build fewer subbands from many absolute spectrum values.
|
||||
* Take care that - compressedbands[] array num_new elements long
|
||||
* - num_old > num_new
|
||||
*
|
||||
* @return 0 on success, else -1
|
||||
*/
|
||||
int LMMS_EXPORT compressbands(const float * _absspec_buffer, float * _compressedband,
|
||||
int _num_old, int _num_new, int _bottom, int _top);
|
||||
|
||||
|
||||
int LMMS_EXPORT calc13octaveband31(float * _absspec_buffer, float * _subbands,
|
||||
int _num_spec, float _max_frequency);
|
||||
|
||||
|
||||
/** Compute power of finite time sequence.
|
||||
* Take care num_values is length of timesignal[].
|
||||
*
|
||||
* @return power on success, else -1
|
||||
*/
|
||||
float LMMS_EXPORT signalpower(const float *timesignal, int num_values);
|
||||
|
||||
#endif
|
||||
|
||||
75
plugins/SpectrumAnalyzer/Analyzer.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Analyzer.cpp - definition of Analyzer class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* Based partially on Eq plugin code,
|
||||
* Copyright (c) 2014-2017, David French <dave/dot/french3/at/googlemail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Analyzer.h"
|
||||
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
Plugin::Descriptor PLUGIN_EXPORT analyzer_plugin_descriptor =
|
||||
{
|
||||
"spectrumanalyzer",
|
||||
"Spectrum Analyzer",
|
||||
QT_TRANSLATE_NOOP("pluginBrowser", "A graphical spectrum analyzer."),
|
||||
"Martin Pavelek <he29/dot/HS/at/gmail/dot/com>",
|
||||
0x0100,
|
||||
Plugin::Effect,
|
||||
new PluginPixmapLoader("logo"),
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Analyzer::Analyzer(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) :
|
||||
Effect(&analyzer_plugin_descriptor, parent, key),
|
||||
m_processor(&m_controls),
|
||||
m_controls(this)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Take audio data and pass them to the spectrum processor.
|
||||
// Skip processing if the controls dialog isn't visible, it would only waste CPU cycles.
|
||||
bool Analyzer::processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count)
|
||||
{
|
||||
if (!isEnabled() || !isRunning ()) {return false;}
|
||||
if (m_controls.isViewVisible()) {m_processor.analyse(buffer, frame_count);}
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
// needed for getting plugin out of shared lib
|
||||
PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *parent, void *data)
|
||||
{
|
||||
return new Analyzer(parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(data));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
* SpectrumAnalyzerControlDialog.h - view for spectrum analyzer
|
||||
/* Analyzer.h - declaration of Analyzer class.
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* Based partially on Eq plugin code,
|
||||
* Copyright (c) 2014-2017, David French <dave/dot/french3/at/googlemail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -22,32 +24,30 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SPECTRUM_ANALYZER_CONTROL_DIALOG_H
|
||||
#define _SPECTRUM_ANALYZER_CONTROL_DIALOG_H
|
||||
#ifndef ANALYZER_H
|
||||
#define ANALYZER_H
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
#include "Effect.h"
|
||||
#include "SaControls.h"
|
||||
#include "SaProcessor.h"
|
||||
|
||||
|
||||
class SpectrumAnalyzerControls;
|
||||
|
||||
|
||||
class SpectrumAnalyzerControlDialog : public EffectControlDialog
|
||||
//! Top level class; handles LMMS interface and feeds data to the data processor.
|
||||
class Analyzer : public Effect
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SpectrumAnalyzerControlDialog( SpectrumAnalyzerControls* controls );
|
||||
virtual ~SpectrumAnalyzerControlDialog()
|
||||
{
|
||||
}
|
||||
Analyzer(Model *parent, const Descriptor::SubPluginFeatures::Key *key);
|
||||
virtual ~Analyzer() {};
|
||||
|
||||
bool processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) override;
|
||||
EffectControls *controls() override {return &m_controls;}
|
||||
|
||||
SaProcessor *getProcessor() {return &m_processor;}
|
||||
|
||||
private:
|
||||
virtual void paintEvent( QPaintEvent* event );
|
||||
SaProcessor m_processor;
|
||||
SaControls m_controls;
|
||||
};
|
||||
|
||||
SpectrumAnalyzerControls* m_controls;
|
||||
#endif // ANALYZER_H
|
||||
|
||||
QPixmap m_logXAxis;
|
||||
QPixmap m_logYAxis;
|
||||
|
||||
} ;
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS})
|
||||
LINK_LIBRARIES(${FFTW3F_LIBRARIES})
|
||||
BUILD_PLUGIN(spectrumanalyzer SpectrumAnalyzer.cpp SpectrumAnalyzerControls.cpp SpectrumAnalyzerControlDialog.cpp SpectrumAnalyzer.h SpectrumAnalyzerControls.h SpectrumAnalyzerControlDialog.h MOCFILES SpectrumAnalyzerControlDialog.h SpectrumAnalyzerControls.h EMBEDDED_RESOURCES *.png)
|
||||
BUILD_PLUGIN(analyzer Analyzer.cpp SaProcessor.cpp SaControls.cpp SaControlsDialog.cpp SaSpectrumView.cpp SaWaterfallView.cpp
|
||||
MOCFILES SaProcessor.h SaControls.h SaControlsDialog.h SaSpectrumView.h SaWaterfallView.h EMBEDDED_RESOURCES *.svg logo.png)
|
||||
|
||||
19
plugins/SpectrumAnalyzer/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Spectrum Analyzer plugin
|
||||
|
||||
## Overview
|
||||
|
||||
This plugin consists of three widgets and back-end code to provide them with required data.
|
||||
|
||||
The top-level widget is SaControlDialog. It populates a configuration widget (created dynamically) and instantiates spectrum display widgets. Its main back-end class is SaControls, which holds all configuration values and globally valid constants (e.g. range definitions).
|
||||
|
||||
SaSpectrumDisplay and SaWaterfallDisplay show the result of spectrum analysis. Their main back-end class is SaProcessor, which performs FFT analysis on data received from the Analyzer class, which in turn handles the interface with LMMS.
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
1.0.1 2019-06-02
|
||||
- code style changes
|
||||
- added tool-tips
|
||||
- use const for unmodified arrays passed to fft_helpers
|
||||
1.0.0 2019-04-07
|
||||
- initial release
|
||||
144
plugins/SpectrumAnalyzer/SaControls.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* SaControls.cpp - definition of SaControls class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SaControls.h"
|
||||
|
||||
#include <QtXml/QDomElement>
|
||||
|
||||
#include "Analyzer.h"
|
||||
#include "SaControlsDialog.h"
|
||||
|
||||
|
||||
SaControls::SaControls(Analyzer *effect) :
|
||||
EffectControls(effect),
|
||||
m_effect(effect),
|
||||
|
||||
// initialize bool models and set default values
|
||||
m_pauseModel(false, this, tr("Pause")),
|
||||
m_refFreezeModel(false, this, tr("Reference freeze")),
|
||||
|
||||
m_waterfallModel(false, this, tr("Waterfall")),
|
||||
m_smoothModel(false, this, tr("Averaging")),
|
||||
m_stereoModel(false, this, tr("Stereo")),
|
||||
m_peakHoldModel(false, this, tr("Peak hold")),
|
||||
|
||||
m_logXModel(true, this, tr("Logarithmic frequency")),
|
||||
m_logYModel(true, this, tr("Logarithmic amplitude")),
|
||||
|
||||
// default values of combo boxes are set after they are populated
|
||||
m_freqRangeModel(this, tr("Frequency range")),
|
||||
m_ampRangeModel(this, tr("Amplitude range")),
|
||||
m_blockSizeModel(this, tr("FFT block size")),
|
||||
m_windowModel(this, tr("FFT window type"))
|
||||
{
|
||||
// Frequency and amplitude ranges; order must match
|
||||
// FREQUENCY_RANGES and AMPLITUDE_RANGES defined in SaControls.h
|
||||
m_freqRangeModel.addItem(tr("Full (auto)"));
|
||||
m_freqRangeModel.addItem(tr("Audible"));
|
||||
m_freqRangeModel.addItem(tr("Bass"));
|
||||
m_freqRangeModel.addItem(tr("Mids"));
|
||||
m_freqRangeModel.addItem(tr("High"));
|
||||
m_freqRangeModel.setValue(m_freqRangeModel.findText(tr("Full (auto)")));
|
||||
|
||||
m_ampRangeModel.addItem(tr("Extended"));
|
||||
m_ampRangeModel.addItem(tr("Default"));
|
||||
m_ampRangeModel.addItem(tr("Audible"));
|
||||
m_ampRangeModel.addItem(tr("Noise"));
|
||||
m_ampRangeModel.setValue(m_ampRangeModel.findText(tr("Default")));
|
||||
|
||||
// FFT block size labels are generated automatically, based on
|
||||
// FFT_BLOCK_SIZES vector defined in fft_helpers.h
|
||||
for (unsigned int i = 0; i < FFT_BLOCK_SIZES.size(); i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
m_blockSizeModel.addItem((std::to_string(FFT_BLOCK_SIZES[i]) + " ").c_str() + tr("(High time res.)"));
|
||||
}
|
||||
else if (i == FFT_BLOCK_SIZES.size() - 1)
|
||||
{
|
||||
m_blockSizeModel.addItem((std::to_string(FFT_BLOCK_SIZES[i]) + " ").c_str() + tr("(High freq. res.)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_blockSizeModel.addItem(std::to_string(FFT_BLOCK_SIZES[i]).c_str());
|
||||
}
|
||||
}
|
||||
m_blockSizeModel.setValue(m_blockSizeModel.findText("2048"));
|
||||
|
||||
// Window type order must match FFT_WINDOWS defined in fft_helpers.h
|
||||
m_windowModel.addItem(tr("Rectangular (Off)"));
|
||||
m_windowModel.addItem(tr("Blackman-Harris (Default)"));
|
||||
m_windowModel.addItem(tr("Hamming"));
|
||||
m_windowModel.addItem(tr("Hanning"));
|
||||
m_windowModel.setValue(m_windowModel.findText(tr("Blackman-Harris (Default)")));
|
||||
|
||||
// Colors
|
||||
// Background color is defined by Qt / theme.
|
||||
// Make sure the sum of colors for L and R channel stays lower or equal
|
||||
// to 255. Otherwise the Waterfall pixels may overflow back to 0 even when
|
||||
// the input signal isn't clipping (over 1.0).
|
||||
m_colorL = QColor(51, 148, 204, 135);
|
||||
m_colorR = QColor(204, 107, 51, 135);
|
||||
m_colorMono = QColor(51, 148, 204, 204);
|
||||
m_colorBG = QColor(7, 7, 7, 255); // ~20 % gray (after gamma correction)
|
||||
m_colorGrid = QColor(30, 34, 38, 255); // ~40 % gray (slightly cold / blue)
|
||||
m_colorLabels = QColor(192, 202, 212, 255); // ~90 % gray (slightly cold / blue)
|
||||
}
|
||||
|
||||
|
||||
// Create the SaControlDialog widget which handles display of GUI elements.
|
||||
EffectControlDialog* SaControls::createView()
|
||||
{
|
||||
return new SaControlsDialog(this, m_effect->getProcessor());
|
||||
}
|
||||
|
||||
|
||||
void SaControls::loadSettings(const QDomElement &_this)
|
||||
{
|
||||
m_waterfallModel.loadSettings(_this, "Waterfall");
|
||||
m_smoothModel.loadSettings(_this, "Smooth");
|
||||
m_stereoModel.loadSettings(_this, "Stereo");
|
||||
m_peakHoldModel.loadSettings(_this, "PeakHold");
|
||||
m_logXModel.loadSettings(_this, "LogX");
|
||||
m_logYModel.loadSettings(_this, "LogY");
|
||||
m_freqRangeModel.loadSettings(_this, "RangeX");
|
||||
m_ampRangeModel.loadSettings(_this, "RangeY");
|
||||
m_blockSizeModel.loadSettings(_this, "BlockSize");
|
||||
m_windowModel.loadSettings(_this, "WindowType");
|
||||
}
|
||||
|
||||
|
||||
void SaControls::saveSettings(QDomDocument &doc, QDomElement &parent)
|
||||
{
|
||||
m_waterfallModel.saveSettings(doc, parent, "Waterfall");
|
||||
m_smoothModel.saveSettings(doc, parent, "Smooth");
|
||||
m_stereoModel.saveSettings(doc, parent, "Stereo");
|
||||
m_peakHoldModel.saveSettings(doc, parent, "PeakHold");
|
||||
m_logXModel.saveSettings(doc, parent, "LogX");
|
||||
m_logYModel.saveSettings(doc, parent, "LogY");
|
||||
m_freqRangeModel.saveSettings(doc, parent, "RangeX");
|
||||
m_ampRangeModel.saveSettings(doc, parent, "RangeY");
|
||||
m_blockSizeModel.saveSettings(doc, parent, "BlockSize");
|
||||
m_windowModel.saveSettings(doc, parent, "WindowType");
|
||||
}
|
||||
126
plugins/SpectrumAnalyzer/SaControls.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* SaControls.h - declaration of SaControls class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SACONTROLS_H
|
||||
#define SACONTROLS_H
|
||||
|
||||
#include "ComboBoxModel.h"
|
||||
#include "EffectControls.h"
|
||||
|
||||
//#define SA_DEBUG 1 // define SA_DEBUG to enable performance measurements
|
||||
|
||||
// Frequency ranges (in Hz).
|
||||
// Full range is defined by LOWEST_LOG_FREQ and current sample rate.
|
||||
const int LOWEST_LOG_FREQ = 10; // arbitrary low limit for log. scale, >1
|
||||
|
||||
enum FREQUENCY_RANGES
|
||||
{
|
||||
FRANGE_FULL = 0,
|
||||
FRANGE_AUDIBLE,
|
||||
FRANGE_BASS,
|
||||
FRANGE_MIDS,
|
||||
FRANGE_HIGH
|
||||
};
|
||||
|
||||
const int FRANGE_AUDIBLE_START = 20;
|
||||
const int FRANGE_AUDIBLE_END = 20000;
|
||||
const int FRANGE_BASS_START = 20;
|
||||
const int FRANGE_BASS_END = 300;
|
||||
const int FRANGE_MIDS_START = 200;
|
||||
const int FRANGE_MIDS_END = 5000;
|
||||
const int FRANGE_HIGH_START = 4000;
|
||||
const int FRANGE_HIGH_END = 20000;
|
||||
|
||||
// Amplitude ranges.
|
||||
// Reference: sine wave from -1.0 to 1.0 = 0 dB.
|
||||
// I.e. if master volume is 100 %, positive values signify clipping.
|
||||
// Doubling or halving the amplitude produces 3 dB difference.
|
||||
enum AMPLITUDE_RANGES
|
||||
{
|
||||
ARANGE_EXTENDED = 0,
|
||||
ARANGE_DEFAULT,
|
||||
ARANGE_AUDIBLE,
|
||||
ARANGE_NOISE
|
||||
};
|
||||
|
||||
const int ARANGE_EXTENDED_START = -80;
|
||||
const int ARANGE_EXTENDED_END = 20;
|
||||
const int ARANGE_DEFAULT_START = -30;
|
||||
const int ARANGE_DEFAULT_END = 0;
|
||||
const int ARANGE_AUDIBLE_START = -50;
|
||||
const int ARANGE_AUDIBLE_END = 10;
|
||||
const int ARANGE_NOISE_START = -60;
|
||||
const int ARANGE_NOISE_END = -20;
|
||||
|
||||
|
||||
class Analyzer;
|
||||
|
||||
// Holds all the configuration values
|
||||
class SaControls : public EffectControls
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SaControls(Analyzer* effect);
|
||||
virtual ~SaControls() {}
|
||||
|
||||
EffectControlDialog* createView() override;
|
||||
|
||||
void saveSettings (QDomDocument& doc, QDomElement& parent) override;
|
||||
void loadSettings (const QDomElement &_this) override;
|
||||
|
||||
QString nodeName() const override {return "Analyzer";}
|
||||
int controlCount() override {return 12;}
|
||||
|
||||
private:
|
||||
Analyzer *m_effect;
|
||||
|
||||
BoolModel m_pauseModel;
|
||||
BoolModel m_refFreezeModel;
|
||||
|
||||
BoolModel m_waterfallModel;
|
||||
BoolModel m_smoothModel;
|
||||
BoolModel m_stereoModel;
|
||||
BoolModel m_peakHoldModel;
|
||||
|
||||
BoolModel m_logXModel;
|
||||
BoolModel m_logYModel;
|
||||
|
||||
ComboBoxModel m_freqRangeModel;
|
||||
ComboBoxModel m_ampRangeModel;
|
||||
ComboBoxModel m_blockSizeModel;
|
||||
ComboBoxModel m_windowModel;
|
||||
|
||||
QColor m_colorL;
|
||||
QColor m_colorR;
|
||||
QColor m_colorMono;
|
||||
QColor m_colorBG;
|
||||
QColor m_colorGrid;
|
||||
QColor m_colorLabels;
|
||||
|
||||
friend class SaControlsDialog;
|
||||
friend class SaSpectrumView;
|
||||
friend class SaWaterfallView;
|
||||
friend class SaProcessor;
|
||||
};
|
||||
#endif // SACONTROLS_H
|
||||
227
plugins/SpectrumAnalyzer/SaControlsDialog.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* SaControlsDialog.cpp - definition of SaControlsDialog class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SaControlsDialog.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QSizePolicy>
|
||||
#include <QSplitter>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ComboBox.h"
|
||||
#include "ComboBoxModel.h"
|
||||
#include "embed.h"
|
||||
#include "Engine.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "PixmapButton.h"
|
||||
#include "SaControls.h"
|
||||
#include "SaProcessor.h"
|
||||
|
||||
|
||||
// The entire GUI layout is built here.
|
||||
SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) :
|
||||
EffectControlDialog(controls),
|
||||
m_controls(controls),
|
||||
m_processor(processor)
|
||||
{
|
||||
// Top level placement of sections is handled by QSplitter widget.
|
||||
QHBoxLayout *master_layout = new QHBoxLayout;
|
||||
QSplitter *display_splitter = new QSplitter(Qt::Vertical);
|
||||
master_layout->addWidget(display_splitter);
|
||||
master_layout->setContentsMargins(2, 6, 2, 8);
|
||||
setLayout(master_layout);
|
||||
|
||||
// QSplitter top: configuration section
|
||||
QWidget *config_widget = new QWidget;
|
||||
QGridLayout *config_layout = new QGridLayout;
|
||||
config_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
config_widget->setMaximumHeight(m_configHeight);
|
||||
config_widget->setLayout(config_layout);
|
||||
display_splitter->addWidget(config_widget);
|
||||
|
||||
// Pre-compute target pixmap size based on monitor DPI.
|
||||
// Using setDevicePixelRatio() on pixmap allows the SVG image to be razor
|
||||
// sharp on High-DPI screens, but the desired size must be manually
|
||||
// enlarged. No idea how to make Qt do it in a more reasonable way.
|
||||
QSize iconSize = QSize(22.0 * devicePixelRatio(), 22.0 * devicePixelRatio());
|
||||
QSize buttonSize = 1.2 * iconSize;
|
||||
|
||||
// pause and freeze buttons
|
||||
PixmapButton *pauseButton = new PixmapButton(this, tr("Pause"));
|
||||
pauseButton->setToolTip(tr("Pause data acquisition"));
|
||||
QPixmap *pauseOnPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("play").scaled(buttonSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
QPixmap *pauseOffPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("pause").scaled(buttonSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
pauseOnPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
pauseOffPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
pauseButton->setActiveGraphic(*pauseOnPixmap);
|
||||
pauseButton->setInactiveGraphic(*pauseOffPixmap);
|
||||
pauseButton->setCheckable(true);
|
||||
pauseButton->setModel(&controls->m_pauseModel);
|
||||
config_layout->addWidget(pauseButton, 0, 0, 2, 1);
|
||||
|
||||
PixmapButton *refFreezeButton = new PixmapButton(this, tr("Reference freeze"));
|
||||
refFreezeButton->setToolTip(tr("Freeze current input as a reference / disable falloff in peak-hold mode."));
|
||||
QPixmap *freezeOnPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("freeze").scaled(buttonSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
QPixmap *freezeOffPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("freeze_off").scaled(buttonSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
freezeOnPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
freezeOffPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
refFreezeButton->setActiveGraphic(*freezeOnPixmap);
|
||||
refFreezeButton->setInactiveGraphic(*freezeOffPixmap);
|
||||
refFreezeButton->setCheckable(true);
|
||||
refFreezeButton->setModel(&controls->m_refFreezeModel);
|
||||
config_layout->addWidget(refFreezeButton, 2, 0, 2, 1);
|
||||
|
||||
// misc configuration switches
|
||||
LedCheckBox *waterfallButton = new LedCheckBox(tr("Waterfall"), this);
|
||||
waterfallButton->setToolTip(tr("Display real-time spectrogram"));
|
||||
waterfallButton->setCheckable(true);
|
||||
waterfallButton->setMinimumSize(70, 12);
|
||||
waterfallButton->setModel(&controls->m_waterfallModel);
|
||||
config_layout->addWidget(waterfallButton, 0, 1);
|
||||
|
||||
LedCheckBox *smoothButton = new LedCheckBox(tr("Averaging"), this);
|
||||
smoothButton->setToolTip(tr("Enable exponential moving average"));
|
||||
smoothButton->setCheckable(true);
|
||||
smoothButton->setMinimumSize(70, 12);
|
||||
smoothButton->setModel(&controls->m_smoothModel);
|
||||
config_layout->addWidget(smoothButton, 1, 1);
|
||||
|
||||
LedCheckBox *stereoButton = new LedCheckBox(tr("Stereo"), this);
|
||||
stereoButton->setToolTip(tr("Display stereo channels separately"));
|
||||
stereoButton->setCheckable(true);
|
||||
stereoButton->setMinimumSize(70, 12);
|
||||
stereoButton->setModel(&controls->m_stereoModel);
|
||||
config_layout->addWidget(stereoButton, 2, 1);
|
||||
|
||||
LedCheckBox *peakHoldButton = new LedCheckBox(tr("Peak hold"), this);
|
||||
peakHoldButton->setToolTip(tr("Display envelope of peak values"));
|
||||
peakHoldButton->setCheckable(true);
|
||||
peakHoldButton->setMinimumSize(70, 12);
|
||||
peakHoldButton->setModel(&controls->m_peakHoldModel);
|
||||
config_layout->addWidget(peakHoldButton, 3, 1);
|
||||
|
||||
// frequency: linear / log. switch and range selector
|
||||
PixmapButton *logXButton = new PixmapButton(this, tr("Logarithmic frequency"));
|
||||
logXButton->setToolTip(tr("Switch between logarithmic and linear frequency scale"));
|
||||
QPixmap *logXOnPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("x_log").scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
QPixmap *logXOffPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("x_linear").scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
logXOnPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
logXOffPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
logXButton->setActiveGraphic(*logXOnPixmap);
|
||||
logXButton->setInactiveGraphic(*logXOffPixmap);
|
||||
logXButton->setCheckable(true);
|
||||
logXButton->setModel(&controls->m_logXModel);
|
||||
config_layout->addWidget(logXButton, 0, 2, 2, 1, Qt::AlignRight);
|
||||
|
||||
ComboBox *freqRangeCombo = new ComboBox(this, tr("Frequency range"));
|
||||
freqRangeCombo->setToolTip(tr("Frequency range"));
|
||||
freqRangeCombo->setMinimumSize(100, 22);
|
||||
freqRangeCombo->setMaximumSize(200, 22);
|
||||
freqRangeCombo->setModel(&controls->m_freqRangeModel);
|
||||
config_layout->addWidget(freqRangeCombo, 0, 3, 2, 1);
|
||||
|
||||
// amplitude: linear / log switch and range selector
|
||||
PixmapButton *logYButton = new PixmapButton(this, tr("Logarithmic amplitude"));
|
||||
logYButton->setToolTip(tr("Switch between logarithmic and linear amplitude scale"));
|
||||
QPixmap *logYOnPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("y_log").scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
QPixmap *logYOffPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("y_linear").scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
logYOnPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
logYOffPixmap->setDevicePixelRatio(devicePixelRatio());
|
||||
logYButton->setActiveGraphic(*logYOnPixmap);
|
||||
logYButton->setInactiveGraphic(*logYOffPixmap);
|
||||
logYButton->setCheckable(true);
|
||||
logYButton->setModel(&controls->m_logYModel);
|
||||
config_layout->addWidget(logYButton, 2, 2, 2, 1, Qt::AlignRight);
|
||||
|
||||
ComboBox *ampRangeCombo = new ComboBox(this, tr("Amplitude range"));
|
||||
ampRangeCombo->setToolTip(tr("Amplitude range"));
|
||||
ampRangeCombo->setMinimumSize(100, 22);
|
||||
ampRangeCombo->setMaximumSize(200, 22);
|
||||
ampRangeCombo->setModel(&controls->m_ampRangeModel);
|
||||
config_layout->addWidget(ampRangeCombo, 2, 3, 2, 1);
|
||||
|
||||
// FFT: block size: icon and selector
|
||||
QLabel *blockSizeLabel = new QLabel("", this);
|
||||
QPixmap *blockSizeIcon = new QPixmap(PLUGIN_NAME::getIconPixmap("block_size"));
|
||||
blockSizeIcon->setDevicePixelRatio(devicePixelRatio());
|
||||
blockSizeLabel->setPixmap(blockSizeIcon->scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
config_layout->addWidget(blockSizeLabel, 0, 4, 2, 1, Qt::AlignRight);
|
||||
|
||||
ComboBox *blockSizeCombo = new ComboBox(this, tr("FFT block bize"));
|
||||
blockSizeCombo->setToolTip(tr("FFT block size"));
|
||||
blockSizeCombo->setMinimumSize(100, 22);
|
||||
blockSizeCombo->setMaximumSize(200, 22);
|
||||
blockSizeCombo->setModel(&controls->m_blockSizeModel);
|
||||
config_layout->addWidget(blockSizeCombo, 0, 5, 2, 1);
|
||||
processor->reallocateBuffers();
|
||||
connect(&controls->m_blockSizeModel, &ComboBoxModel::dataChanged, [=] {processor->reallocateBuffers();});
|
||||
|
||||
// FFT: window type: icon and selector
|
||||
QLabel *windowLabel = new QLabel("", this);
|
||||
QPixmap *windowIcon = new QPixmap(PLUGIN_NAME::getIconPixmap("window"));
|
||||
windowIcon->setDevicePixelRatio(devicePixelRatio());
|
||||
windowLabel->setPixmap(windowIcon->scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
config_layout->addWidget(windowLabel, 2, 4, 2, 1, Qt::AlignRight);
|
||||
|
||||
ComboBox *windowCombo = new ComboBox(this, tr("FFT window type"));
|
||||
windowCombo->setToolTip(tr("FFT window type"));
|
||||
windowCombo->setMinimumSize(100, 22);
|
||||
windowCombo->setMaximumSize(200, 22);
|
||||
windowCombo->setModel(&controls->m_windowModel);
|
||||
config_layout->addWidget(windowCombo, 2, 5, 2, 1);
|
||||
processor->rebuildWindow();
|
||||
connect(&controls->m_windowModel, &ComboBoxModel::dataChanged, [=] {processor->rebuildWindow();});
|
||||
|
||||
|
||||
// QSplitter middle and bottom: spectrum display widgets
|
||||
m_spectrum = new SaSpectrumView(controls, processor, this);
|
||||
display_splitter->addWidget(m_spectrum);
|
||||
|
||||
m_waterfall = new SaWaterfallView(controls, processor, this);
|
||||
display_splitter->addWidget(m_waterfall);
|
||||
m_waterfall->setVisible(m_controls->m_waterfallModel.value());
|
||||
connect(&controls->m_waterfallModel, &BoolModel::dataChanged, [=] {m_waterfall->updateVisibility();});
|
||||
}
|
||||
|
||||
|
||||
// Suggest the best current widget size.
|
||||
QSize SaControlsDialog::sizeHint() const
|
||||
{
|
||||
// Best width is determined by spectrum display sizeHint.
|
||||
// Best height depends on whether waterfall is visible and
|
||||
// consists of heights of the config section, spectrum, waterfall
|
||||
// and some reserve for margins.
|
||||
if (m_waterfall->isVisible())
|
||||
{
|
||||
return QSize(m_spectrum->sizeHint().width(),
|
||||
m_configHeight + m_spectrum->sizeHint().height() + m_waterfall->sizeHint().height() + 50);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QSize(m_spectrum->sizeHint().width(),
|
||||
m_configHeight + m_spectrum->sizeHint().height() + 50);
|
||||
}
|
||||
}
|
||||
|
||||
57
plugins/SpectrumAnalyzer/SaControlsDialog.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SaControlsDialog.h - declatation of SaControlsDialog class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SACONTROLSDIALOG_H
|
||||
#define SACONTROLSDIALOG_H
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
#include "SaControls.h"
|
||||
#include "SaSpectrumView.h"
|
||||
#include "SaProcessor.h"
|
||||
#include "SaWaterfallView.h"
|
||||
|
||||
|
||||
//! Top-level widget holding the configuration GUI and spectrum displays
|
||||
class SaControlsDialog : public EffectControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SaControlsDialog(SaControls *controls, SaProcessor *processor);
|
||||
virtual ~SaControlsDialog() {}
|
||||
|
||||
bool isResizable() const override {return true;}
|
||||
QSize sizeHint() const override;
|
||||
|
||||
private:
|
||||
SaControls *m_controls;
|
||||
SaProcessor *m_processor;
|
||||
|
||||
// Pointers to created widgets are needed to keep track of their sizeHint() changes.
|
||||
// Config widget is a plain QWidget so it has just a fixed height instead.
|
||||
const int m_configHeight = 75;
|
||||
SaSpectrumView *m_spectrum;
|
||||
SaWaterfallView *m_waterfall;
|
||||
};
|
||||
|
||||
#endif // SACONTROLSDIALOG_H
|
||||
571
plugins/SpectrumAnalyzer/SaProcessor.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
/* SaProcessor.cpp - implementation of SaProcessor class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* Based partially on Eq plugin code,
|
||||
* Copyright (c) 2014-2017, David French <dave/dot/french3/at/googlemail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SaProcessor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "lmms_math.h"
|
||||
|
||||
|
||||
SaProcessor::SaProcessor(SaControls *controls) :
|
||||
m_controls(controls),
|
||||
m_inBlockSize(FFT_BLOCK_SIZES[0]),
|
||||
m_fftBlockSize(FFT_BLOCK_SIZES[0]),
|
||||
m_sampleRate(Engine::mixer()->processingSampleRate()),
|
||||
m_framesFilledUp(0),
|
||||
m_spectrumActive(false),
|
||||
m_waterfallActive(false),
|
||||
m_waterfallNotEmpty(0),
|
||||
m_reallocating(false)
|
||||
{
|
||||
m_fftWindow.resize(m_inBlockSize, 1.0);
|
||||
precomputeWindow(m_fftWindow.data(), m_inBlockSize, BLACKMAN_HARRIS);
|
||||
|
||||
m_bufferL.resize(m_fftBlockSize, 0);
|
||||
m_bufferR.resize(m_fftBlockSize, 0);
|
||||
m_spectrumL = (fftwf_complex *) fftwf_malloc(binCount() * sizeof (fftwf_complex));
|
||||
m_spectrumR = (fftwf_complex *) fftwf_malloc(binCount() * sizeof (fftwf_complex));
|
||||
m_fftPlanL = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_bufferL.data(), m_spectrumL, FFTW_MEASURE);
|
||||
m_fftPlanR = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_bufferR.data(), m_spectrumR, FFTW_MEASURE);
|
||||
|
||||
m_absSpectrumL.resize(binCount(), 0);
|
||||
m_absSpectrumR.resize(binCount(), 0);
|
||||
m_normSpectrumL.resize(binCount(), 0);
|
||||
m_normSpectrumR.resize(binCount(), 0);
|
||||
|
||||
m_history.resize(binCount() * m_waterfallHeight * sizeof qRgb(0,0,0), 0);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
SaProcessor::~SaProcessor()
|
||||
{
|
||||
if (m_fftPlanL != NULL) {fftwf_destroy_plan(m_fftPlanL);}
|
||||
if (m_fftPlanR != NULL) {fftwf_destroy_plan(m_fftPlanR);}
|
||||
if (m_spectrumL != NULL) {fftwf_free(m_spectrumL);}
|
||||
if (m_spectrumR != NULL) {fftwf_free(m_spectrumR);}
|
||||
|
||||
m_fftPlanL = NULL;
|
||||
m_fftPlanR = NULL;
|
||||
m_spectrumL = NULL;
|
||||
m_spectrumR = NULL;
|
||||
}
|
||||
|
||||
|
||||
// Load a batch of data from LMMS; run FFT analysis if buffer is full enough.
|
||||
void SaProcessor::analyse(sampleFrame *in_buffer, const fpp_t frame_count)
|
||||
{
|
||||
#ifdef SA_DEBUG
|
||||
int start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
// only take in data if any view is visible and not paused
|
||||
if ((m_spectrumActive || m_waterfallActive) && !m_controls->m_pauseModel.value())
|
||||
{
|
||||
const bool stereo = m_controls->m_stereoModel.value();
|
||||
fpp_t in_frame = 0;
|
||||
while (in_frame < frame_count)
|
||||
{
|
||||
// fill sample buffers and check for zero input
|
||||
bool block_empty = true;
|
||||
for (; in_frame < frame_count && m_framesFilledUp < m_inBlockSize; in_frame++, m_framesFilledUp++)
|
||||
{
|
||||
if (stereo)
|
||||
{
|
||||
m_bufferL[m_framesFilledUp] = in_buffer[in_frame][0];
|
||||
m_bufferR[m_framesFilledUp] = in_buffer[in_frame][1];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bufferL[m_framesFilledUp] =
|
||||
m_bufferR[m_framesFilledUp] = (in_buffer[in_frame][0] + in_buffer[in_frame][1]) * 0.5f;
|
||||
}
|
||||
if (in_buffer[in_frame][0] != 0.f || in_buffer[in_frame][1] != 0.f)
|
||||
{
|
||||
block_empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Run analysis only if buffers contain enough data.
|
||||
// Also, to prevent audio interruption and a momentary GUI freeze,
|
||||
// skip analysis if buffers are being reallocated.
|
||||
if (m_framesFilledUp < m_inBlockSize || m_reallocating) {return;}
|
||||
|
||||
// update sample rate
|
||||
m_sampleRate = Engine::mixer()->processingSampleRate();
|
||||
|
||||
// apply FFT window
|
||||
for (unsigned int i = 0; i < m_inBlockSize; i++)
|
||||
{
|
||||
m_bufferL[i] = m_bufferL[i] * m_fftWindow[i];
|
||||
m_bufferR[i] = m_bufferR[i] * m_fftWindow[i];
|
||||
}
|
||||
|
||||
// lock data shared with SaSpectrumView and SaWaterfallView
|
||||
QMutexLocker lock(&m_dataAccess);
|
||||
|
||||
// Run FFT on left channel, convert the result to absolute magnitude
|
||||
// spectrum and normalize it.
|
||||
fftwf_execute(m_fftPlanL);
|
||||
absspec(m_spectrumL, m_absSpectrumL.data(), binCount());
|
||||
normalize(m_absSpectrumL, m_normSpectrumL, m_inBlockSize);
|
||||
|
||||
// repeat analysis for right channel if stereo processing is enabled
|
||||
if (stereo)
|
||||
{
|
||||
fftwf_execute(m_fftPlanR);
|
||||
absspec(m_spectrumR, m_absSpectrumR.data(), binCount());
|
||||
normalize(m_absSpectrumR, m_normSpectrumR, m_inBlockSize);
|
||||
}
|
||||
|
||||
// count empty lines so that empty history does not have to update
|
||||
if (block_empty && m_waterfallNotEmpty)
|
||||
{
|
||||
m_waterfallNotEmpty -= 1;
|
||||
}
|
||||
else if (!block_empty)
|
||||
{
|
||||
m_waterfallNotEmpty = m_waterfallHeight + 2;
|
||||
}
|
||||
|
||||
if (m_waterfallActive && m_waterfallNotEmpty)
|
||||
{
|
||||
// move waterfall history one line down and clear the top line
|
||||
QRgb *pixel = (QRgb *)m_history.data();
|
||||
std::copy(pixel,
|
||||
pixel + binCount() * m_waterfallHeight - binCount(),
|
||||
pixel + binCount());
|
||||
memset(pixel, 0, binCount() * sizeof (QRgb));
|
||||
|
||||
// add newest result on top
|
||||
int target; // pixel being constructed
|
||||
float accL = 0; // accumulators for merging multiple bins
|
||||
float accR = 0;
|
||||
|
||||
for (unsigned int i = 0; i < binCount(); i++)
|
||||
{
|
||||
// Every frequency bin spans a frequency range that must be
|
||||
// partially or fully mapped to a pixel. Any inconsistency
|
||||
// may be seen in the spectrogram as dark or white lines --
|
||||
// play white noise to confirm your change did not break it.
|
||||
float band_start = freqToXPixel(binToFreq(i) - binBandwidth() / 2.0, binCount());
|
||||
float band_end = freqToXPixel(binToFreq(i + 1) - binBandwidth() / 2.0, binCount());
|
||||
if (m_controls->m_logXModel.value())
|
||||
{
|
||||
// Logarithmic scale
|
||||
if (band_end - band_start > 1.0)
|
||||
{
|
||||
// band spans multiple pixels: draw all pixels it covers
|
||||
for (target = (int)band_start; target < (int)band_end; target++)
|
||||
{
|
||||
if (target >= 0 && target < binCount())
|
||||
{
|
||||
pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]);
|
||||
}
|
||||
}
|
||||
// save remaining portion of the band for the following band / pixel
|
||||
// (in case the next band uses sub-pixel drawing)
|
||||
accL = (band_end - (int)band_end) * m_normSpectrumL[i];
|
||||
accR = (band_end - (int)band_end) * m_normSpectrumR[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// sub-pixel drawing; add contribution of current band
|
||||
target = (int)band_start;
|
||||
if ((int)band_start == (int)band_end)
|
||||
{
|
||||
// band ends within current target pixel, accumulate
|
||||
accL += (band_end - band_start) * m_normSpectrumL[i];
|
||||
accR += (band_end - band_start) * m_normSpectrumR[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Band ends in the next pixel -- finalize the current pixel.
|
||||
// Make sure contribution is split correctly on pixel boundary.
|
||||
accL += ((int)band_end - band_start) * m_normSpectrumL[i];
|
||||
accR += ((int)band_end - band_start) * m_normSpectrumR[i];
|
||||
|
||||
if (target >= 0 && target < binCount()) {pixel[target] = makePixel(accL, accR);}
|
||||
|
||||
// save remaining portion of the band for the following band / pixel
|
||||
accL = (band_end - (int)band_end) * m_normSpectrumL[i];
|
||||
accR = (band_end - (int)band_end) * m_normSpectrumR[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Linear: always draws one or more pixels per band
|
||||
for (target = (int)band_start; target < band_end; target++)
|
||||
{
|
||||
if (target >= 0 && target < binCount())
|
||||
{
|
||||
pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef SA_DEBUG
|
||||
// report FFT processing speed
|
||||
start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - start_time;
|
||||
std::cout << "Processed " << m_framesFilledUp << " samples in " << start_time / 1000000.0 << " ms" << std::endl;
|
||||
#endif
|
||||
|
||||
// clean up before checking for more data from input buffer
|
||||
m_framesFilledUp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Produce a spectrogram pixel from normalized spectrum data.
|
||||
// Values over 1.0 will cause the color components to overflow: this is left
|
||||
// intentionally untreated as it clearly indicates which frequency is clipping.
|
||||
// Gamma correction is applied to make small values more visible and to make
|
||||
// a linear gradient actually appear roughly linear. The correction should be
|
||||
// around 0.42 to 0.45 for sRGB displays (or lower for bigger visibility boost).
|
||||
QRgb SaProcessor::makePixel(float left, float right, float gamma_correction) const
|
||||
{
|
||||
if (m_controls->m_stereoModel.value())
|
||||
{
|
||||
float ampL = pow(left, gamma_correction);
|
||||
float ampR = pow(right, gamma_correction);
|
||||
return qRgb(m_controls->m_colorL.red() * ampL + m_controls->m_colorR.red() * ampR,
|
||||
m_controls->m_colorL.green() * ampL + m_controls->m_colorR.green() * ampR,
|
||||
m_controls->m_colorL.blue() * ampL + m_controls->m_colorR.blue() * ampR);
|
||||
}
|
||||
else
|
||||
{
|
||||
float ampL = pow(left, gamma_correction);
|
||||
// make mono color brighter to compensate for the fact it is not summed
|
||||
return qRgb(m_controls->m_colorMono.lighter().red() * ampL,
|
||||
m_controls->m_colorMono.lighter().green() * ampL,
|
||||
m_controls->m_colorMono.lighter().blue() * ampL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Inform the processor whether any display widgets actually need it.
|
||||
void SaProcessor::setSpectrumActive(bool active)
|
||||
{
|
||||
m_spectrumActive = active;
|
||||
}
|
||||
|
||||
void SaProcessor::setWaterfallActive(bool active)
|
||||
{
|
||||
m_waterfallActive = active;
|
||||
}
|
||||
|
||||
|
||||
// Reallocate data buffers according to newly set block size.
|
||||
void SaProcessor::reallocateBuffers()
|
||||
{
|
||||
unsigned int new_size_index = m_controls->m_blockSizeModel.value();
|
||||
unsigned int new_in_size, new_fft_size;
|
||||
unsigned int new_bins;
|
||||
|
||||
// get new block sizes and bin count based on selected index
|
||||
if (new_size_index < FFT_BLOCK_SIZES.size())
|
||||
{
|
||||
new_in_size = FFT_BLOCK_SIZES[new_size_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_in_size = FFT_BLOCK_SIZES.back();
|
||||
}
|
||||
if (new_size_index + m_zeroPadFactor < FFT_BLOCK_SIZES.size())
|
||||
{
|
||||
new_fft_size = FFT_BLOCK_SIZES[new_size_index + m_zeroPadFactor];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_fft_size = FFT_BLOCK_SIZES.back();
|
||||
}
|
||||
|
||||
new_bins = new_fft_size / 2 +1;
|
||||
|
||||
// Lock data shared with SaSpectrumView and SaWaterfallView.
|
||||
// The m_reallocating is here to tell analyse() to avoid asking for the
|
||||
// lock, since fftw3 can take a while to find the fastest FFT algorithm
|
||||
// for given machine, which would produce interruption in the audio stream.
|
||||
m_reallocating = true;
|
||||
QMutexLocker lock(&m_dataAccess);
|
||||
|
||||
// destroy old FFT plan and free the result buffer
|
||||
if (m_fftPlanL != NULL) {fftwf_destroy_plan(m_fftPlanL);}
|
||||
if (m_fftPlanR != NULL) {fftwf_destroy_plan(m_fftPlanR);}
|
||||
if (m_spectrumL != NULL) {fftwf_free(m_spectrumL);}
|
||||
if (m_spectrumR != NULL) {fftwf_free(m_spectrumR);}
|
||||
|
||||
// allocate new space, create new plan and resize containers
|
||||
m_fftWindow.resize(new_in_size, 1.0);
|
||||
precomputeWindow(m_fftWindow.data(), new_in_size, (FFT_WINDOWS) m_controls->m_windowModel.value());
|
||||
m_bufferL.resize(new_fft_size, 0);
|
||||
m_bufferR.resize(new_fft_size, 0);
|
||||
m_spectrumL = (fftwf_complex *) fftwf_malloc(new_bins * sizeof (fftwf_complex));
|
||||
m_spectrumR = (fftwf_complex *) fftwf_malloc(new_bins * sizeof (fftwf_complex));
|
||||
m_fftPlanL = fftwf_plan_dft_r2c_1d(new_fft_size, m_bufferL.data(), m_spectrumL, FFTW_MEASURE);
|
||||
m_fftPlanR = fftwf_plan_dft_r2c_1d(new_fft_size, m_bufferR.data(), m_spectrumR, FFTW_MEASURE);
|
||||
|
||||
if (m_fftPlanL == NULL || m_fftPlanR == NULL)
|
||||
{
|
||||
std::cerr << "Failed to create new FFT plan!" << std::endl;
|
||||
}
|
||||
m_absSpectrumL.resize(new_bins, 0);
|
||||
m_absSpectrumR.resize(new_bins, 0);
|
||||
m_normSpectrumL.resize(new_bins, 0);
|
||||
m_normSpectrumR.resize(new_bins, 0);
|
||||
|
||||
m_history.resize(new_bins * m_waterfallHeight * sizeof qRgb(0,0,0), 0);
|
||||
|
||||
// done; publish new sizes and clean up
|
||||
m_inBlockSize = new_in_size;
|
||||
m_fftBlockSize = new_fft_size;
|
||||
|
||||
lock.unlock();
|
||||
m_reallocating = false;
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
// Precompute a new FFT window based on currently selected type.
|
||||
void SaProcessor::rebuildWindow()
|
||||
{
|
||||
// computation is done in fft_helpers
|
||||
QMutexLocker lock(&m_dataAccess);
|
||||
precomputeWindow(m_fftWindow.data(), m_inBlockSize, (FFT_WINDOWS) m_controls->m_windowModel.value());
|
||||
}
|
||||
|
||||
|
||||
// Clear all data buffers and replace contents with zeros.
|
||||
// Note: may take a few milliseconds, do not call in a loop!
|
||||
void SaProcessor::clear()
|
||||
{
|
||||
QMutexLocker lock(&m_dataAccess);
|
||||
m_framesFilledUp = 0;
|
||||
std::fill(m_bufferL.begin(), m_bufferL.end(), 0);
|
||||
std::fill(m_bufferR.begin(), m_bufferR.end(), 0);
|
||||
std::fill(m_absSpectrumL.begin(), m_absSpectrumL.end(), 0);
|
||||
std::fill(m_absSpectrumR.begin(), m_absSpectrumR.end(), 0);
|
||||
std::fill(m_normSpectrumL.begin(), m_normSpectrumL.end(), 0);
|
||||
std::fill(m_normSpectrumR.begin(), m_normSpectrumR.end(), 0);
|
||||
std::fill(m_history.begin(), m_history.end(), 0);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------
|
||||
// Frequency conversion helpers
|
||||
//
|
||||
|
||||
// Get sample rate value that is valid for currently stored results.
|
||||
unsigned int SaProcessor::getSampleRate() const
|
||||
{
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
|
||||
// Maximum frequency of a sampled signal is equal to half of its sample rate.
|
||||
float SaProcessor::getNyquistFreq() const
|
||||
{
|
||||
return getSampleRate() / 2.0f;
|
||||
}
|
||||
|
||||
|
||||
// FFTW automatically discards upper half of the symmetric FFT output, so
|
||||
// the useful bin count is the transform size divided by 2, plus zero.
|
||||
unsigned int SaProcessor::binCount() const
|
||||
{
|
||||
return m_fftBlockSize / 2 + 1;
|
||||
}
|
||||
|
||||
|
||||
// Return the center frequency of given frequency bin.
|
||||
float SaProcessor::binToFreq(unsigned int bin_index) const
|
||||
{
|
||||
return getNyquistFreq() * bin_index / binCount();
|
||||
}
|
||||
|
||||
|
||||
// Return width of the frequency range that falls into one bin.
|
||||
// The binCount is lowered by one since half of the first and last bin is
|
||||
// actually outside the frequency range.
|
||||
float SaProcessor::binBandwidth() const
|
||||
{
|
||||
return getNyquistFreq() / (binCount() - 1);
|
||||
}
|
||||
|
||||
|
||||
float SaProcessor::getFreqRangeMin(bool linear) const
|
||||
{
|
||||
switch (m_controls->m_freqRangeModel.value())
|
||||
{
|
||||
case FRANGE_AUDIBLE: return FRANGE_AUDIBLE_START;
|
||||
case FRANGE_BASS: return FRANGE_BASS_START;
|
||||
case FRANGE_MIDS: return FRANGE_MIDS_START;
|
||||
case FRANGE_HIGH: return FRANGE_HIGH_START;
|
||||
default:
|
||||
case FRANGE_FULL: return linear ? 0 : LOWEST_LOG_FREQ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float SaProcessor::getFreqRangeMax() const
|
||||
{
|
||||
switch (m_controls->m_freqRangeModel.value())
|
||||
{
|
||||
case FRANGE_AUDIBLE: return FRANGE_AUDIBLE_END;
|
||||
case FRANGE_BASS: return FRANGE_BASS_END;
|
||||
case FRANGE_MIDS: return FRANGE_MIDS_END;
|
||||
case FRANGE_HIGH: return FRANGE_HIGH_END;
|
||||
default:
|
||||
case FRANGE_FULL: return getNyquistFreq();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map frequency to pixel x position on a display of given width.
|
||||
float SaProcessor::freqToXPixel(float freq, unsigned int width) const
|
||||
{
|
||||
if (m_controls->m_logXModel.value())
|
||||
{
|
||||
if (freq <= 1) {return 0;}
|
||||
float min = log10(getFreqRangeMin());
|
||||
float range = log10(getFreqRangeMax()) - min;
|
||||
return (log10(freq) - min) / range * width;
|
||||
}
|
||||
else
|
||||
{
|
||||
float min = getFreqRangeMin();
|
||||
float range = getFreqRangeMax() - min;
|
||||
return (freq - min) / range * width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map pixel x position on display of given width back to frequency.
|
||||
float SaProcessor::xPixelToFreq(float x, unsigned int width) const
|
||||
{
|
||||
if (m_controls->m_logXModel.value())
|
||||
{
|
||||
float min = log10(getFreqRangeMin());
|
||||
float max = log10(getFreqRangeMax());
|
||||
float range = max - min;
|
||||
return pow(10, min + x / width * range);
|
||||
}
|
||||
else
|
||||
{
|
||||
float min = getFreqRangeMin();
|
||||
float range = getFreqRangeMax() - min;
|
||||
return min + x / width * range;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------
|
||||
// Amplitude conversion helpers
|
||||
//
|
||||
float SaProcessor::getAmpRangeMin(bool linear) const
|
||||
{
|
||||
// return very low limit to make sure zero gets included at linear grid
|
||||
if (linear) {return -900;}
|
||||
switch (m_controls->m_ampRangeModel.value())
|
||||
{
|
||||
case ARANGE_EXTENDED: return ARANGE_EXTENDED_START;
|
||||
case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_START;
|
||||
case ARANGE_NOISE: return ARANGE_NOISE_START;
|
||||
default:
|
||||
case ARANGE_DEFAULT: return ARANGE_DEFAULT_START;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float SaProcessor::getAmpRangeMax() const
|
||||
{
|
||||
switch (m_controls->m_ampRangeModel.value())
|
||||
{
|
||||
case ARANGE_EXTENDED: return ARANGE_EXTENDED_END;
|
||||
case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_END;
|
||||
case ARANGE_NOISE: return ARANGE_NOISE_END;
|
||||
default:
|
||||
case ARANGE_DEFAULT: return ARANGE_DEFAULT_END;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map linear amplitude to pixel y position on a display of given height.
|
||||
// Note that display coordinates are flipped: amplitude grows from [height] to zero.
|
||||
float SaProcessor::ampToYPixel(float amplitude, unsigned int height) const
|
||||
{
|
||||
if (m_controls->m_logYModel.value())
|
||||
{
|
||||
// logarithmic scale: convert linear amplitude to dB (relative to 1.0)
|
||||
float amplitude_dB = 10 * log10(amplitude);
|
||||
if (amplitude_dB < getAmpRangeMin())
|
||||
{
|
||||
return height;
|
||||
}
|
||||
else
|
||||
{
|
||||
float max = getAmpRangeMax();
|
||||
float range = getAmpRangeMin() - max;
|
||||
return (amplitude_dB - max) / range * height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// linear scale: convert returned ranges from dB to linear scale
|
||||
float max = pow(10, getAmpRangeMax() / 10);
|
||||
float range = pow(10, getAmpRangeMin() / 10) - max;
|
||||
return (amplitude - max) / range * height;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map pixel y position on display of given height back to amplitude.
|
||||
// Note that display coordinates are flipped: amplitude grows from [height] to zero.
|
||||
// Also note that in logarithmic Y mode the returned amplitude is in dB, not linear.
|
||||
float SaProcessor::yPixelToAmp(float y, unsigned int height) const
|
||||
{
|
||||
if (m_controls->m_logYModel.value())
|
||||
{
|
||||
float max = getAmpRangeMax();
|
||||
float range = getAmpRangeMin() - max;
|
||||
return max + range * (y / height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// linear scale: convert returned ranges from dB to linear scale
|
||||
float max = pow(10, getAmpRangeMax() / 10);
|
||||
float range = pow(10, getAmpRangeMin() / 10) - max;
|
||||
return max + range * (y / height);
|
||||
}
|
||||
}
|
||||
|
||||
122
plugins/SpectrumAnalyzer/SaProcessor.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* SaProcessor.h - declaration of SaProcessor class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* Based partially on Eq plugin code,
|
||||
* Copyright (c) 2014 David French <dave/dot/french3/at/googlemail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SAPROCESSOR_H
|
||||
#define SAPROCESSOR_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QMutex>
|
||||
#include <vector>
|
||||
|
||||
#include "fft_helpers.h"
|
||||
#include "SaControls.h"
|
||||
|
||||
|
||||
//! Receives audio data, runs FFT analysis and stores the result.
|
||||
class SaProcessor
|
||||
{
|
||||
public:
|
||||
explicit SaProcessor(SaControls *controls);
|
||||
virtual ~SaProcessor();
|
||||
|
||||
void analyse(sampleFrame *in_buffer, const fpp_t frame_count);
|
||||
|
||||
// inform processor if any processing is actually required
|
||||
void setSpectrumActive(bool active);
|
||||
void setWaterfallActive(bool active);
|
||||
|
||||
// configuration is taken from models in SaControls; some changes require
|
||||
// an exlicit update request (reallocation and window rebuild)
|
||||
void reallocateBuffers();
|
||||
void rebuildWindow();
|
||||
void clear();
|
||||
|
||||
// information about results and unit conversion helpers
|
||||
float binToFreq(unsigned int bin_index) const;
|
||||
float binBandwidth() const;
|
||||
|
||||
float freqToXPixel(float frequency, unsigned int width) const;
|
||||
float xPixelToFreq(float x, unsigned int width) const;
|
||||
|
||||
float ampToYPixel(float amplitude, unsigned int height) const;
|
||||
float yPixelToAmp(float y, unsigned int height) const;
|
||||
|
||||
unsigned int getSampleRate() const;
|
||||
float getNyquistFreq() const;
|
||||
|
||||
float getFreqRangeMin(bool linear = false) const;
|
||||
float getFreqRangeMax() const;
|
||||
float getAmpRangeMin(bool linear = false) const;
|
||||
float getAmpRangeMax() const;
|
||||
|
||||
// data access lock must be acquired by any friendly class that touches
|
||||
// the results, mainly to prevent unexpected mid-way reallocation
|
||||
QMutex m_dataAccess;
|
||||
|
||||
private:
|
||||
SaControls *m_controls;
|
||||
|
||||
// currently valid configuration
|
||||
const unsigned int m_zeroPadFactor = 2; //!< use n-steps bigger FFT for given block size
|
||||
unsigned int m_inBlockSize; //!< size of input (time domain) data block
|
||||
unsigned int m_fftBlockSize; //!< size of padded block for FFT processing
|
||||
unsigned int m_sampleRate;
|
||||
|
||||
unsigned int binCount() const; //!< size of output (frequency domain) data block
|
||||
|
||||
// data buffers (roughly in the order of processing, from input to output)
|
||||
unsigned int m_framesFilledUp;
|
||||
std::vector<float> m_bufferL; //!< time domain samples (left)
|
||||
std::vector<float> m_bufferR; //!< time domain samples (right)
|
||||
std::vector<float> m_fftWindow; //!< precomputed window function coefficients
|
||||
fftwf_plan m_fftPlanL;
|
||||
fftwf_plan m_fftPlanR;
|
||||
fftwf_complex *m_spectrumL; //!< frequency domain samples (complex) (left)
|
||||
fftwf_complex *m_spectrumR; //!< frequency domain samples (complex) (right)
|
||||
std::vector<float> m_absSpectrumL; //!< frequency domain samples (absolute) (left)
|
||||
std::vector<float> m_absSpectrumR; //!< frequency domain samples (absolute) (right)
|
||||
std::vector<float> m_normSpectrumL; //!< frequency domain samples (normalized) (left)
|
||||
std::vector<float> m_normSpectrumR; //!< frequency domain samples (normalized) (right)
|
||||
|
||||
// spectrum history for waterfall: new normSpectrum lines are added on top
|
||||
std::vector<uchar> m_history;
|
||||
const unsigned int m_waterfallHeight = 200; // Number of stored lines.
|
||||
// Note: high values may make it harder to see transients.
|
||||
|
||||
// book keeping
|
||||
bool m_spectrumActive;
|
||||
bool m_waterfallActive;
|
||||
unsigned int m_waterfallNotEmpty;
|
||||
bool m_reallocating;
|
||||
|
||||
// merge L and R channels and apply gamma correction to make a spectrogram pixel
|
||||
QRgb makePixel(float left, float right, float gamma_correction = 0.30) const;
|
||||
|
||||
friend class SaSpectrumView;
|
||||
friend class SaWaterfallView;
|
||||
};
|
||||
#endif // SAPROCESSOR_H
|
||||
|
||||
796
plugins/SpectrumAnalyzer/SaSpectrumView.cpp
Normal file
@@ -0,0 +1,796 @@
|
||||
/* SaSpectrumView.cpp - implementation of SaSpectrumView class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* Based partially on Eq plugin code,
|
||||
* Copyright (c) 2014-2017, David French <dave/dot/french3/at/googlemail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SaSpectrumView.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <QMouseEvent>
|
||||
#include <QMutexLocker>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
#include "SaProcessor.h"
|
||||
|
||||
#ifdef SA_DEBUG
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
|
||||
SaSpectrumView::SaSpectrumView(SaControls *controls, SaProcessor *processor, QWidget *_parent) :
|
||||
QWidget(_parent),
|
||||
m_controls(controls),
|
||||
m_processor(processor),
|
||||
m_freezeRequest(false),
|
||||
m_frozen(false)
|
||||
{
|
||||
setMinimumSize(360, 170);
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
connect(gui->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(periodicUpdate()));
|
||||
|
||||
m_displayBufferL.resize(m_processor->binCount(), 0);
|
||||
m_displayBufferR.resize(m_processor->binCount(), 0);
|
||||
m_peakBufferL.resize(m_processor->binCount(), 0);
|
||||
m_peakBufferR.resize(m_processor->binCount(), 0);
|
||||
|
||||
m_freqRangeIndex = m_controls->m_freqRangeModel.value();
|
||||
m_ampRangeIndex = m_controls->m_ampRangeModel.value();
|
||||
|
||||
m_logFreqTics = makeLogFreqTics(m_processor->getFreqRangeMin(), m_processor->getFreqRangeMax());
|
||||
m_linearFreqTics = makeLinearFreqTics(m_processor->getFreqRangeMin(), m_processor->getFreqRangeMax());
|
||||
m_logAmpTics = makeLogAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax());
|
||||
m_linearAmpTics = makeLinearAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax());
|
||||
|
||||
m_cursor = QPoint(0, 0);
|
||||
}
|
||||
|
||||
|
||||
// Compose and draw all the content; periodically called by Qt.
|
||||
// NOTE: Performance sensitive! If the drawing takes too long, it will drag
|
||||
// the FPS down for the entire program! Use SA_DEBUG to display timings.
|
||||
void SaSpectrumView::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
#ifdef SA_DEBUG
|
||||
int total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
|
||||
// 0) Constants and init
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// drawing and path-making are split into multiple methods for clarity;
|
||||
// display boundaries are updated here and shared as member variables
|
||||
m_displayTop = 1;
|
||||
m_displayBottom = height() -20;
|
||||
m_displayLeft = 26;
|
||||
m_displayRight = width() -26;
|
||||
m_displayWidth = m_displayRight - m_displayLeft;
|
||||
|
||||
// recompute range labels if needed
|
||||
if (m_freqRangeIndex != m_controls->m_freqRangeModel.value())
|
||||
{
|
||||
m_logFreqTics = makeLogFreqTics(m_processor->getFreqRangeMin(), m_processor->getFreqRangeMax());
|
||||
m_linearFreqTics = makeLinearFreqTics(m_processor->getFreqRangeMin(true), m_processor->getFreqRangeMax());
|
||||
m_freqRangeIndex = m_controls->m_freqRangeModel.value();
|
||||
}
|
||||
if (m_ampRangeIndex != m_controls->m_ampRangeModel.value())
|
||||
{
|
||||
m_logAmpTics = makeLogAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax());
|
||||
m_linearAmpTics = makeLinearAmpTics(m_processor->getAmpRangeMin(true), m_processor->getAmpRangeMax());
|
||||
m_ampRangeIndex = m_controls->m_ampRangeModel.value();
|
||||
}
|
||||
|
||||
// generate freeze request or clear "frozen" status based on freeze button
|
||||
if (!m_frozen && m_controls->m_refFreezeModel.value())
|
||||
{
|
||||
m_freezeRequest = true;
|
||||
}
|
||||
else if (!m_controls->m_refFreezeModel.value())
|
||||
{
|
||||
m_frozen = false;
|
||||
}
|
||||
|
||||
// 1) Background, grid and labels
|
||||
drawGrid(painter);
|
||||
|
||||
// 2) Spectrum display
|
||||
drawSpectrum(painter);
|
||||
|
||||
// 3) Overlays
|
||||
// draw cursor (if it is within bounds)
|
||||
drawCursor(painter);
|
||||
|
||||
// always draw the display outline
|
||||
painter.setPen(QPen(m_controls->m_colorGrid, 2, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawRoundedRect(m_displayLeft, 1,
|
||||
m_displayWidth, m_displayBottom,
|
||||
2.0, 2.0);
|
||||
|
||||
#ifdef SA_DEBUG
|
||||
// display what FPS would be achieved if spectrum display ran in a loop
|
||||
total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - total_time;
|
||||
painter.setPen(QPen(m_controls->m_colorLabels, 1,
|
||||
Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawText(m_displayRight -100, 70, 100, 16, Qt::AlignLeft,
|
||||
QString(std::string("Max FPS: " + std::to_string(1000000000.0 / total_time)).c_str()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Refresh data and draw the spectrum.
|
||||
void SaSpectrumView::drawSpectrum(QPainter &painter)
|
||||
{
|
||||
#ifdef SA_DEBUG
|
||||
int path_time = 0, draw_time = 0;
|
||||
#endif
|
||||
|
||||
// draw the graph only if there is any input, averaging residue or peaks
|
||||
QMutexLocker lock(&m_processor->m_dataAccess);
|
||||
if (m_decaySum > 0 || notEmpty(m_processor->m_normSpectrumL) || notEmpty(m_processor->m_normSpectrumR))
|
||||
{
|
||||
lock.unlock();
|
||||
#ifdef SA_DEBUG
|
||||
path_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
// update data buffers and reconstruct paths
|
||||
refreshPaths();
|
||||
#ifdef SA_DEBUG
|
||||
path_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - path_time;
|
||||
#endif
|
||||
|
||||
// draw stored paths
|
||||
#ifdef SA_DEBUG
|
||||
draw_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
// in case stereo is disabled, mono data are stored in left channel structures
|
||||
if (m_controls->m_stereoModel.value())
|
||||
{
|
||||
painter.fillPath(m_pathR, QBrush(m_controls->m_colorR));
|
||||
painter.fillPath(m_pathL, QBrush(m_controls->m_colorL));
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.fillPath(m_pathL, QBrush(m_controls->m_colorMono));
|
||||
}
|
||||
// draw the peakBuffer only if peak hold or reference freeze is active
|
||||
if (m_controls->m_peakHoldModel.value() || m_controls->m_refFreezeModel.value())
|
||||
{
|
||||
if (m_controls->m_stereoModel.value())
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorR, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawPath(m_pathPeakR);
|
||||
painter.setPen(QPen(m_controls->m_colorL, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawPath(m_pathPeakL);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorL, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawPath(m_pathPeakL);
|
||||
}
|
||||
}
|
||||
#ifdef SA_DEBUG
|
||||
draw_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - draw_time;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
#ifdef SA_DEBUG
|
||||
// display measurement results
|
||||
painter.drawText(m_displayRight -100, 90, 100, 16, Qt::AlignLeft,
|
||||
QString(std::string("Path ms: " + std::to_string(path_time / 1000000.0)).c_str()));
|
||||
painter.drawText(m_displayRight -100, 110, 100, 16, Qt::AlignLeft,
|
||||
QString(std::string("Draw ms: " + std::to_string(draw_time / 1000000.0)).c_str()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Read newest FFT results from SaProcessor, update local display buffers
|
||||
// and build QPainter paths.
|
||||
void SaSpectrumView::refreshPaths()
|
||||
{
|
||||
// Lock is required for the entire function, mainly to prevent block size
|
||||
// changes from causing reallocation of data structures mid-way.
|
||||
QMutexLocker lock(&m_processor->m_dataAccess);
|
||||
|
||||
// check if bin count changed and reallocate display buffers accordingly
|
||||
if (m_processor->binCount() != m_displayBufferL.size())
|
||||
{
|
||||
m_displayBufferL.clear();
|
||||
m_displayBufferR.clear();
|
||||
m_peakBufferL.clear();
|
||||
m_peakBufferR.clear();
|
||||
m_displayBufferL.resize(m_processor->binCount(), 0);
|
||||
m_displayBufferR.resize(m_processor->binCount(), 0);
|
||||
m_peakBufferL.resize(m_processor->binCount(), 0);
|
||||
m_peakBufferR.resize(m_processor->binCount(), 0);
|
||||
}
|
||||
|
||||
// update display buffers for left and right channel
|
||||
#ifdef SA_DEBUG
|
||||
int refresh_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
m_decaySum = 0;
|
||||
updateBuffers(m_processor->m_normSpectrumL.data(), m_displayBufferL.data(), m_peakBufferL.data());
|
||||
updateBuffers(m_processor->m_normSpectrumR.data(), m_displayBufferR.data(), m_peakBufferR.data());
|
||||
#ifdef SA_DEBUG
|
||||
refresh_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - refresh_time;
|
||||
#endif
|
||||
|
||||
// if there was a freeze request, it was taken care of during the update
|
||||
if (m_controls->m_refFreezeModel.value() && m_freezeRequest)
|
||||
{
|
||||
m_freezeRequest = false;
|
||||
m_frozen = true;
|
||||
}
|
||||
|
||||
#ifdef SA_DEBUG
|
||||
int make_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
// Use updated display buffers to prepare new paths for QPainter.
|
||||
// This is the second slowest action (first is the subsequent drawing); use
|
||||
// the resolution parameter to balance display quality and performance.
|
||||
m_pathL = makePath(m_displayBufferL, 1.5);
|
||||
if (m_controls->m_stereoModel.value())
|
||||
{
|
||||
m_pathR = makePath(m_displayBufferR, 1.5);
|
||||
}
|
||||
if (m_controls->m_peakHoldModel.value() || m_controls->m_refFreezeModel.value())
|
||||
{
|
||||
m_pathPeakL = makePath(m_peakBufferL, 0.25);
|
||||
if (m_controls->m_stereoModel.value())
|
||||
{
|
||||
m_pathPeakR = makePath(m_peakBufferR, 0.25);
|
||||
}
|
||||
}
|
||||
#ifdef SA_DEBUG
|
||||
make_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - make_time;
|
||||
#endif
|
||||
|
||||
#ifdef SA_DEBUG
|
||||
// print measurement results
|
||||
std::cout << "Buffer update ms: " << std::to_string(refresh_time / 1000000.0) << ", ";
|
||||
std::cout << "Path-make ms: " << std::to_string(make_time / 1000000.0) << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Update display buffers: add new data, update average and peaks / reference.
|
||||
// Output the sum of all displayed values -- draw only if it is non-zero.
|
||||
// NOTE: The calling function is responsible for acquiring SaProcessor data
|
||||
// access lock!
|
||||
void SaSpectrumView::updateBuffers(float *spectrum, float *displayBuffer, float *peakBuffer)
|
||||
{
|
||||
for (int n = 0; n < m_processor->binCount(); n++)
|
||||
{
|
||||
// Update the exponential average if enabled, or simply copy the value.
|
||||
if (!m_controls->m_pauseModel.value())
|
||||
{
|
||||
if (m_controls->m_smoothModel.value())
|
||||
{
|
||||
displayBuffer[n] = spectrum[n] * m_smoothFactor + displayBuffer[n] * (1 - m_smoothFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayBuffer[n] = spectrum[n];
|
||||
}
|
||||
}
|
||||
// Update peak-hold and reference freeze data (using a shared curve).
|
||||
// Peak hold and freeze can be combined: decay only if not frozen.
|
||||
// Ref. freeze operates on the (possibly averaged) display buffer.
|
||||
if (m_controls->m_refFreezeModel.value() && m_freezeRequest)
|
||||
{
|
||||
peakBuffer[n] = displayBuffer[n];
|
||||
}
|
||||
else if (m_controls->m_peakHoldModel.value() && !m_controls->m_pauseModel.value())
|
||||
{
|
||||
if (spectrum[n] > peakBuffer[n])
|
||||
{
|
||||
peakBuffer[n] = spectrum[n];
|
||||
}
|
||||
else if (!m_controls->m_refFreezeModel.value())
|
||||
{
|
||||
peakBuffer[n] = peakBuffer[n] * m_peakDecayFactor;
|
||||
}
|
||||
}
|
||||
else if (!m_controls->m_refFreezeModel.value() && !m_controls->m_peakHoldModel.value())
|
||||
{
|
||||
peakBuffer[n] = 0;
|
||||
}
|
||||
// take note if there was actually anything to display
|
||||
m_decaySum += displayBuffer[n] + peakBuffer[n];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use display buffer to build a path that can be drawn or filled by QPainter.
|
||||
// Resolution controls the performance / quality tradeoff; the value specifies
|
||||
// number of points in x axis per device pixel. Values over 1.0 still
|
||||
// contribute to quality and accuracy thanks to anti-aliasing.
|
||||
QPainterPath SaSpectrumView::makePath(std::vector<float> &displayBuffer, float resolution = 1.0)
|
||||
{
|
||||
// convert resolution to number of path points per logical pixel
|
||||
float pixel_limit = resolution * window()->devicePixelRatio();
|
||||
|
||||
QPainterPath path;
|
||||
path.moveTo(m_displayLeft, m_displayBottom);
|
||||
|
||||
// Translate frequency bins to path points.
|
||||
// Display is flipped: y values grow towards zero, initial max is bottom.
|
||||
// Bins falling to interval [x_start, x_next) contribute to a single point.
|
||||
float max = m_displayBottom;
|
||||
float x_start = -1; // lower bound of currently constructed point
|
||||
for (unsigned int n = 0; n < m_processor->binCount(); n++)
|
||||
{
|
||||
float x = freqToXPixel(binToFreq(n), m_displayWidth);
|
||||
float x_next = freqToXPixel(binToFreq(n + 1), m_displayWidth);
|
||||
float y = ampToYPixel(displayBuffer[n], m_displayBottom);
|
||||
|
||||
// consider making a point only if x falls within display bounds
|
||||
if (0 < x && x < m_displayWidth)
|
||||
{
|
||||
if (x_start == -1)
|
||||
{
|
||||
x_start = x;
|
||||
// the first displayed bin is stretched to the left edge to prevent
|
||||
// creating a misleading slope leading to zero (at log. scale)
|
||||
path.lineTo(m_displayLeft, y + m_displayTop);
|
||||
}
|
||||
// Opt.: QPainter is very slow -- draw at most [pixel_limit] points
|
||||
// per logical pixel. As opposed to limiting the bin count, this
|
||||
// allows high resolution display if user resizes the analyzer.
|
||||
// Look at bins that share the pixel and use the highest value:
|
||||
max = y < max ? y : max;
|
||||
// And make the final point in the middle of current interval.
|
||||
if ((int)(x * pixel_limit) != (int)(x_next * pixel_limit))
|
||||
{
|
||||
x = (x + x_start) / 2;
|
||||
path.lineTo(x + m_displayLeft, max + m_displayTop);
|
||||
max = m_displayBottom;
|
||||
x_start = x_next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stop processing after a bin falls outside right edge
|
||||
// and align it to the edge to prevent a gap
|
||||
if (n > 0 && x > 0)
|
||||
{
|
||||
path.lineTo(m_displayRight, y + m_displayTop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path.lineTo(m_displayRight, m_displayBottom);
|
||||
path.closeSubpath();
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
// Draw background, grid and associated frequency and amplitude labels.
|
||||
void SaSpectrumView::drawGrid(QPainter &painter)
|
||||
{
|
||||
std::vector<std::pair<int, std::string>> *freqTics = NULL;
|
||||
std::vector<std::pair<float, std::string>> *ampTics = NULL;
|
||||
float pos = 0;
|
||||
float label_width = 24;
|
||||
float label_height = 15;
|
||||
float margin = 5;
|
||||
|
||||
// always draw the background
|
||||
painter.fillRect(m_displayLeft, m_displayTop,
|
||||
m_displayWidth, m_displayBottom,
|
||||
m_controls->m_colorBG);
|
||||
|
||||
// select logarithmic or linear frequency grid and draw it
|
||||
if (m_controls->m_logXModel.value())
|
||||
{
|
||||
freqTics = &m_logFreqTics;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTics = &m_linearFreqTics;
|
||||
}
|
||||
// draw frequency grid (line.first is display position)
|
||||
painter.setPen(QPen(m_controls->m_colorGrid, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
for (auto &line: *freqTics)
|
||||
{
|
||||
painter.drawLine(m_displayLeft + freqToXPixel(line.first, m_displayWidth),
|
||||
2,
|
||||
m_displayLeft + freqToXPixel(line.first, m_displayWidth),
|
||||
m_displayBottom);
|
||||
}
|
||||
// print frequency labels (line.second is label)
|
||||
painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
for (auto & line: *freqTics)
|
||||
{
|
||||
pos = m_displayLeft + freqToXPixel(line.first, m_displayWidth);
|
||||
// align first and last label to the edge if needed, otherwise center them
|
||||
if (line == freqTics->front() && pos - label_width / 2 < m_displayLeft)
|
||||
{
|
||||
painter.drawText(m_displayLeft, m_displayBottom + margin,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
else if (line == freqTics->back() && pos + label_width / 2 > m_displayRight)
|
||||
{
|
||||
painter.drawText(m_displayRight - label_width, m_displayBottom + margin,
|
||||
label_width, label_height, Qt::AlignRight | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawText(pos - label_width / 2, m_displayBottom + margin,
|
||||
label_width, label_height, Qt::AlignHCenter | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
margin = 2;
|
||||
// select logarithmic or linear amplitude grid and draw it
|
||||
if (m_controls->m_logYModel.value())
|
||||
{
|
||||
ampTics = &m_logAmpTics;
|
||||
}
|
||||
else
|
||||
{
|
||||
ampTics = &m_linearAmpTics;
|
||||
}
|
||||
// draw amplitude grid
|
||||
painter.setPen(QPen(m_controls->m_colorGrid, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
for (auto & line: *ampTics)
|
||||
{
|
||||
painter.drawLine(m_displayLeft + 1,
|
||||
ampToYPixel(line.first, m_displayBottom),
|
||||
m_displayRight - 1,
|
||||
ampToYPixel(line.first, m_displayBottom));
|
||||
}
|
||||
// print amplitude labels
|
||||
painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
bool stereo = m_controls->m_stereoModel.value();
|
||||
for (auto & line: *ampTics)
|
||||
{
|
||||
pos = ampToYPixel(line.first, m_displayBottom);
|
||||
// align first and last labels to edge if needed, otherwise center them
|
||||
if (line == ampTics->back() && pos < 8)
|
||||
{
|
||||
if (stereo)
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorL.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
}
|
||||
painter.drawText(m_displayLeft - label_width - margin, m_displayTop - 2,
|
||||
label_width, label_height, Qt::AlignRight | Qt::AlignTop | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
if (stereo)
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorR.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
}
|
||||
painter.drawText(m_displayRight + margin, m_displayTop - 2,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
else if (line == ampTics->front() && pos > m_displayBottom - label_height)
|
||||
{
|
||||
if (stereo)
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorL.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
}
|
||||
painter.drawText(m_displayLeft - label_width - margin, m_displayBottom - label_height + 2,
|
||||
label_width, label_height, Qt::AlignRight | Qt::AlignBottom | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
if (stereo)
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorR.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
}
|
||||
painter.drawText(m_displayRight + margin, m_displayBottom - label_height + 2,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::AlignBottom | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stereo)
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorL.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
}
|
||||
painter.drawText(m_displayLeft - label_width - margin, pos - label_height / 2,
|
||||
label_width, label_height, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
if (stereo)
|
||||
{
|
||||
painter.setPen(QPen(m_controls->m_colorR.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
}
|
||||
painter.drawText(m_displayRight + margin, pos - label_height / 2,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Draw cursor and its coordinates if it is within display bounds.
|
||||
void SaSpectrumView::drawCursor(QPainter &painter)
|
||||
{
|
||||
if( m_cursor.x() >= m_displayLeft
|
||||
&& m_cursor.x() <= m_displayRight
|
||||
&& m_cursor.y() >= m_displayTop
|
||||
&& m_cursor.y() <= m_displayBottom)
|
||||
{
|
||||
// cursor lines
|
||||
painter.setPen(QPen(m_controls->m_colorGrid.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawLine(m_cursor.x(), m_displayTop, m_cursor.x(), m_displayBottom);
|
||||
painter.drawLine(m_displayLeft, m_cursor.y(), m_displayRight, m_cursor.y());
|
||||
|
||||
// coordinates
|
||||
painter.setPen(QPen(m_controls->m_colorLabels.darker(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawText(m_displayRight -60, 5, 100, 16, Qt::AlignLeft, "Cursor");
|
||||
|
||||
QString tmps;
|
||||
// frequency
|
||||
int xFreq = (int)m_processor->xPixelToFreq(m_cursor.x() - m_displayLeft, m_displayWidth);
|
||||
tmps = QString(std::string(std::to_string(xFreq) + " Hz").c_str());
|
||||
painter.drawText(m_displayRight -60, 18, 100, 16, Qt::AlignLeft, tmps);
|
||||
|
||||
// amplitude
|
||||
float yAmp = m_processor->yPixelToAmp(m_cursor.y(), m_displayBottom);
|
||||
if (m_controls->m_logYModel.value())
|
||||
{
|
||||
tmps = QString(std::string(std::to_string(yAmp).substr(0, 5) + " dB").c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// add 0.0005 to get proper rounding to 3 decimal places
|
||||
tmps = QString(std::string(std::to_string(0.0005f + yAmp)).substr(0, 5).c_str());
|
||||
}
|
||||
painter.drawText(m_displayRight -60, 30, 100, 16, Qt::AlignLeft, tmps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wrappers for most used SaProcessor helpers (to make local code more compact).
|
||||
float SaSpectrumView::binToFreq(unsigned int bin_index)
|
||||
{
|
||||
return m_processor->binToFreq(bin_index);
|
||||
}
|
||||
|
||||
|
||||
float SaSpectrumView::freqToXPixel(float frequency, unsigned int width)
|
||||
{
|
||||
return m_processor->freqToXPixel(frequency, width);
|
||||
}
|
||||
|
||||
|
||||
float SaSpectrumView::ampToYPixel(float amplitude, unsigned int height)
|
||||
{
|
||||
return m_processor->ampToYPixel(amplitude, height);
|
||||
}
|
||||
|
||||
|
||||
// Generate labels suitable for logarithmic frequency scale.
|
||||
// Low / high limits are in Hz. Lowest possible label is 10 Hz.
|
||||
std::vector<std::pair<int, std::string>> SaSpectrumView::makeLogFreqTics(int low, int high)
|
||||
{
|
||||
std::vector<std::pair<int, std::string>> result;
|
||||
int i, j;
|
||||
int a[] = {10, 20, 50}; // sparse series multipliers
|
||||
int b[] = {14, 30, 70}; // additional (denser) series
|
||||
|
||||
// generate main steps (powers of 10); use the series to specify smaller steps
|
||||
for (i = 1; i <= high; i *= 10)
|
||||
{
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
// insert a label from sparse series if it falls within bounds
|
||||
if (i * a[j] >= low && i * a[j] <= high)
|
||||
{
|
||||
if (i * a[j] < 1000)
|
||||
{
|
||||
result.emplace_back(i * a[j], std::to_string(i * a[j]));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(i * a[j], std::to_string(i * a[j] / 1000) + "k");
|
||||
}
|
||||
}
|
||||
// also insert denser series if high and low values are close
|
||||
if ((log10(high) - log10(low) < 2) && (i * b[j] >= low && i * b[j] <= high))
|
||||
{
|
||||
if (i * b[j] < 1500)
|
||||
{
|
||||
result.emplace_back(i * b[j], std::to_string(i * b[j]));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(i * b[j], std::to_string(i * b[j] / 1000) + "k");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Generate labels suitable for linear frequency scale.
|
||||
// Low / high limits are in Hz.
|
||||
std::vector<std::pair<int, std::string>> SaSpectrumView::makeLinearFreqTics(int low, int high)
|
||||
{
|
||||
std::vector<std::pair<int, std::string>> result;
|
||||
int i, increment;
|
||||
|
||||
// select a suitable increment based on zoom level
|
||||
if (high - low < 500) {increment = 50;}
|
||||
else if (high - low < 1000) {increment = 100;}
|
||||
else if (high - low < 5000) {increment = 1000;}
|
||||
else {increment = 2000;}
|
||||
|
||||
// generate steps based on increment, starting at 0
|
||||
for (i = 0; i <= high; i += increment)
|
||||
{
|
||||
if (i >= low)
|
||||
{
|
||||
if (i < 1000)
|
||||
{
|
||||
result.emplace_back(i, std::to_string(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(i, std::to_string(i/1000) + "k");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Generate labels suitable for logarithmic (dB) amplitude scale.
|
||||
// Low / high limits are in dB; 0 dB amplitude = 1.0 linear.
|
||||
// Treating results as power ratio, i.e., 3 dB should be about twice as loud.
|
||||
std::vector<std::pair<float, std::string>> SaSpectrumView::makeLogAmpTics(int low, int high)
|
||||
{
|
||||
std::vector<std::pair<float, std::string>> result;
|
||||
float i;
|
||||
double increment;
|
||||
|
||||
// Base zoom level on selected range and how close is the current height
|
||||
// to the sizeHint() (denser scale for bigger window).
|
||||
if ((high - low) < 20 * ((float)height() / sizeHint().height()))
|
||||
{
|
||||
increment = pow(10, 0.3); // 3 dB steps when really zoomed in
|
||||
}
|
||||
else if (high - low < 45 * ((float)height() / sizeHint().height()))
|
||||
{
|
||||
increment = pow(10, 0.6); // 6 dB steps when sufficiently zoomed in
|
||||
}
|
||||
else
|
||||
{
|
||||
increment = 10; // 10 dB steps otherwise
|
||||
}
|
||||
|
||||
// Generate n dB increments, start checking at -90 dB. Limits are tweaked
|
||||
// just a little bit to make sure float comparisons do not miss edges.
|
||||
for (i = 0.000000001; 10 * log10(i) <= (high + 0.001); i *= increment)
|
||||
{
|
||||
if (10 * log10(i) >= (low - 0.001))
|
||||
{
|
||||
result.emplace_back(i, std::to_string((int)std::round(10 * log10(i))));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Generate labels suitable for linear amplitude scale.
|
||||
// Low / high limits are in dB; 0 dB amplitude = 1.0 linear.
|
||||
// Smallest possible label is 0.001, largest is 999. This includes the majority
|
||||
// of useful labels; going lower or higher would require increasing margin size
|
||||
// so that the text can fit. That would be a waste of space -- the linear scale
|
||||
// would only make the experience worse for the main, logarithmic (dB) scale.
|
||||
std::vector<std::pair<float, std::string>> SaSpectrumView::makeLinearAmpTics(int low, int high)
|
||||
{
|
||||
std::vector<std::pair<float, std::string>> result;
|
||||
double i, nearest;
|
||||
|
||||
// make about 5 labels when window is small, 10 if it is big
|
||||
float split = (float)height() / sizeHint().height() >= 1.5 ? 10.0 : 5.0;
|
||||
|
||||
// convert limits to linear scale
|
||||
float lin_low = pow(10, low / 10.0);
|
||||
float lin_high = pow(10, high / 10.0);
|
||||
|
||||
// Linear scale will vary widely, so instead of trying to craft extra nice
|
||||
// multiples, just generate a few evenly spaced increments across the range,
|
||||
// paying attention only to the decimal places to keep labels short.
|
||||
// Limits are shifted a bit so that float comparisons do not miss edges.
|
||||
for (i = 0; i <= (lin_high + 0.0001); i += (lin_high - lin_low) / split)
|
||||
{
|
||||
if (i >= (lin_low - 0.0001))
|
||||
{
|
||||
if (i >= 9.99 && i < 99.9)
|
||||
{
|
||||
nearest = std::round(i);
|
||||
result.emplace_back(nearest, std::to_string(nearest).substr(0, 2));
|
||||
}
|
||||
else if (i >= 0.099)
|
||||
{ // also covers numbers above 100
|
||||
nearest = std::round(i * 10) / 10;
|
||||
result.emplace_back(nearest, std::to_string(nearest).substr(0, 3));
|
||||
}
|
||||
else if (i >= 0.0099)
|
||||
{
|
||||
nearest = std::round(i * 1000) / 1000;
|
||||
result.emplace_back(nearest, std::to_string(nearest).substr(0, 4));
|
||||
}
|
||||
else if (i >= 0.00099)
|
||||
{
|
||||
nearest = std::round(i * 10000) / 10000;
|
||||
result.emplace_back(nearest, std::to_string(nearest).substr(1, 4));
|
||||
}
|
||||
else if (i > -0.01 && i < 0.01)
|
||||
{
|
||||
result.emplace_back(i, "0"); // an exception, zero is short..
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Periodic update is called by LMMS.
|
||||
void SaSpectrumView::periodicUpdate()
|
||||
{
|
||||
// check if the widget is visible; if it is not, processing can be paused
|
||||
m_processor->setSpectrumActive(isVisible());
|
||||
// tell Qt it is time for repaint
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
// Handle mouse input: set new cursor position.
|
||||
void SaSpectrumView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
m_cursor = event->pos();
|
||||
}
|
||||
|
||||
void SaSpectrumView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
m_cursor = event->pos();
|
||||
}
|
||||
|
||||
|
||||
// Handle resize event: rebuild grid and labels
|
||||
void SaSpectrumView::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
// frequency does not change density with size
|
||||
// amplitude does: rebuild labels
|
||||
m_logAmpTics = makeLogAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax());
|
||||
m_linearAmpTics = makeLinearAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax());
|
||||
}
|
||||
|
||||
126
plugins/SpectrumAnalyzer/SaSpectrumView.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/* SaSpectrumView.h - declaration of SaSpectrumView class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* Based partially on Eq plugin code,
|
||||
* Copyright (c) 2014 David French <dave/dot/french3/at/googlemail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SASPECTRUMVIEW_H
|
||||
#define SASPECTRUMVIEW_H
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <QPainterPath>
|
||||
#include <QWidget>
|
||||
|
||||
class QMouseEvent;
|
||||
class QPainter;
|
||||
class SaControls;
|
||||
class SaProcessor;
|
||||
|
||||
//! Widget that displays a spectrum curve and frequency / amplitude grid
|
||||
class SaSpectrumView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SaSpectrumView(SaControls *controls, SaProcessor *processor, QWidget *_parent = 0);
|
||||
virtual ~SaSpectrumView() {}
|
||||
|
||||
QSize sizeHint() const override {return QSize(400, 200);}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void periodicUpdate();
|
||||
|
||||
private:
|
||||
const SaControls *m_controls;
|
||||
SaProcessor *m_processor;
|
||||
|
||||
// grid labels (position, label) and methods to generate them
|
||||
std::vector<std::pair<int, std::string>> m_logFreqTics; // 10-20-50... Hz
|
||||
std::vector<std::pair<int, std::string>> m_linearFreqTics; // 2k-4k-6k... Hz
|
||||
std::vector<std::pair<float, std::string>> m_logAmpTics; // dB
|
||||
std::vector<std::pair<float, std::string>> m_linearAmpTics; // 0..1
|
||||
|
||||
std::vector<std::pair<int, std::string>> makeLogFreqTics(int low, int high);
|
||||
std::vector<std::pair<int, std::string>> makeLinearFreqTics(int low, int high);
|
||||
std::vector<std::pair<float, std::string>> makeLogAmpTics(int low, int high);
|
||||
std::vector<std::pair<float, std::string>> makeLinearAmpTics(int low, int high);
|
||||
|
||||
// currently selected ranges (see SaControls.h for enum definitions)
|
||||
int m_freqRangeIndex;
|
||||
int m_ampRangeIndex;
|
||||
|
||||
// draw the grid and all labels based on selected ranges
|
||||
void drawGrid(QPainter &painter);
|
||||
|
||||
// local buffers for frequency bin values and a method to update them
|
||||
// (mainly needed for averaging and to keep track of peak values)
|
||||
std::vector<float> m_displayBufferL;
|
||||
std::vector<float> m_displayBufferR;
|
||||
std::vector<float> m_peakBufferL;
|
||||
std::vector<float> m_peakBufferR;
|
||||
void updateBuffers(float *spectrum, float *displayBuffer, float *peakBuffer);
|
||||
|
||||
// final paths to be drawn by QPainter and methods to build them
|
||||
QPainterPath m_pathL;
|
||||
QPainterPath m_pathR;
|
||||
QPainterPath m_pathPeakL;
|
||||
QPainterPath m_pathPeakR;
|
||||
void refreshPaths();
|
||||
QPainterPath makePath(std::vector<float> &displayBuffer, float resolution);
|
||||
|
||||
// helper variables for path drawing
|
||||
float m_decaySum; // indicates if there is anything left to draw
|
||||
bool m_freezeRequest; // new reference should be acquired
|
||||
bool m_frozen; // a reference is currently stored in the peakBuffer
|
||||
|
||||
const float m_smoothFactor = 0.15; // alpha for exponential smoothing
|
||||
const float m_peakDecayFactor = 0.992; // multiplier for gradual peak decay
|
||||
|
||||
// top level: refresh buffers, make paths and draw the spectrum
|
||||
void drawSpectrum(QPainter &painter);
|
||||
|
||||
// current cursor location and a method to draw it
|
||||
QPoint m_cursor;
|
||||
void drawCursor(QPainter &painter);
|
||||
|
||||
// wrappers for most used SaProcessor conversion helpers
|
||||
// (to make local code more readable)
|
||||
float binToFreq(unsigned int bin_index);
|
||||
float freqToXPixel(float frequency, unsigned int width);
|
||||
float ampToYPixel(float amplitude, unsigned int height);
|
||||
|
||||
// current boundaries for drawing
|
||||
unsigned int m_displayTop;
|
||||
unsigned int m_displayBottom;
|
||||
unsigned int m_displayLeft;
|
||||
unsigned int m_displayRight;
|
||||
unsigned int m_displayWidth;
|
||||
};
|
||||
#endif // SASPECTRUMVIEW_H
|
||||
|
||||
230
plugins/SpectrumAnalyzer/SaWaterfallView.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/* SaWaterfallViewView.cpp - implementation of SaWaterfallViewView class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SaWaterfallView.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <QImage>
|
||||
#include <QMutexLocker>
|
||||
#include <QPainter>
|
||||
#include <QSplitter>
|
||||
#include <QString>
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
#include "SaProcessor.h"
|
||||
|
||||
|
||||
SaWaterfallView::SaWaterfallView(SaControls *controls, SaProcessor *processor, QWidget *_parent) :
|
||||
QWidget(_parent),
|
||||
m_controls(controls),
|
||||
m_processor(processor)
|
||||
{
|
||||
m_controlDialog = (EffectControlDialog*) _parent;
|
||||
setMinimumSize(300, 150);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
connect(gui->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(periodicUpdate()));
|
||||
|
||||
m_timeTics = makeTimeTics();
|
||||
m_oldTimePerLine = (float)m_processor->m_inBlockSize / m_processor->getSampleRate();
|
||||
}
|
||||
|
||||
|
||||
// Compose and draw all the content; called by Qt.
|
||||
// Not as performance sensitive as SaSpectrumView, most of the processing is
|
||||
// done directly in SaProcessor.
|
||||
void SaWaterfallView::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
#ifdef SA_DEBUG
|
||||
int start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
#endif
|
||||
|
||||
// all drawing done here, local variables are sufficient for the boundary
|
||||
const int displayTop = 1;
|
||||
const int displayBottom = height() -2;
|
||||
const int displayLeft = 26;
|
||||
const int displayRight = width() -26;
|
||||
const int displayWidth = displayRight - displayLeft;
|
||||
float label_width = 20;
|
||||
float label_height = 16;
|
||||
float margin = 2;
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// check if time labels need to be rebuilt
|
||||
if ((float)m_processor->m_inBlockSize / m_processor->getSampleRate() != m_oldTimePerLine)
|
||||
{
|
||||
m_timeTics = makeTimeTics();
|
||||
m_oldTimePerLine = (float)m_processor->m_inBlockSize / m_processor->getSampleRate();
|
||||
}
|
||||
|
||||
// print time labels
|
||||
float pos = 0;
|
||||
painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
for (auto & line: m_timeTics)
|
||||
{
|
||||
pos = timeToYPixel(line.first, displayBottom);
|
||||
// align first and last label to the edge if needed, otherwise center them
|
||||
if (line == m_timeTics.front() && pos < label_height / 2)
|
||||
{
|
||||
painter.drawText(displayLeft - label_width - margin, displayTop - 1,
|
||||
label_width, label_height, Qt::AlignRight | Qt::AlignTop | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
painter.drawText(displayRight + margin, displayTop - 1,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
else if (line == m_timeTics.back() && pos > displayBottom - label_height + 2)
|
||||
{
|
||||
painter.drawText(displayLeft - label_width - margin, displayBottom - label_height,
|
||||
label_width, label_height, Qt::AlignRight | Qt::AlignBottom | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
painter.drawText(displayRight + margin, displayBottom - label_height + 2,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::AlignBottom | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawText(displayLeft - label_width - margin, pos - label_height / 2,
|
||||
label_width, label_height, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
painter.drawText(displayRight + margin, pos - label_height / 2,
|
||||
label_width, label_height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip,
|
||||
QString(line.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// draw the spectrogram precomputed in SaProcessor
|
||||
if (m_processor->m_waterfallNotEmpty)
|
||||
{
|
||||
QMutexLocker lock(&m_processor->m_dataAccess);
|
||||
painter.drawImage(displayLeft, displayTop, // top left corner coordinates
|
||||
QImage(m_processor->m_history.data(), // raw pixel data to display
|
||||
m_processor->binCount(), // width = number of frequency bins
|
||||
m_processor->m_waterfallHeight, // height = number of history lines
|
||||
QImage::Format_RGB32
|
||||
).scaled(displayWidth, // scale to fit view..
|
||||
displayBottom,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
lock.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.fillRect(displayLeft, displayTop, displayWidth, displayBottom, QColor(0,0,0));
|
||||
}
|
||||
|
||||
// always draw the outline
|
||||
painter.setPen(QPen(m_controls->m_colorGrid, 2, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawRoundedRect(displayLeft, displayTop, displayWidth, displayBottom, 2.0, 2.0);
|
||||
|
||||
#ifdef SA_DEBUG
|
||||
// display what FPS would be achieved if waterfall ran in a loop
|
||||
start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - start_time;
|
||||
painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
|
||||
painter.drawText(displayRight -100, 10, 100, 16, Qt::AlignLeft,
|
||||
QString(std::string("Max FPS: " + std::to_string(1000000000.0 / start_time)).c_str()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Convert time value to Y coordinate for display of given height.
|
||||
float SaWaterfallView::timeToYPixel(float time, int height)
|
||||
{
|
||||
float pixels_per_line = (float)height / m_processor->m_waterfallHeight;
|
||||
float seconds_per_line = ((float)m_processor->m_inBlockSize / m_processor->getSampleRate());
|
||||
|
||||
return pixels_per_line * time / seconds_per_line;
|
||||
}
|
||||
|
||||
|
||||
// Generate labels for linear time scale.
|
||||
std::vector<std::pair<float, std::string>> SaWaterfallView::makeTimeTics()
|
||||
{
|
||||
std::vector<std::pair<float, std::string>> result;
|
||||
float i;
|
||||
|
||||
// upper limit defined by number of lines * time per line
|
||||
float limit = m_processor->m_waterfallHeight * ((float)m_processor->m_inBlockSize / m_processor->getSampleRate());
|
||||
|
||||
// set increment so that about 8 tics are generated
|
||||
float increment = std::round(10 * limit / 7) / 10;
|
||||
|
||||
// NOTE: labels positions are rounded to match the (rounded) label value
|
||||
for (i = 0; i <= limit; i += increment)
|
||||
{
|
||||
if (i < 10)
|
||||
{
|
||||
result.emplace_back(std::round(i * 10) / 10, std::to_string(std::round(i * 10) / 10).substr(0, 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(std::round(i), std::to_string(std::round(i)).substr(0, 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Periodically trigger repaint and check if the widget is visible.
|
||||
// If it is not, stop drawing and inform the processor.
|
||||
void SaWaterfallView::periodicUpdate()
|
||||
{
|
||||
m_processor->setWaterfallActive(isVisible());
|
||||
if (isVisible()) {update();}
|
||||
}
|
||||
|
||||
|
||||
// Adjust window size and widget visibility when waterfall is enabled or disabbled.
|
||||
void SaWaterfallView::updateVisibility()
|
||||
{
|
||||
// get container of the control dialog to be resized if needed
|
||||
QWidget *subWindow = m_controlDialog->parentWidget();
|
||||
|
||||
|
||||
if (m_controls->m_waterfallModel.value())
|
||||
{
|
||||
// clear old data before showing the waterfall
|
||||
QMutexLocker lock(&m_processor->m_dataAccess);
|
||||
std::fill(m_processor->m_history.begin(), m_processor->m_history.end(), 0);
|
||||
lock.unlock();
|
||||
|
||||
setVisible(true);
|
||||
|
||||
// increase window size if it is too small
|
||||
if (subWindow->size().height() < m_controlDialog->sizeHint().height())
|
||||
{
|
||||
subWindow->resize(subWindow->size().width(), m_controlDialog->sizeHint().height());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setVisible(false);
|
||||
// decrease window size only if it does not violate sizeHint
|
||||
subWindow->resize(subWindow->size().width(), m_controlDialog->sizeHint().height());
|
||||
}
|
||||
}
|
||||
|
||||
66
plugins/SpectrumAnalyzer/SaWaterfallView.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* SaWaterfallView.h - declaration of SaWaterfallView class.
|
||||
*
|
||||
* Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#ifndef SAWATERFALLVIEW_H
|
||||
#define SAWATERFALLVIEW_H
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
|
||||
#include "SaControls.h"
|
||||
#include "SaProcessor.h"
|
||||
|
||||
|
||||
// Widget that displays a spectrum waterfall (spectrogram) and time labels.
|
||||
class SaWaterfallView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SaWaterfallView(SaControls *controls, SaProcessor *processor, QWidget *_parent = 0);
|
||||
virtual ~SaWaterfallView() {}
|
||||
|
||||
QSize sizeHint() const override {return QSize(400, 350);}
|
||||
|
||||
// Check if waterfall should be displayed and adjust window size if needed.
|
||||
void updateVisibility();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void periodicUpdate();
|
||||
|
||||
private:
|
||||
const SaControls *m_controls;
|
||||
SaProcessor *m_processor;
|
||||
const EffectControlDialog *m_controlDialog;
|
||||
|
||||
// Methods and data used to make time labels
|
||||
float m_oldTimePerLine;
|
||||
float timeToYPixel(float time, int height);
|
||||
std::vector<std::pair<float, std::string>> makeTimeTics();
|
||||
std::vector<std::pair<float, std::string>> m_timeTics; // 0..n (s)
|
||||
};
|
||||
#endif // SAWATERFALLVIEW_H
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* SpectrumAnalyzer.cpp - spectrum analyzer effect plugin
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SpectrumAnalyzer.h"
|
||||
|
||||
#include "embed.h"
|
||||
#include "plugin_export.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT spectrumanalyzer_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
"Spectrum Analyzer",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser", "Graphical spectrum analyzer plugin" ),
|
||||
"Tobias Doerffel <tobydox/at/users.sf.net>",
|
||||
0x0100,
|
||||
Plugin::Effect,
|
||||
new PluginPixmapLoader(),
|
||||
NULL,
|
||||
NULL
|
||||
} ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
SpectrumAnalyzer::SpectrumAnalyzer( Model * _parent,
|
||||
const Descriptor::SubPluginFeatures::Key * _key ) :
|
||||
Effect( &spectrumanalyzer_plugin_descriptor, _parent, _key ),
|
||||
m_saControls( this ),
|
||||
m_framesFilledUp( 0 ),
|
||||
m_energy( 0 )
|
||||
{
|
||||
memset( m_buffer, 0, sizeof( m_buffer ) );
|
||||
|
||||
m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) );
|
||||
m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SpectrumAnalyzer::~SpectrumAnalyzer()
|
||||
{
|
||||
fftwf_destroy_plan( m_fftPlan );
|
||||
fftwf_free( m_specBuf );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool SpectrumAnalyzer::processAudioBuffer( sampleFrame* _buf, const fpp_t _frames )
|
||||
{
|
||||
if( !isEnabled() || !isRunning () )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !m_saControls.isViewVisible() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
fpp_t f = 0;
|
||||
if( _frames > FFT_BUFFER_SIZE )
|
||||
{
|
||||
m_framesFilledUp = 0;
|
||||
f = _frames - FFT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
const int cm = m_saControls.m_channelMode.value();
|
||||
|
||||
switch( cm )
|
||||
{
|
||||
case MergeChannels:
|
||||
for( ; f < _frames; ++f )
|
||||
{
|
||||
m_buffer[m_framesFilledUp] =
|
||||
( _buf[f][0] + _buf[f][1] ) * 0.5;
|
||||
++m_framesFilledUp;
|
||||
}
|
||||
break;
|
||||
case LeftChannel:
|
||||
for( ; f < _frames; ++f )
|
||||
{
|
||||
m_buffer[m_framesFilledUp] = _buf[f][0];
|
||||
++m_framesFilledUp;
|
||||
}
|
||||
break;
|
||||
case RightChannel:
|
||||
for( ; f < _frames; ++f )
|
||||
{
|
||||
m_buffer[m_framesFilledUp] = _buf[f][1];
|
||||
++m_framesFilledUp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if( m_framesFilledUp < FFT_BUFFER_SIZE )
|
||||
{
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
|
||||
// hanming( m_buffer, FFT_BUFFER_SIZE, HAMMING );
|
||||
|
||||
const sample_rate_t sr = Engine::mixer()->processingSampleRate();
|
||||
const int LOWEST_FREQ = 0;
|
||||
const int HIGHEST_FREQ = sr / 2;
|
||||
|
||||
fftwf_execute( m_fftPlan );
|
||||
absspec( m_specBuf, m_absSpecBuf, FFT_BUFFER_SIZE+1 );
|
||||
if( m_saControls.m_linearSpec.value() )
|
||||
{
|
||||
compressbands( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1,
|
||||
MAX_BANDS,
|
||||
(int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(sr/2)),
|
||||
(int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(sr/2)));
|
||||
m_energy = maximum( m_bands, MAX_BANDS ) / maximum( m_buffer, FFT_BUFFER_SIZE );
|
||||
}
|
||||
else
|
||||
{
|
||||
calc13octaveband31( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1, sr/2.0 );
|
||||
m_energy = signalpower( m_buffer, FFT_BUFFER_SIZE ) / maximum( m_buffer, FFT_BUFFER_SIZE );
|
||||
}
|
||||
|
||||
|
||||
m_framesFilledUp = 0;
|
||||
|
||||
checkGate( 1 );
|
||||
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// necessary for getting instance out of shared lib
|
||||
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data )
|
||||
{
|
||||
return new SpectrumAnalyzer( parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>( data ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* SpectrumAnalyzer.h - spectrum anaylzer effect plugin
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _SPECTRUM_ANALYZER_H
|
||||
#define _SPECTRUM_ANALYZER_H
|
||||
|
||||
#include "Effect.h"
|
||||
#include "fft_helpers.h"
|
||||
#include "SpectrumAnalyzerControls.h"
|
||||
|
||||
|
||||
const int MAX_BANDS = 249;
|
||||
|
||||
|
||||
class SpectrumAnalyzer : public Effect
|
||||
{
|
||||
public:
|
||||
enum ChannelModes
|
||||
{
|
||||
MergeChannels,
|
||||
LeftChannel,
|
||||
RightChannel
|
||||
} ;
|
||||
|
||||
SpectrumAnalyzer( Model * _parent,
|
||||
const Descriptor::SubPluginFeatures::Key * _key );
|
||||
virtual ~SpectrumAnalyzer();
|
||||
virtual bool processAudioBuffer( sampleFrame * _buf,
|
||||
const fpp_t _frames );
|
||||
|
||||
virtual EffectControls * controls()
|
||||
{
|
||||
return( &m_saControls );
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
SpectrumAnalyzerControls m_saControls;
|
||||
|
||||
fftwf_plan m_fftPlan;
|
||||
|
||||
fftwf_complex * m_specBuf;
|
||||
float m_absSpecBuf[FFT_BUFFER_SIZE+1];
|
||||
float m_buffer[FFT_BUFFER_SIZE*2];
|
||||
int m_framesFilledUp;
|
||||
|
||||
float m_bands[MAX_BANDS];
|
||||
float m_energy;
|
||||
|
||||
friend class SpectrumAnalyzerControls;
|
||||
friend class SpectrumView;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* SpectrumAnalyzerControlDialog.cpp - view for spectrum analyzer
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QLayout>
|
||||
#include <QPainter>
|
||||
|
||||
#include "SpectrumAnalyzer.h"
|
||||
#include "MainWindow.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "embed.h"
|
||||
|
||||
|
||||
static inline void darken( QImage& img, int x, int y, int w, int h )
|
||||
{
|
||||
int imgWidth = img.width();
|
||||
QRgb * base = ( (QRgb *) img.bits() ) + y*imgWidth + x;
|
||||
for( int y = 0; y < h; ++y )
|
||||
{
|
||||
QRgb * d = base + y*imgWidth;
|
||||
for( int x = 0; x < w; ++x )
|
||||
{
|
||||
// shift each color component by 1 bit and set alpha
|
||||
// to 0xff
|
||||
d[x] = ( ( d[x] >> 1 ) & 0x7f7f7f7f ) | 0xff000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SpectrumView : public QWidget
|
||||
{
|
||||
public:
|
||||
SpectrumView( SpectrumAnalyzer* s, QWidget * _parent ) :
|
||||
QWidget( _parent ),
|
||||
m_sa( s ),
|
||||
m_backgroundPlain( PLUGIN_NAME::getIconPixmap( "spectrum_background_plain" ).toImage() ),
|
||||
m_background( PLUGIN_NAME::getIconPixmap( "spectrum_background" ).toImage() )
|
||||
{
|
||||
setFixedSize( 249, 151 );
|
||||
connect( gui->mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) );
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
}
|
||||
|
||||
virtual ~SpectrumView()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void paintEvent( QPaintEvent* event )
|
||||
{
|
||||
QPainter p( this );
|
||||
QImage i = m_sa->m_saControls.m_linearSpec.value() ?
|
||||
m_backgroundPlain : m_background;
|
||||
const float e = m_sa->m_energy;
|
||||
if( e <= 0 )
|
||||
{
|
||||
darken( i, 0, 0, i.width(), i.height() );
|
||||
p.drawImage( 0, 0, i );
|
||||
return;
|
||||
}
|
||||
|
||||
const bool lin_y = m_sa->m_saControls.m_linearYAxis.value();
|
||||
float * b = m_sa->m_bands;
|
||||
const int LOWER_Y = -60; // dB
|
||||
int h;
|
||||
const int fh = height();
|
||||
if( m_sa->m_saControls.m_linearSpec.value() )
|
||||
{
|
||||
if( lin_y )
|
||||
{
|
||||
for( int x = 0; x < MAX_BANDS; ++x, ++b )
|
||||
{
|
||||
h = fh * 2.0 / 3.0 * (*b / e );
|
||||
if( h < 0 ) h = 0; else if( h >= fh ) continue;
|
||||
darken( i, x, 0, 1, fh-h );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int x = 0; x < MAX_BANDS; ++x, ++b )
|
||||
{
|
||||
h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) );
|
||||
if( h < 0 ) h = 0; else if( h >= fh ) continue;
|
||||
darken( i, x, 0, 1, fh-h );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( lin_y )
|
||||
{
|
||||
for( int x = 0; x < 31; ++x, ++b )
|
||||
{
|
||||
h = fh * 2.0 / 3.0 * ( 1.2 * *b / e );
|
||||
if( h < 0 ) h = 0; else if( h >= fh ) continue; else h = ( h / 3 ) * 3;
|
||||
darken( i, x*8, 0, 8, fh-h );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int x = 0; x < 31; ++x, ++b )
|
||||
{
|
||||
h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) );
|
||||
if( h < 0 ) h = 0; else if( h >= fh ) continue; else h = ( h / 3 ) * 3;
|
||||
darken( i, x*8, 0, 8, fh-h );
|
||||
}
|
||||
}
|
||||
darken( i, 31*8, 0, 1, fh );
|
||||
}
|
||||
p.drawImage( 0, 0, i );
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
SpectrumAnalyzer * m_sa;
|
||||
QImage m_backgroundPlain;
|
||||
QImage m_background;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
SpectrumAnalyzerControlDialog::SpectrumAnalyzerControlDialog( SpectrumAnalyzerControls* controls ) :
|
||||
EffectControlDialog( controls ),
|
||||
m_controls( controls ),
|
||||
m_logXAxis( PLUGIN_NAME::getIconPixmap( "log_x_axis" ) ),
|
||||
m_logYAxis( PLUGIN_NAME::getIconPixmap( "log_y_axis" ) )
|
||||
{
|
||||
setAutoFillBackground( true );
|
||||
QPalette pal;
|
||||
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "background" ) );
|
||||
setFixedSize( 293, 205 );
|
||||
setPalette( pal );
|
||||
/* QVBoxLayout * l = new QVBoxLayout( this );*/
|
||||
SpectrumView* v = new SpectrumView( controls->m_effect, this );
|
||||
v->move( 34, 10 );
|
||||
|
||||
LedCheckBox * lin_spec = new LedCheckBox( tr( "Linear spectrum" ), this );
|
||||
lin_spec->move( 32, 182 );
|
||||
lin_spec->setModel( &controls->m_linearSpec );
|
||||
|
||||
LedCheckBox * lin_y = new LedCheckBox( tr( "Linear Y axis" ), this );
|
||||
lin_y->move( 137, 182 );
|
||||
lin_y->setModel( &controls->m_linearYAxis );
|
||||
|
||||
connect( &controls->m_linearSpec, SIGNAL( dataChanged() ), this, SLOT( update() ) );
|
||||
connect( &controls->m_linearYAxis, SIGNAL( dataChanged() ), this, SLOT( update() ) );
|
||||
/* l->addWidget( v );
|
||||
l->addWidget( lin_spec );
|
||||
l->addWidget( lin_y );*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SpectrumAnalyzerControlDialog::paintEvent( QPaintEvent * )
|
||||
{
|
||||
QPainter p( this );
|
||||
|
||||
if( !m_controls->m_linearSpec.value() )
|
||||
{
|
||||
p.drawPixmap( 33, 165, m_logXAxis );
|
||||
}
|
||||
|
||||
if( !m_controls->m_linearYAxis.value() )
|
||||
{
|
||||
p.drawPixmap( 10, 29, m_logYAxis);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* SpectrumAnalyzerControls.cpp - controls for spectrum analyzer
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "SpectrumAnalyzer.h"
|
||||
#include "SpectrumAnalyzerControls.h"
|
||||
|
||||
|
||||
|
||||
SpectrumAnalyzerControls::SpectrumAnalyzerControls( SpectrumAnalyzer* effect ) :
|
||||
EffectControls( effect ),
|
||||
m_effect( effect ),
|
||||
m_linearSpec( false, this, tr( "Linear spectrum" ) ),
|
||||
m_linearYAxis( false, this, tr( "Linear Y axis" ) ),
|
||||
m_channelMode( SpectrumAnalyzer::MergeChannels,
|
||||
SpectrumAnalyzer::MergeChannels,
|
||||
SpectrumAnalyzer::RightChannel,
|
||||
this, tr( "Channel mode" ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SpectrumAnalyzerControls::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SpectrumAnalyzerControls::saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _this )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* SpectrumAnalyzerControls.h - controls for spectrum-analyzer
|
||||
*
|
||||
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SPECTRUM_ANALYZER_CONTROLS_H
|
||||
#define SPECTRUM_ANALYZER_CONTROLS_H
|
||||
|
||||
#include "EffectControls.h"
|
||||
#include "SpectrumAnalyzerControlDialog.h"
|
||||
#include "Knob.h"
|
||||
|
||||
|
||||
class SpectrumAnalyzer;
|
||||
|
||||
|
||||
class SpectrumAnalyzerControls : public EffectControls
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SpectrumAnalyzerControls( SpectrumAnalyzer* effect );
|
||||
virtual ~SpectrumAnalyzerControls()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
inline virtual QString nodeName() const
|
||||
{
|
||||
return "spectrumanaylzercontrols";
|
||||
}
|
||||
|
||||
virtual int controlCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual EffectControlDialog * createView()
|
||||
{
|
||||
return new SpectrumAnalyzerControlDialog( this );
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
SpectrumAnalyzer* m_effect;
|
||||
BoolModel m_linearSpec;
|
||||
BoolModel m_linearYAxis;
|
||||
IntModel m_channelMode;
|
||||
|
||||
friend class SpectrumAnalyzer;
|
||||
friend class SpectrumAnalyzerControlDialog;
|
||||
friend class SpectrumView;
|
||||
|
||||
} ;
|
||||
|
||||
#endif
|
||||
|
Before Width: | Height: | Size: 859 B |
273
plugins/SpectrumAnalyzer/block_size.svg
Normal file
@@ -0,0 +1,273 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="block_size.svg.2019_03_31_01_06_33.0.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutM"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutM"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path8579"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.4)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInM"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInM"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path8570"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.4)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="154.07316"
|
||||
inkscape:cy="214.45272"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 72.999999,117 v 80"
|
||||
id="path8424"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8426"
|
||||
d="m 148,117 v 80"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInM);marker-end:url(#TriangleOutM)"
|
||||
d="M 87.113439,142.15138 H 133.56041"
|
||||
id="path8432"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path9420"
|
||||
d="m 79.86528,184.07963 h 62.36081"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4.09999943;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4.10000002, 4.10000002;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<g
|
||||
id="g8730">
|
||||
<path
|
||||
id="path8736"
|
||||
d="m 77.881439,142.15138 13.84,-8 v 16 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.60000008pt;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path8738"
|
||||
d="m 142.79241,142.15138 -13.84,8 v -16 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.60000008pt;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.6 KiB |
296
plugins/SpectrumAnalyzer/freeze.svg
Normal file
@@ -0,0 +1,296 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="freeze.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="61.391665"
|
||||
inkscape:cy="165.33406"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:snap-center="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 110,117 v 80"
|
||||
id="path8424"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8426"
|
||||
d="M 75.358985,177 144.64101,137"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8591"
|
||||
d="M 144.64101,177 75.358984,137"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<g
|
||||
id="g8599">
|
||||
<path
|
||||
inkscape:transform-center-x="6.9453125"
|
||||
inkscape:transform-center-y="-6.9453101"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 97.160779,123.8149 12.846151,12.84614"
|
||||
id="path8593"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8595"
|
||||
d="m 122.83921,123.8149 -12.84614,12.84614"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:transform-center-y="-6.9453101"
|
||||
inkscape:transform-center-x="-6.94531"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g8599"
|
||||
id="use8515"
|
||||
inkscape:transform-center-y="-17.121722"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="rotate(60,110,157)"
|
||||
inkscape:transform-center-x="-27.10753" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8515"
|
||||
inkscape:transform-center-x="-27.107531"
|
||||
inkscape:transform-center-y="17.121718"
|
||||
id="use8517"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8517"
|
||||
inkscape:transform-center-x="-4.7502381e-06"
|
||||
inkscape:transform-center-y="27.2912"
|
||||
id="use8519"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8519"
|
||||
inkscape:transform-center-x="27.107525"
|
||||
inkscape:transform-center-y="17.121722"
|
||||
id="use8521"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8521"
|
||||
inkscape:transform-center-x="27.107527"
|
||||
inkscape:transform-center-y="-17.121718"
|
||||
id="use8523"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
297
plugins/SpectrumAnalyzer/freeze_off.svg
Normal file
@@ -0,0 +1,297 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="freeze_off.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="54.320595"
|
||||
inkscape:cy="173.66782"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g8599"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:snap-center="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#8a8e96;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 110,117 v 80"
|
||||
id="path8424"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8426"
|
||||
d="M 75.358985,177 144.64101,137"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#8a8e96;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8591"
|
||||
d="M 144.64101,177 75.358984,137"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#8a8e96;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<g
|
||||
id="g8599"
|
||||
style="stroke:#8a8e96;stroke-opacity:1">
|
||||
<path
|
||||
inkscape:transform-center-x="6.9453125"
|
||||
inkscape:transform-center-y="-6.9453101"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#8a8e96;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 97.160779,123.8149 12.846151,12.84614"
|
||||
id="path8593"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8595"
|
||||
d="m 122.83921,123.8149 -12.84614,12.84614"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#8a8e96;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:transform-center-y="-6.9453101"
|
||||
inkscape:transform-center-x="-6.94531"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g8599"
|
||||
id="use8515"
|
||||
inkscape:transform-center-y="-16.857138"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="rotate(60,110,157)"
|
||||
inkscape:transform-center-x="-26.746102" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8515"
|
||||
inkscape:transform-center-x="-26.746104"
|
||||
inkscape:transform-center-y="16.857132"
|
||||
id="use8517"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8517"
|
||||
inkscape:transform-center-x="-8.3316985e-06"
|
||||
inkscape:transform-center-y="27.026615"
|
||||
id="use8519"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8519"
|
||||
inkscape:transform-center-x="26.746093"
|
||||
inkscape:transform-center-y="16.857138"
|
||||
id="use8521"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use8521"
|
||||
inkscape:transform-center-x="26.746095"
|
||||
inkscape:transform-center-y="-16.857132"
|
||||
id="use8523"
|
||||
transform="rotate(60,110,157)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 734 B |
220
plugins/SpectrumAnalyzer/pause.svg
Normal file
@@ -0,0 +1,220 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="pause.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.5247505"
|
||||
inkscape:cx="186.56659"
|
||||
inkscape:cy="175.68099"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 88.874998,122 v 70"
|
||||
id="path8424"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8426"
|
||||
d="m 132.12499,122 v 70"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
219
plugins/SpectrumAnalyzer/play.svg
Normal file
@@ -0,0 +1,219 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="play.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="71.366921"
|
||||
inkscape:cy="213.56884"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<g
|
||||
id="g8730"
|
||||
transform="translate(0,-10.583333)">
|
||||
<path
|
||||
id="path8738"
|
||||
d="m 140.92066,167.57847 -63.353995,35.67994 v -71.3599 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.60000008pt;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 819 B |
232
plugins/SpectrumAnalyzer/window.svg
Normal file
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="99.73542mm"
|
||||
height="99.73542mm"
|
||||
viewBox="0 0 99.73542 99.73542"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="window.svg"
|
||||
inkscape:export-filename="/home/martin/projects/software/lmms/plugins/SpectrumAnalyzer/window.svg.png"
|
||||
inkscape:export-xdpi="16.299124"
|
||||
inkscape:export-ydpi="16.299124">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutM"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutM"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8579"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.4)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInM"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInM"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8570"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.4)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="64.760601"
|
||||
inkscape:cy="152.43979"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-60.13229,-107.13229)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 72.999999,117 v 80"
|
||||
id="path8424"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path8426"
|
||||
d="m 148,117 v 80"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 73.884273,181.83888 c 0,0 12.89641,-6.08542 18.543384,-20.6375 5.646974,-14.55209 7.824273,-34.925 17.909263,-34.925 10.08499,0 12.2623,20.37291 17.90927,34.925 5.64698,14.55208 18.54338,20.6375 18.54338,20.6375"
|
||||
id="path8432"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cszsc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path9420"
|
||||
d="M 79.675577,184.07963 H 142.5001"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4.0999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4.1, 4.1;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
242
plugins/SpectrumAnalyzer/x_linear.svg
Normal file
@@ -0,0 +1,242 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="x_linear.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-138.52362"
|
||||
inkscape:cy="185.47638"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<g
|
||||
aria-label="X"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8591"
|
||||
transform="translate(0,-1.0583333)">
|
||||
<path
|
||||
d="M 116.01015,174.93644 127.60979,192 h -8.97964 L 110.81495,180.57951 103.06693,192 H 94.0425 l 11.59964,-17.06356 -11.151777,-16.36937 h 9.002037 l 7.32255,10.77109 7.30015,-10.77109 h 9.04683 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:45.86111069px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path8505"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(-2.39e-4,-3.2244934)"
|
||||
aria-label="LOG"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';letter-spacing:0px;word-spacing:0px;fill:#8a8e96;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8595-3">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 77.419237,123.94899 h 5.977241 v 20.70502 h 10.47309 v 5.01262 H 77.419237 Z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path26803" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 107.01261,128.28981 q -2.72163,0 -4.23747,2.23931 -1.49862,2.23931 -1.49862,6.30452 0,4.04799 1.49862,6.2873 1.51584,2.23932 4.23747,2.23932 2.73885,0 4.23747,-2.23932 1.51584,-2.23931 1.51584,-6.2873 0,-4.06521 -1.51584,-6.30452 -1.49862,-2.23931 -4.23747,-2.23931 z m 0,-4.80591 q 5.58105,0 8.73332,3.54845 3.16948,3.54845 3.16948,9.80129 0,6.23563 -3.16948,9.78408 -3.15227,3.54844 -8.73332,3.54844 -5.56383,0 -8.733319,-3.54844 -3.169488,-3.54845 -3.169488,-9.78408 0,-6.25284 3.169488,-9.80129 3.169489,-3.54845 8.733319,-3.54845 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path26805" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 144.25409,147.7546 q -2.23931,1.20578 -4.65088,1.80867 -2.39434,0.60289 -4.94371,0.60289 -5.78776,0 -9.16395,-3.58289 -3.3762,-3.60013 -3.3762,-9.74963 0,-6.21839 3.42787,-9.78407 3.4451,-3.56567 9.43956,-3.56567 2.30822,0 4.42695,0.48231 2.11874,0.48232 3.99631,1.42972 v 5.32267 q -1.94648,-1.22301 -3.8585,-1.8259 -1.91203,-0.60289 -3.84128,-0.60289 -3.56568,0 -5.49493,2.22208 -1.92926,2.20487 -1.92926,6.32175 0,4.08244 1.86036,6.30453 1.86035,2.22209 5.28822,2.22209 0.93017,0 1.72255,-0.12058 0.80959,-0.13781 1.42971,-0.41341 v -4.99539 h -3.63458 v -4.44418 h 9.30176 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path26807" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
240
plugins/SpectrumAnalyzer/x_log.svg
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="x_log.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-140.02362"
|
||||
inkscape:cy="188.97638"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<g
|
||||
aria-label="LOG"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8595"
|
||||
transform="translate(0,-3.2244934)">
|
||||
<path
|
||||
d="m 77.419237,123.94899 h 5.977241 v 20.70502 h 10.47309 v 5.01262 H 77.419237 Z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path26796"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 107.01261,128.28981 q -2.72163,0 -4.23747,2.23931 -1.49862,2.23931 -1.49862,6.30452 0,4.04799 1.49862,6.2873 1.51584,2.23932 4.23747,2.23932 2.73885,0 4.23747,-2.23932 1.51584,-2.23931 1.51584,-6.2873 0,-4.06521 -1.51584,-6.30452 -1.49862,-2.23931 -4.23747,-2.23931 z m 0,-4.80591 q 5.58105,0 8.73332,3.54845 3.16948,3.54845 3.16948,9.80129 0,6.23563 -3.16948,9.78408 -3.15227,3.54844 -8.73332,3.54844 -5.56383,0 -8.733319,-3.54844 -3.169488,-3.54845 -3.169488,-9.78408 0,-6.25284 3.169488,-9.80129 3.169489,-3.54845 8.733319,-3.54845 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path26798"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 144.25409,147.7546 q -2.23931,1.20578 -4.65088,1.80867 -2.39434,0.60289 -4.94371,0.60289 -5.78776,0 -9.16395,-3.58289 -3.3762,-3.60013 -3.3762,-9.74963 0,-6.21839 3.42787,-9.78407 3.4451,-3.56567 9.43956,-3.56567 2.30822,0 4.42695,0.48231 2.11874,0.48232 3.99631,1.42972 v 5.32267 q -1.94648,-1.22301 -3.8585,-1.8259 -1.91203,-0.60289 -3.84128,-0.60289 -3.56568,0 -5.49493,2.22208 -1.92926,2.20487 -1.92926,6.32175 0,4.08244 1.86036,6.30453 1.86035,2.22209 5.28822,2.22209 0.93017,0 1.72255,-0.12058 0.80959,-0.13781 1.42971,-0.41341 v -4.99539 h -3.63458 v -4.44418 h 9.30176 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path26800"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="X"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8591"
|
||||
transform="translate(0,-1.0583333)">
|
||||
<path
|
||||
d="M 116.01015,174.93644 127.60979,192 h -8.97964 L 110.81495,180.57951 103.06693,192 H 94.0425 l 11.59964,-17.06356 -11.151777,-16.36937 h 9.002037 l 7.32255,10.77109 7.30015,-10.77109 h 9.04683 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:45.86111069px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path35945"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
240
plugins/SpectrumAnalyzer/y_linear.svg
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="y_linear.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-140.02362"
|
||||
inkscape:cy="188.97638"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<g
|
||||
aria-label="LOG"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';letter-spacing:0px;word-spacing:0px;fill:#8a8e96;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8595"
|
||||
transform="translate(-2.39e-4,-3.2244934)">
|
||||
<path
|
||||
d="m 77.419237,123.94899 h 5.977241 v 20.70502 h 10.47309 v 5.01262 H 77.419237 Z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path26803"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 107.01261,128.28981 q -2.72163,0 -4.23747,2.23931 -1.49862,2.23931 -1.49862,6.30452 0,4.04799 1.49862,6.2873 1.51584,2.23932 4.23747,2.23932 2.73885,0 4.23747,-2.23932 1.51584,-2.23931 1.51584,-6.2873 0,-4.06521 -1.51584,-6.30452 -1.49862,-2.23931 -4.23747,-2.23931 z m 0,-4.80591 q 5.58105,0 8.73332,3.54845 3.16948,3.54845 3.16948,9.80129 0,6.23563 -3.16948,9.78408 -3.15227,3.54844 -8.73332,3.54844 -5.56383,0 -8.733319,-3.54844 -3.169488,-3.54845 -3.169488,-9.78408 0,-6.25284 3.169488,-9.80129 3.169489,-3.54845 8.733319,-3.54845 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path26805"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 144.25409,147.7546 q -2.23931,1.20578 -4.65088,1.80867 -2.39434,0.60289 -4.94371,0.60289 -5.78776,0 -9.16395,-3.58289 -3.3762,-3.60013 -3.3762,-9.74963 0,-6.21839 3.42787,-9.78407 3.4451,-3.56567 9.43956,-3.56567 2.30822,0 4.42695,0.48231 2.11874,0.48232 3.99631,1.42972 v 5.32267 q -1.94648,-1.22301 -3.8585,-1.8259 -1.91203,-0.60289 -3.84128,-0.60289 -3.56568,0 -5.49493,2.22208 -1.92926,2.20487 -1.92926,6.32175 0,4.08244 1.86036,6.30453 1.86035,2.22209 5.28822,2.22209 0.93017,0 1.72255,-0.12058 0.80959,-0.13781 1.42971,-0.41341 v -4.99539 h -3.63458 v -4.44418 h 9.30176 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path26807"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="Y"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8591"
|
||||
transform="translate(0,-1.0583333)">
|
||||
<path
|
||||
d="m 93.796176,158.56707 h 9.427504 l 7.61366,11.91314 7.61366,-11.91314 h 9.4499 l -12.74169,19.34766 V 192 h -8.62135 v -14.08527 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:45.86111069px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path35942"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
240
plugins/SpectrumAnalyzer/y_log.svg
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="y_log.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8614"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleOutM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8612" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker8538"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="TriangleInM">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(-0.4)"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path8536" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker9923"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path9921"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8461"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(-0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8458"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="scale(0.6)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9470"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Send">
|
||||
<path
|
||||
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9468"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9424"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow2Sstart">
|
||||
<path
|
||||
transform="matrix(0.3,0,0,0.3,-0.69,0)"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="path9422"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8440"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8576"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="TriangleInL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleInL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path8567"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
|
||||
transform="scale(-0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-140.02362"
|
||||
inkscape:cy="188.97638"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-59.999998,-107)">
|
||||
<g
|
||||
aria-label="LOG"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8595"
|
||||
transform="translate(0,-3.2244934)">
|
||||
<path
|
||||
d="m 77.419237,123.94899 h 5.977241 v 20.70502 h 10.47309 v 5.01262 H 77.419237 Z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path17641"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 107.01261,128.28981 q -2.72163,0 -4.23747,2.23931 -1.49862,2.23931 -1.49862,6.30452 0,4.04799 1.49862,6.2873 1.51584,2.23932 4.23747,2.23932 2.73885,0 4.23747,-2.23932 1.51584,-2.23931 1.51584,-6.2873 0,-4.06521 -1.51584,-6.30452 -1.49862,-2.23931 -4.23747,-2.23931 z m 0,-4.80591 q 5.58105,0 8.73332,3.54845 3.16948,3.54845 3.16948,9.80129 0,6.23563 -3.16948,9.78408 -3.15227,3.54844 -8.73332,3.54844 -5.56383,0 -8.733319,-3.54844 -3.169488,-3.54845 -3.169488,-9.78408 0,-6.25284 3.169488,-9.80129 3.169489,-3.54845 8.733319,-3.54845 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path17643"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 144.25409,147.7546 q -2.23931,1.20578 -4.65088,1.80867 -2.39434,0.60289 -4.94371,0.60289 -5.78776,0 -9.16395,-3.58289 -3.3762,-3.60013 -3.3762,-9.74963 0,-6.21839 3.42787,-9.78407 3.4451,-3.56567 9.43956,-3.56567 2.30822,0 4.42695,0.48231 2.11874,0.48232 3.99631,1.42972 v 5.32267 q -1.94648,-1.22301 -3.8585,-1.8259 -1.91203,-0.60289 -3.84128,-0.60289 -3.56568,0 -5.49493,2.22208 -1.92926,2.20487 -1.92926,6.32175 0,4.08244 1.86036,6.30453 1.86035,2.22209 5.28822,2.22209 0.93017,0 1.72255,-0.12058 0.80959,-0.13781 1.42971,-0.41341 v -4.99539 h -3.63458 v -4.44418 h 9.30176 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
|
||||
id="path17645"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="Y"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text8591"
|
||||
transform="translate(0,-1.0583333)">
|
||||
<path
|
||||
d="m 93.796176,158.56707 h 9.427504 l 7.61366,11.91314 7.61366,-11.91314 h 9.4499 l -12.74169,19.34766 V 192 h -8.62135 v -14.08527 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:45.86111069px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
|
||||
id="path17638"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
@@ -2,6 +2,7 @@
|
||||
* fft_helpers.cpp - some functions around FFT analysis
|
||||
*
|
||||
* Copyright (c) 2008-2012 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2019 Martin Pavelek <he29.HS/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -28,127 +29,194 @@
|
||||
#include <cmath>
|
||||
#include "lmms_constants.h"
|
||||
|
||||
/* returns biggest value from abs_spectrum[spec_size] array
|
||||
|
||||
returns -1 on error
|
||||
*/
|
||||
float maximum(float *abs_spectrum, unsigned int spec_size)
|
||||
/* Returns biggest value from abs_spectrum[spec_size] array.
|
||||
*
|
||||
* return -1 on error, otherwise the maximum value
|
||||
*/
|
||||
float maximum(const float *abs_spectrum, unsigned int spec_size)
|
||||
{
|
||||
float maxi=0;
|
||||
float maxi = 0;
|
||||
unsigned int i;
|
||||
|
||||
if ( abs_spectrum==NULL )
|
||||
return -1;
|
||||
if (abs_spectrum == NULL) {return -1;}
|
||||
if (spec_size <= 0) {return -1;}
|
||||
|
||||
if (spec_size<=0)
|
||||
return -1;
|
||||
|
||||
for ( i=0; i<spec_size; i++ )
|
||||
for (i = 0; i < spec_size; i++)
|
||||
{
|
||||
if ( abs_spectrum[i]>maxi )
|
||||
maxi=abs_spectrum[i];
|
||||
if (abs_spectrum[i] > maxi) {maxi = abs_spectrum[i];}
|
||||
}
|
||||
|
||||
return maxi;
|
||||
}
|
||||
|
||||
float maximum(const std::vector<float> &abs_spectrum)
|
||||
{
|
||||
return maximum(abs_spectrum.data(), abs_spectrum.size());
|
||||
}
|
||||
|
||||
/* apply hanning or hamming window to channel
|
||||
|
||||
returns -1 on error */
|
||||
int hanming(float *timebuffer, int length, WINDOWS type)
|
||||
/* Normalize the array of absolute magnitudes to a 0..1 range.
|
||||
* Block size refers to FFT block size before any zero padding.
|
||||
*
|
||||
* return -1 on error, 0 on success
|
||||
*/
|
||||
int normalize(const float *abs_spectrum, float *norm_spectrum, unsigned int bin_count, unsigned int block_size)
|
||||
{
|
||||
int i;
|
||||
float alpha;
|
||||
|
||||
if ( (timebuffer==NULL)||(length<=0) )
|
||||
return -1;
|
||||
if (abs_spectrum == NULL || norm_spectrum == NULL) {return -1;}
|
||||
if (bin_count == 0 || block_size == 0) {return -1;}
|
||||
|
||||
for (i = 0; i < bin_count; i++)
|
||||
{
|
||||
norm_spectrum[i] = abs_spectrum[i] / block_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int normalize(const std::vector<float> &abs_spectrum, std::vector<float> &norm_spectrum, unsigned int block_size)
|
||||
{
|
||||
if (abs_spectrum.size() != norm_spectrum.size()) {return -1;}
|
||||
|
||||
return normalize(abs_spectrum.data(), norm_spectrum.data(), abs_spectrum.size(), block_size);
|
||||
}
|
||||
|
||||
|
||||
/* Check if the spectrum contains any non-zero value.
|
||||
*
|
||||
* return 1 if spectrum contains any non-zero value
|
||||
* return 0 otherwise
|
||||
*/
|
||||
int notEmpty(const std::vector<float> &spectrum)
|
||||
{
|
||||
for (int i = 0; i < spectrum.size(); i++)
|
||||
{
|
||||
if (spectrum[i] != 0) {return 1;}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Precompute an FFT window function for later real-time use.
|
||||
*
|
||||
* return -1 on error
|
||||
*/
|
||||
int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool normalized)
|
||||
{
|
||||
unsigned int i;
|
||||
float gain = 0;
|
||||
float a0;
|
||||
float a1;
|
||||
float a2;
|
||||
float a3;
|
||||
|
||||
if (window == NULL) {return -1;}
|
||||
|
||||
// constants taken from
|
||||
// https://en.wikipedia.org/wiki/Window_function#AList_of_window_functions
|
||||
switch (type)
|
||||
{
|
||||
case HAMMING: alpha=0.54; break;
|
||||
default:
|
||||
case RECTANGULAR:
|
||||
for (i = 0; i < length; i++) {window[i] = 1.0;}
|
||||
gain = 1;
|
||||
return 0;
|
||||
case BLACKMAN_HARRIS:
|
||||
a0 = 0.35875;
|
||||
a1 = 0.48829;
|
||||
a2 = 0.14128;
|
||||
a3 = 0.01168;
|
||||
break;
|
||||
case HAMMING:
|
||||
a0 = 0.54;
|
||||
a1 = 1.0 - a0;
|
||||
a2 = 0;
|
||||
a3 = 0;
|
||||
break;
|
||||
case HANNING:
|
||||
default: alpha=0.5; break;
|
||||
a0 = 0.5;
|
||||
a1 = 1.0 - a0;
|
||||
a2 = 0;
|
||||
a3 = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
for ( i=0; i<length; i++ )
|
||||
// common computation for cosine-sum based windows
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
timebuffer[i]=timebuffer[i]*(alpha+(1-alpha)*cos(2*F_PI*i/((float)length-1.0)));
|
||||
window[i] = (a0 - a1 * cos(2 * F_PI * i / ((float)length - 1.0))
|
||||
+ a2 * cos(4 * F_PI * i / ((float)length - 1.0))
|
||||
- a3 * cos(6 * F_PI * i / ((float)length - 1.0)));
|
||||
gain += window[i];
|
||||
}
|
||||
|
||||
// apply amplitude correction
|
||||
gain /= (float) length;
|
||||
for (i = 0; i < length; i++) {window[i] /= gain;}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* compute absolute values of complex_buffer, save to absspec_buffer
|
||||
take care that - compl_len is not bigger than complex_buffer!
|
||||
- absspec buffer is big enough!
|
||||
|
||||
returns 0 on success, else -1 */
|
||||
int absspec(fftwf_complex *complex_buffer, float *absspec_buffer, int compl_length)
|
||||
/* Compute absolute values of complex_buffer, save to absspec_buffer.
|
||||
* Take care that - compl_len is not bigger than complex_buffer!
|
||||
* - absspec buffer is big enough!
|
||||
*
|
||||
* return 0 on success, else -1
|
||||
*/
|
||||
int absspec(const fftwf_complex *complex_buffer, float *absspec_buffer, unsigned int compl_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( (complex_buffer==NULL)||(absspec_buffer==NULL) )
|
||||
return -1;
|
||||
if ( compl_length<=0 )
|
||||
return -1;
|
||||
if (complex_buffer == NULL || absspec_buffer == NULL) {return -1;}
|
||||
if (compl_length <= 0) {return -1;}
|
||||
|
||||
for (i=0; i<compl_length; i++)
|
||||
for (i = 0; i < compl_length; i++)
|
||||
{
|
||||
absspec_buffer[i]=(float )sqrt(complex_buffer[i][0]*complex_buffer[i][0] + complex_buffer[i][1]*complex_buffer[i][1]);
|
||||
absspec_buffer[i] = (float)sqrt(complex_buffer[i][0] * complex_buffer[i][0]
|
||||
+ complex_buffer[i][1] * complex_buffer[i][1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* build fewer subbands from many absolute spectrum values
|
||||
take care that - compressedbands[] array num_new elements long
|
||||
- num_old > num_new
|
||||
|
||||
returns 0 on success, else -1 */
|
||||
int compressbands(float *absspec_buffer, float *compressedband, int num_old, int num_new, int bottom, int top)
|
||||
/* Build fewer subbands from many absolute spectrum values.
|
||||
* Take care that - compressedbands[] array num_new elements long
|
||||
* - num_old > num_new
|
||||
*
|
||||
* return 0 on success, else -1
|
||||
*/
|
||||
int compressbands(const float *absspec_buffer, float *compressedband, int num_old, int num_new, int bottom, int top)
|
||||
{
|
||||
float ratio;
|
||||
int i, usefromold;
|
||||
float j;
|
||||
float j_min, j_max;
|
||||
|
||||
if ( (absspec_buffer==NULL)||(compressedband==NULL) )
|
||||
return -1;
|
||||
if (absspec_buffer == NULL || compressedband == NULL) {return -1;}
|
||||
if (num_old < num_new) {return -1;}
|
||||
if (num_old <= 0 || num_new <= 0) {return -1;}
|
||||
if (bottom < 0) {bottom = 0;}
|
||||
if (top >= num_old) {top = num_old - 1;}
|
||||
|
||||
if ( num_old<num_new )
|
||||
return -1;
|
||||
usefromold = num_old - (num_old - top) - bottom;
|
||||
|
||||
if ( (num_old<=0)||(num_new<=0) )
|
||||
return -1;
|
||||
|
||||
if ( bottom<0 )
|
||||
bottom=0;
|
||||
|
||||
if ( top>=num_old )
|
||||
top=num_old-1;
|
||||
|
||||
usefromold=num_old-(num_old-top)-bottom;
|
||||
|
||||
ratio=(float)usefromold/(float)num_new;
|
||||
ratio = (float)usefromold / (float)num_new;
|
||||
|
||||
// for each new subband
|
||||
for ( i=0; i<num_new; i++ )
|
||||
for (i = 0; i < num_new; i++)
|
||||
{
|
||||
compressedband[i]=0;
|
||||
compressedband[i] = 0;
|
||||
|
||||
j_min=(i*ratio)+bottom;
|
||||
j_min = (i * ratio) + bottom;
|
||||
|
||||
if ( j_min<0 )
|
||||
j_min=bottom;
|
||||
if (j_min < 0) {j_min = bottom;}
|
||||
|
||||
j_max=j_min+ratio;
|
||||
j_max = j_min + ratio;
|
||||
|
||||
for ( j=(int)j_min; j<=j_max; j++ )
|
||||
for (j = (int)j_min; j <= j_max; j++)
|
||||
{
|
||||
compressedband[i]+=absspec_buffer[(int)j];
|
||||
compressedband[i] += absspec_buffer[(int)j];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,84 +226,73 @@ int compressbands(float *absspec_buffer, float *compressedband, int num_old, int
|
||||
|
||||
int calc13octaveband31(float *absspec_buffer, float *subbands, int num_spec, float max_frequency)
|
||||
{
|
||||
static const int onethirdoctavecenterfr[] = {20, 25, 31, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000};
|
||||
static const int onethirdoctavecenterfr[] = {20, 25, 31, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000};
|
||||
int i, j;
|
||||
float f_min, f_max, frequency, bandwidth;
|
||||
int j_min, j_max=0;
|
||||
int j_min, j_max = 0;
|
||||
float fpower;
|
||||
|
||||
|
||||
if ( (absspec_buffer==NULL)||(subbands==NULL) )
|
||||
return -1;
|
||||
|
||||
if ( num_spec<31 )
|
||||
return -1;
|
||||
|
||||
if ( max_frequency<=0 )
|
||||
return -1;
|
||||
if (absspec_buffer == NULL || subbands == NULL) {return -1;}
|
||||
if (num_spec < 31) {return -1;}
|
||||
if (max_frequency <= 0) {return -1;}
|
||||
|
||||
/*** energy ***/
|
||||
fpower=0;
|
||||
for ( i=0; i<num_spec; i++ )
|
||||
fpower = 0;
|
||||
for (i = 0; i < num_spec; i++)
|
||||
{
|
||||
absspec_buffer[i]=(absspec_buffer[i]*absspec_buffer[i])/FFT_BUFFER_SIZE;
|
||||
fpower=fpower+(2*absspec_buffer[i]);
|
||||
absspec_buffer[i] = (absspec_buffer[i] * absspec_buffer[i]) / FFT_BUFFER_SIZE;
|
||||
fpower = fpower + (2 * absspec_buffer[i]);
|
||||
}
|
||||
fpower=fpower-(absspec_buffer[0]); //dc not mirrored
|
||||
|
||||
fpower = fpower - (absspec_buffer[0]); //dc not mirrored
|
||||
|
||||
/*** for each subband: sum up power ***/
|
||||
for ( i=0; i<31; i++ )
|
||||
for (i = 0; i < 31; i++)
|
||||
{
|
||||
subbands[i]=0;
|
||||
subbands[i] = 0;
|
||||
|
||||
// calculate bandwidth for subband
|
||||
frequency=onethirdoctavecenterfr[i];
|
||||
frequency = onethirdoctavecenterfr[i];
|
||||
|
||||
bandwidth=(pow(2, 1.0/3.0)-1)*frequency;
|
||||
bandwidth = (pow(2, 1.0/3.0)-1) * frequency;
|
||||
|
||||
f_min=frequency-bandwidth/2.0;
|
||||
f_max=frequency+bandwidth/2.0;
|
||||
f_min = frequency - bandwidth / 2.0;
|
||||
f_max = frequency + bandwidth / 2.0;
|
||||
|
||||
j_min=(int)(f_min/max_frequency*(float)num_spec);
|
||||
|
||||
j_max=(int)(f_max/max_frequency*(float)num_spec);
|
||||
j_min = (int)(f_min / max_frequency * (float)num_spec);
|
||||
j_max = (int)(f_max / max_frequency * (float)num_spec);
|
||||
|
||||
|
||||
if ( (j_min<0)||(j_max<0) )
|
||||
if (j_min < 0 || j_max < 0)
|
||||
{
|
||||
fprintf(stderr, "Error: calc13octaveband31() in fft_helpers.cpp line %d failed.\n", __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for ( j=j_min; j<=j_max; j++ )
|
||||
for (j = j_min; j <= j_max; j++)
|
||||
{
|
||||
if( j_max<num_spec )
|
||||
subbands[i]+=absspec_buffer[j];
|
||||
if (j_max < num_spec) {subbands[i] += absspec_buffer[j];}
|
||||
}
|
||||
|
||||
} //for
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* compute power of finite time sequence
|
||||
take care num_values is length of timesignal[]
|
||||
|
||||
returns power on success, else -1 */
|
||||
float signalpower(float *timesignal, int num_values)
|
||||
/* Compute power of finite time sequence.
|
||||
* Take care num_values is length of timesignal[]
|
||||
*
|
||||
* return power on success, else -1
|
||||
*/
|
||||
float signalpower(const float *timesignal, int num_values)
|
||||
{
|
||||
if ( num_values<=0 )
|
||||
return -1;
|
||||
if (num_values <= 0) {return -1;}
|
||||
|
||||
if( timesignal==NULL )
|
||||
return -1;
|
||||
if (timesignal == NULL) {return -1;}
|
||||
|
||||
float power=0;
|
||||
for ( int i=0; i<num_values; i++ )
|
||||
float power = 0;
|
||||
for (int i = 0; i < num_values; i++)
|
||||
{
|
||||
power+=timesignal[i]*timesignal[i];
|
||||
power += timesignal[i] * timesignal[i];
|
||||
}
|
||||
|
||||
return power;
|
||||
|
||||
@@ -625,6 +625,7 @@ SubWindow* MainWindow::addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags
|
||||
SubWindow *win = new SubWindow(m_workspace->viewport(), windowFlags);
|
||||
win->setAttribute(Qt::WA_DeleteOnClose);
|
||||
win->setWidget(w);
|
||||
if (w->sizeHint().isValid()) {win->resize(w->sizeHint());}
|
||||
m_workspace->addSubWindow(win);
|
||||
return win;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <QMdiArea>
|
||||
#include <QMoveEvent>
|
||||
#include <QPainter>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "embed.h"
|
||||
|
||||
@@ -97,9 +97,14 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
|
||||
if( m_controlView )
|
||||
{
|
||||
m_subWindow = gui->mainWindow()->addWindowedWidget( m_controlView );
|
||||
m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
|
||||
if (m_subWindow->layout()) {
|
||||
m_subWindow->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
if ( !m_controlView->isResizable() )
|
||||
{
|
||||
m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
|
||||
if (m_subWindow->layout())
|
||||
{
|
||||
m_subWindow->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::WindowFlags flags = m_subWindow->windowFlags();
|
||||
|
||||
@@ -134,11 +134,11 @@ QSize PixmapButton::sizeHint() const
|
||||
{
|
||||
if( ( model() != NULL && model()->value() ) || m_pressed )
|
||||
{
|
||||
return m_activePixmap.size();
|
||||
return m_activePixmap.size() / devicePixelRatio();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_inactivePixmap.size();
|
||||
return m_inactivePixmap.size() / devicePixelRatio();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||