2011-06-23 01:25:08 +02:00
|
|
|
/* This file is part of Clementine.
|
2014-11-29 20:07:01 +01:00
|
|
|
Copyright 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
|
|
|
|
Copyright 2011-2012, 2014, David Sansome <me@davidsansome.com>
|
|
|
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
|
|
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
2011-06-23 01:25:08 +02:00
|
|
|
|
|
|
|
Clementine 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 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nyancatanalyzer.h"
|
|
|
|
|
2011-06-23 02:22:56 +02:00
|
|
|
#include <cmath>
|
2011-06-23 01:25:08 +02:00
|
|
|
|
|
|
|
#include <QTimerEvent>
|
2011-06-23 15:21:08 +02:00
|
|
|
#include <QBrush>
|
2011-06-23 01:25:08 +02:00
|
|
|
|
2014-04-29 14:11:52 +02:00
|
|
|
#include "core/arraysize.h"
|
|
|
|
#include "core/logging.h"
|
|
|
|
|
2014-05-13 14:46:22 +02:00
|
|
|
using Analyzer::Scope;
|
|
|
|
|
2011-07-20 21:33:11 +02:00
|
|
|
const char* NyanCatAnalyzer::kName = "Nyanalyzer cat";
|
2011-06-23 17:14:39 +02:00
|
|
|
const float NyanCatAnalyzer::kPixelScale = 0.02f;
|
2011-06-23 01:25:08 +02:00
|
|
|
|
|
|
|
NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
2014-02-07 16:34:20 +01:00
|
|
|
: Analyzer::Base(parent, 9),
|
|
|
|
cat_(":/nyancat.png"),
|
|
|
|
timer_id_(startTimer(kFrameIntervalMs)),
|
|
|
|
frame_(0),
|
|
|
|
current_buffer_(0),
|
|
|
|
available_rainbow_width_(0),
|
|
|
|
px_per_frame_(0),
|
|
|
|
x_offset_(0),
|
|
|
|
background_brush_(QColor(0x0f, 0x43, 0x73)) {
|
2014-05-23 13:31:50 +02:00
|
|
|
memset(history_, 0, sizeof(history_));
|
2011-06-23 01:25:08 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < kRainbowBands; ++i) {
|
2011-06-23 22:36:38 +02:00
|
|
|
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255),
|
2014-02-07 16:34:20 +01:00
|
|
|
kCatHeight / kRainbowBands, Qt::SolidLine, Qt::FlatCap,
|
|
|
|
Qt::RoundJoin);
|
2011-06-23 22:36:30 +02:00
|
|
|
|
|
|
|
// pow constants computed so that
|
|
|
|
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
|
2014-02-07 16:34:20 +01:00
|
|
|
band_scale_[i] =
|
|
|
|
-std::cos(M_PI * i / (kRainbowBands - 1)) * 0.5 * std::pow(2.3, i);
|
2011-06-23 01:25:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add "Psychedelic Colour" mode to all analyzers
(Well, except Nyanalyzer and Rainbow dash because they are already colourful enough.)
I have added functionality for any 2D analyzer to change any part of its colour palatte with the frequency content of the music, in the same way that Moodbars do.
I find this gives the analyzer a sort of "third dimention".
This is built into Analyzer::Base, so all analyzers can use it and override it as they please. I have thus added support for Block, Boom, Turbine, Sonogram, and Bar, however Boom and Block seem to look the best in my opinion.
This is of course all optional and is toggled by a checkbox in the context menu for the analyzer, disabled by default.
I have not been able to measure any increase in CPU activity with this enabled, even at 60fps.
2015-07-01 17:48:03 +02:00
|
|
|
void NyanCatAnalyzer::transform(Scope& s) { fht_->spectrum(&s.front()); }
|
2011-06-23 01:25:08 +02:00
|
|
|
|
|
|
|
void NyanCatAnalyzer::timerEvent(QTimerEvent* e) {
|
|
|
|
if (e->timerId() == timer_id_) {
|
|
|
|
frame_ = (frame_ + 1) % kCatFrameCount;
|
|
|
|
} else {
|
|
|
|
Analyzer::Base::timerEvent(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-23 22:36:38 +02:00
|
|
|
void NyanCatAnalyzer::resizeEvent(QResizeEvent* e) {
|
|
|
|
// Invalidate the buffer so it's recreated from scratch in the next paint
|
|
|
|
// event.
|
2011-12-01 18:29:27 +01:00
|
|
|
buffer_[0] = QPixmap();
|
|
|
|
buffer_[1] = QPixmap();
|
2011-06-24 00:49:33 +02:00
|
|
|
|
|
|
|
available_rainbow_width_ = width() - kCatWidth + kRainbowOverlap;
|
Add "Psychedelic Colour" mode to all analyzers
(Well, except Nyanalyzer and Rainbow dash because they are already colourful enough.)
I have added functionality for any 2D analyzer to change any part of its colour palatte with the frequency content of the music, in the same way that Moodbars do.
I find this gives the analyzer a sort of "third dimention".
This is built into Analyzer::Base, so all analyzers can use it and override it as they please. I have thus added support for Block, Boom, Turbine, Sonogram, and Bar, however Boom and Block seem to look the best in my opinion.
This is of course all optional and is toggled by a checkbox in the context menu for the analyzer, disabled by default.
I have not been able to measure any increase in CPU activity with this enabled, even at 60fps.
2015-07-01 17:48:03 +02:00
|
|
|
px_per_frame_ =
|
|
|
|
static_cast<float>(available_rainbow_width_) / (kHistorySize - 1) + 1;
|
2014-02-07 16:34:20 +01:00
|
|
|
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
|
2011-06-23 22:36:38 +02:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
|
|
|
bool new_frame) {
|
2011-06-23 01:25:08 +02:00
|
|
|
// Discard the second half of the transform
|
|
|
|
const int scope_size = s.size() / 2;
|
|
|
|
|
2012-10-16 12:20:56 +02:00
|
|
|
if ((new_frame && is_playing_) ||
|
|
|
|
(buffer_[0].isNull() && buffer_[1].isNull())) {
|
2011-06-23 22:36:14 +02:00
|
|
|
// Transform the music into rainbows!
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int band = 0; band < kRainbowBands; ++band) {
|
2011-06-23 22:36:14 +02:00
|
|
|
float* band_start = history_ + band * kHistorySize;
|
2011-06-23 01:25:08 +02:00
|
|
|
|
2011-06-23 22:36:14 +02:00
|
|
|
// Move the history of each band across by 1 frame.
|
|
|
|
memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float));
|
|
|
|
}
|
2011-06-23 01:25:08 +02:00
|
|
|
|
2011-06-23 22:36:14 +02:00
|
|
|
// Now accumulate the scope data into each band. Should maybe use a series
|
|
|
|
// of band pass filters for this, so bands can leak into neighbouring bands,
|
|
|
|
// but for now it's a series of separate square filters.
|
|
|
|
const int samples_per_band = scope_size / kRainbowBands;
|
|
|
|
int sample = 0;
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int band = 0; band < kRainbowBands; ++band) {
|
2011-06-23 22:36:30 +02:00
|
|
|
float accumulator = 0.0;
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < samples_per_band; ++i) {
|
2011-06-23 22:36:30 +02:00
|
|
|
accumulator += s[sample++];
|
2011-06-23 22:36:14 +02:00
|
|
|
}
|
2011-06-23 22:36:30 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
history_[(band + 1) * kHistorySize - 1] = accumulator * band_scale_[band];
|
2011-06-23 01:25:08 +02:00
|
|
|
}
|
|
|
|
|
2011-06-24 00:49:33 +02:00
|
|
|
// Create polylines for the rainbows.
|
|
|
|
QPointF polyline[kRainbowBands * kHistorySize];
|
|
|
|
QPointF* dest = polyline;
|
|
|
|
float* source = history_;
|
|
|
|
|
Add "Psychedelic Colour" mode to all analyzers
(Well, except Nyanalyzer and Rainbow dash because they are already colourful enough.)
I have added functionality for any 2D analyzer to change any part of its colour palatte with the frequency content of the music, in the same way that Moodbars do.
I find this gives the analyzer a sort of "third dimention".
This is built into Analyzer::Base, so all analyzers can use it and override it as they please. I have thus added support for Block, Boom, Turbine, Sonogram, and Bar, however Boom and Block seem to look the best in my opinion.
This is of course all optional and is toggled by a checkbox in the context menu for the analyzer, disabled by default.
I have not been able to measure any increase in CPU activity with this enabled, even at 60fps.
2015-07-01 17:48:03 +02:00
|
|
|
const float top_of_cat =
|
|
|
|
static_cast<float>(height()) / 2 - static_cast<float>(kCatHeight) / 2;
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int band = 0; band < kRainbowBands; ++band) {
|
2011-06-24 00:49:33 +02:00
|
|
|
// Calculate the Y position of this band.
|
2014-02-07 16:34:20 +01:00
|
|
|
const float y =
|
Add "Psychedelic Colour" mode to all analyzers
(Well, except Nyanalyzer and Rainbow dash because they are already colourful enough.)
I have added functionality for any 2D analyzer to change any part of its colour palatte with the frequency content of the music, in the same way that Moodbars do.
I find this gives the analyzer a sort of "third dimention".
This is built into Analyzer::Base, so all analyzers can use it and override it as they please. I have thus added support for Block, Boom, Turbine, Sonogram, and Bar, however Boom and Block seem to look the best in my opinion.
This is of course all optional and is toggled by a checkbox in the context menu for the analyzer, disabled by default.
I have not been able to measure any increase in CPU activity with this enabled, even at 60fps.
2015-07-01 17:48:03 +02:00
|
|
|
static_cast<float>(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) +
|
|
|
|
top_of_cat;
|
2011-06-24 00:49:33 +02:00
|
|
|
|
|
|
|
// Add each point in the line.
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int x = 0; x < kHistorySize; ++x) {
|
|
|
|
*dest = QPointF(px_per_frame_ * x, y + *source * kPixelScale);
|
|
|
|
++dest;
|
|
|
|
++source;
|
2011-06-24 00:49:33 +02:00
|
|
|
}
|
2011-06-23 01:25:08 +02:00
|
|
|
}
|
|
|
|
|
2011-06-24 00:49:33 +02:00
|
|
|
// Do we have to draw the whole rainbow into the buffer?
|
2011-12-01 18:29:27 +01:00
|
|
|
if (buffer_[0].isNull()) {
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < 2; ++i) {
|
2011-12-01 18:29:27 +01:00
|
|
|
buffer_[i] = QPixmap(QSize(width() + x_offset_, height()));
|
|
|
|
buffer_[i].fill(background_brush_.color());
|
|
|
|
}
|
|
|
|
current_buffer_ = 0;
|
2011-06-23 15:21:08 +02:00
|
|
|
|
2011-12-01 18:29:27 +01:00
|
|
|
QPainter buffer_painter(&buffer_[0]);
|
2011-06-24 00:49:33 +02:00
|
|
|
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
2011-06-24 00:49:33 +02:00
|
|
|
buffer_painter.setPen(colors_[band]);
|
2014-02-07 16:34:20 +01:00
|
|
|
buffer_painter.drawPolyline(&polyline[band * kHistorySize],
|
|
|
|
kHistorySize);
|
|
|
|
buffer_painter.drawPolyline(&polyline[band * kHistorySize],
|
|
|
|
kHistorySize);
|
2011-06-24 00:49:33 +02:00
|
|
|
}
|
|
|
|
} else {
|
2011-12-01 18:29:27 +01:00
|
|
|
const int last_buffer = current_buffer_;
|
|
|
|
current_buffer_ = (current_buffer_ + 1) % 2;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
// We can just shuffle the buffer along a bit and draw the new frame's
|
|
|
|
// data.
|
2011-12-01 18:29:27 +01:00
|
|
|
QPainter buffer_painter(&buffer_[current_buffer_]);
|
2011-06-24 00:49:33 +02:00
|
|
|
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
buffer_painter.drawPixmap(
|
|
|
|
0, 0, buffer_[last_buffer], px_per_frame_, 0,
|
|
|
|
x_offset_ + available_rainbow_width_ - px_per_frame_, 0);
|
|
|
|
buffer_painter.fillRect(
|
|
|
|
x_offset_ + available_rainbow_width_ - px_per_frame_, 0,
|
|
|
|
kCatWidth - kRainbowOverlap + px_per_frame_, height(),
|
|
|
|
background_brush_);
|
2011-06-24 00:49:33 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
2011-06-24 00:49:33 +02:00
|
|
|
buffer_painter.setPen(colors_[band]);
|
2014-02-07 16:34:20 +01:00
|
|
|
buffer_painter.drawPolyline(&polyline[(band + 1) * kHistorySize - 3],
|
|
|
|
3);
|
2011-06-24 00:49:33 +02:00
|
|
|
}
|
2011-06-23 22:36:38 +02:00
|
|
|
}
|
2011-06-23 01:25:08 +02:00
|
|
|
}
|
|
|
|
|
2011-06-23 22:36:38 +02:00
|
|
|
// Draw the buffer on to the widget
|
2011-12-01 18:29:27 +01:00
|
|
|
p.drawPixmap(0, 0, buffer_[current_buffer_], x_offset_, 0, 0, 0);
|
2011-06-23 22:36:38 +02:00
|
|
|
|
2011-06-24 00:49:33 +02:00
|
|
|
// Draw nyan cat (he's been waiting for this for 75 lines).
|
2011-06-23 01:25:08 +02:00
|
|
|
// Nyan nyan nyan nyan.
|
2012-10-16 12:20:56 +02:00
|
|
|
if (!is_playing_) {
|
|
|
|
// Ssshhh!
|
|
|
|
p.drawPixmap(SleepingCatDestRect(), cat_, SleepingCatSourceRect());
|
|
|
|
} else {
|
|
|
|
p.drawPixmap(CatDestRect(), cat_, CatSourceRect());
|
|
|
|
}
|
2011-06-23 01:25:08 +02:00
|
|
|
}
|