/*************************************************************************** * Copyright (C) 2007-2017 by Ilya Kotov * * forkotov02@ya.ru * * * * 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; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "fft.h" #include "inlines.h" #include "analyzer.h" Analyzer::Analyzer (QWidget *parent) : Visual (parent) { m_intern_vis_data = 0; m_peaks = 0; m_x_scale = 0; m_rows = 0; m_cols = 0; m_update = false; m_running = false; setWindowTitle (tr("Qmmp Analyzer")); setMinimumSize(2*300-30,105); m_timer = new QTimer (this); connect(m_timer, SIGNAL (timeout()), this, SLOT (timeout())); clear(); createMenu(); readSettings(); } Analyzer::~Analyzer() { if(m_peaks) delete [] m_peaks; if(m_intern_vis_data) delete [] m_intern_vis_data; if(m_x_scale) delete [] m_x_scale; } void Analyzer::start() { m_running = true; if(isVisible()) m_timer->start(); } void Analyzer::stop() { m_running = false; m_timer->stop(); clear(); } void Analyzer::clear() { m_rows = 0; m_cols = 0; update(); } void Analyzer::timeout() { if(takeData(m_left_buffer, m_right_buffer)) { process(); update(); } } void Analyzer::toggleFullScreen() { setWindowState(windowState() ^Qt::WindowFullScreen); } void Analyzer::readSettings() { QSettings settings(Qmmp::configFile(), QSettings::IniFormat); settings.beginGroup("Analyzer"); m_peaks_falloff = settings.value("peak_falloff", 0.2).toDouble(); m_analyzer_falloff = settings.value("analyzer_falloff", 2.2).toDouble(); m_show_peaks = settings.value("show_peaks", true).toBool(); m_timer->setInterval(1000 / settings.value("refresh_rate", 25).toInt()); m_color1.setNamedColor(settings.value("color1", "Green").toString()); m_color2.setNamedColor(settings.value("color2", "Yellow").toString()); m_color3.setNamedColor(settings.value("color3", "Red").toString()); m_bgColor.setNamedColor(settings.value("bg_color", "Black").toString()); m_peakColor.setNamedColor(settings.value("peak_color", "Cyan").toString()); m_cell_size = settings.value("cells_size", QSize(15, 6)).toSize(); if(!m_update) { m_update = true; m_peaksAction->setChecked(m_show_peaks); foreach(QAction *act, m_fpsGroup->actions ()) { if (m_timer->interval() == 1000 / act->data().toInt()) act->setChecked(true); } foreach(QAction *act, m_peaksFalloffGroup->actions ()) { if (m_peaks_falloff == act->data().toDouble()) act->setChecked(true); } foreach(QAction *act, m_analyzerFalloffGroup->actions ()) { if (m_analyzer_falloff == act->data().toDouble()) act->setChecked(true); } //fallback if(!m_fpsGroup->checkedAction()) { m_fpsGroup->actions().at(1)->setChecked(true); m_timer->setInterval(1000 / 25); } if(!m_peaksFalloffGroup->checkedAction()) { m_peaksFalloffGroup->actions().at(1)->setChecked(2); m_peaks_falloff = 0.2; } if(!m_peaksFalloffGroup->checkedAction()) { m_peaksFalloffGroup->actions().at(1)->setChecked(2); m_analyzer_falloff = 2.2; } restoreGeometry(settings.value("geometry").toByteArray()); } } void Analyzer::writeSettings() { QSettings settings(Qmmp::configFile(), QSettings::IniFormat); settings.beginGroup("Analyzer"); QAction *act = m_fpsGroup->checkedAction (); settings.setValue("refresh_rate", act ? act->data().toInt() : 25); act = m_peaksFalloffGroup->checkedAction (); settings.setValue("peak_falloff", act ? act->data().toDouble() : 0.2); act = m_analyzerFalloffGroup->checkedAction (); settings.setValue("analyzer_falloff", act ? act->data().toDouble() : 2.2); settings.setValue("show_peaks", m_peaksAction->isChecked()); settings.endGroup(); } void Analyzer::hideEvent (QHideEvent *) { m_timer->stop(); } void Analyzer::showEvent (QShowEvent *) { if(m_running) m_timer->start(); } void Analyzer::closeEvent (QCloseEvent *event) { //save geometry QSettings settings(Qmmp::configFile(), QSettings::IniFormat); settings.setValue("Analyzer/geometry", saveGeometry()); Visual::closeEvent(event); //removes visualization object } void Analyzer::paintEvent (QPaintEvent * e) { QPainter painter (this); painter.fillRect(e->rect(),m_bgColor); draw(&painter); } void Analyzer::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::RightButton) m_menu->exec(e->globalPos()); } void Analyzer::process() { static fft_state *state = 0; if (!state) state = fft_init(); int rows = (height() - 2) / m_cell_size.height(); int cols = (width() - 2) / m_cell_size.width() / 2; if(m_rows != rows || m_cols != cols) { m_rows = rows; m_cols = cols; if(m_peaks) delete [] m_peaks; if(m_intern_vis_data) delete [] m_intern_vis_data; if(m_x_scale) delete [] m_x_scale; m_peaks = new double[m_cols * 2]; m_intern_vis_data = new double[m_cols * 2]; m_x_scale = new int[m_cols + 1]; for(int i = 0; i < m_cols * 2; ++i) { m_peaks[i] = 0; m_intern_vis_data[i] = 0; } for(int i = 0; i < m_cols + 1; ++i) m_x_scale[i] = pow(pow(255.0, 1.0 / m_cols), i); } short dest_l[256]; short dest_r[256]; short yl, yr; int j, k, magnitude_l, magnitude_r; calc_freq (dest_l, m_left_buffer); calc_freq (dest_r, m_right_buffer); double y_scale = (double) 1.25 * m_rows / log(256); for (int i = 0; i < m_cols; i++) { j = m_cols * 2 - i - 1; //mirror index yl = yr = 0; magnitude_l = magnitude_r = 0; if(m_x_scale[i] == m_x_scale[i + 1]) { yl = dest_l[i]; yr = dest_r[i]; } for (k = m_x_scale[i]; k < m_x_scale[i + 1]; k++) { yl = qMax(dest_l[k], yl); yr = qMax(dest_r[k], yr); } yl >>= 7; //256 yr >>= 7; if (yl) { magnitude_l = int(log (yl) * y_scale); magnitude_l = qBound(0, magnitude_l, m_rows); } if (yr) { magnitude_r = int(log (yr) * y_scale); magnitude_r = qBound(0, magnitude_r, m_rows); } m_intern_vis_data[i] -= m_analyzer_falloff * m_rows / 15; m_intern_vis_data[i] = magnitude_l > m_intern_vis_data[i] ? magnitude_l : m_intern_vis_data[i]; m_intern_vis_data[j] -= m_analyzer_falloff * m_rows / 15; m_intern_vis_data[j] = magnitude_r > m_intern_vis_data[j] ? magnitude_r : m_intern_vis_data[j]; if (m_show_peaks) { m_peaks[i] -= m_peaks_falloff * m_rows / 15; m_peaks[i] = magnitude_l > m_peaks[i] ? magnitude_l : m_peaks[i]; m_peaks[j] -= m_peaks_falloff * m_rows / 15; m_peaks[j] = magnitude_r > m_peaks[j] ? magnitude_r : m_peaks[j]; } } } void Analyzer::draw(QPainter *p) { QBrush brush(Qt::SolidPattern); int x = 0; int rdx = qMax(0, width() - 2 * m_cell_size.width() * m_cols); for (int j = 0; j < m_cols * 2; ++j) { x = j * m_cell_size.width() + 1; if(j >= m_cols) x += rdx; //correct right part position for (int i = 0; i <= m_intern_vis_data[j]; ++i) { if (i <= m_rows/3) brush.setColor(m_color1); else if (i > m_rows/3 && i <= 2 * m_rows / 3) brush.setColor(m_color2); else brush.setColor(m_color3); p->fillRect (x, height() - i * m_cell_size.height() + 1, m_cell_size.width() - 2, m_cell_size.height() - 2, brush); } if (m_show_peaks) { p->fillRect (x, height() - int(m_peaks[j])*m_cell_size.height() + 1, m_cell_size.width() - 2, m_cell_size.height() - 2, m_peakColor); } } } void Analyzer::createMenu() { m_menu = new QMenu (this); connect(m_menu, SIGNAL(triggered (QAction *)),SLOT(writeSettings())); connect(m_menu, SIGNAL(triggered (QAction *)),SLOT(readSettings())); m_peaksAction = m_menu->addAction(tr("Peaks")); m_peaksAction->setCheckable(true); QMenu *refreshRate = m_menu->addMenu(tr("Refresh Rate")); m_fpsGroup = new QActionGroup(this); m_fpsGroup->setExclusive(true); m_fpsGroup->addAction(tr("50 fps"))->setData(50); m_fpsGroup->addAction(tr("25 fps"))->setData(25); m_fpsGroup->addAction(tr("10 fps"))->setData(10); m_fpsGroup->addAction(tr("5 fps"))->setData(5); foreach(QAction *act, m_fpsGroup->actions ()) { act->setCheckable(true); refreshRate->addAction(act); } QMenu *analyzerFalloff = m_menu->addMenu(tr("Analyzer Falloff")); m_analyzerFalloffGroup = new QActionGroup(this); m_analyzerFalloffGroup->setExclusive(true); m_analyzerFalloffGroup->addAction(tr("Slowest"))->setData(1.2); m_analyzerFalloffGroup->addAction(tr("Slow"))->setData(1.8); m_analyzerFalloffGroup->addAction(tr("Medium"))->setData(2.2); m_analyzerFalloffGroup->addAction(tr("Fast"))->setData(2.4); m_analyzerFalloffGroup->addAction(tr("Fastest"))->setData(2.8); foreach(QAction *act, m_analyzerFalloffGroup->actions ()) { act->setCheckable(true); analyzerFalloff->addAction(act); } QMenu *peaksFalloff = m_menu->addMenu(tr("Peaks Falloff")); m_peaksFalloffGroup = new QActionGroup(this); m_peaksFalloffGroup->setExclusive(true); m_peaksFalloffGroup->addAction(tr("Slowest"))->setData(0.05); m_peaksFalloffGroup->addAction(tr("Slow"))->setData(0.1); m_peaksFalloffGroup->addAction(tr("Medium"))->setData(0.2); m_peaksFalloffGroup->addAction(tr("Fast"))->setData(0.4); m_peaksFalloffGroup->addAction(tr("Fastest"))->setData(0.8); foreach(QAction *act, m_peaksFalloffGroup->actions ()) { act->setCheckable(true); peaksFalloff->addAction(act); } m_menu->addSeparator(); QAction *fullScreenAction = m_menu->addAction(tr("&Full Screen"), this, SLOT(toggleFullScreen()), tr("F")); addAction(fullScreenAction); update(); }