diff --git a/data/data.qrc b/data/data.qrc
index abb6d1323..c3adf5c45 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -342,6 +342,7 @@
providers/subsonic.png
providers/ubuntuone.png
providers/wikipedia.png
+ rainbowdash.png
sample.mood
schema/device-schema.sql
schema/jamendo.sql
diff --git a/data/rainbowdash.png b/data/rainbowdash.png
new file mode 100644
index 000000000..82a9f8ec2
Binary files /dev/null and b/data/rainbowdash.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 93fa03387..913154385 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -75,6 +75,7 @@ set(SOURCES
analyzers/blockanalyzer.cpp
analyzers/boomanalyzer.cpp
analyzers/nyancatanalyzer.cpp
+ analyzers/rainbowdashanalyzer.cpp
analyzers/sonogram.cpp
analyzers/turbine.cpp
@@ -407,6 +408,7 @@ set(HEADERS
analyzers/blockanalyzer.h
analyzers/boomanalyzer.h
analyzers/nyancatanalyzer.h
+ analyzers/rainbowdashanalyzer.h
analyzers/sonogram.h
analyzers/turbine.h
diff --git a/src/analyzers/analyzercontainer.cpp b/src/analyzers/analyzercontainer.cpp
index c7da4c9e9..7dfed8b83 100644
--- a/src/analyzers/analyzercontainer.cpp
+++ b/src/analyzers/analyzercontainer.cpp
@@ -20,6 +20,7 @@
#include "blockanalyzer.h"
#include "boomanalyzer.h"
#include "nyancatanalyzer.h"
+#include "rainbowdashanalyzer.h"
#include "sonogram.h"
#include "turbine.h"
#include "core/logging.h"
@@ -74,6 +75,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
AddAnalyzerType();
AddAnalyzerType();
AddAnalyzerType();
+ AddAnalyzerType();
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
disable_action_ = context_menu_->addAction(tr("No analyzer"), this,
diff --git a/src/analyzers/rainbowdashanalyzer.cpp b/src/analyzers/rainbowdashanalyzer.cpp
new file mode 100644
index 000000000..52d90ba0c
--- /dev/null
+++ b/src/analyzers/rainbowdashanalyzer.cpp
@@ -0,0 +1,177 @@
+/* This file is part of Clementine.
+ Copyright 2010, David Sansome
+
+ 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 .
+*/
+
+#include "rainbowdashanalyzer.h"
+
+#include
+
+#include
+#include
+
+#include "core/arraysize.h"
+#include "core/logging.h"
+
+using Analyzer::Scope;
+
+const char* RainbowDashAnalyzer::kName = "Rainbow Dash";
+const float RainbowDashAnalyzer::kPixelScale = 0.02f;
+
+RainbowDashAnalyzer::RainbowDashAnalyzer(QWidget* parent)
+ : Analyzer::Base(parent, 9),
+ dash_(":/rainbowdash.png"),
+ timer_id_(startTimer(kFrameIntervalMs)),
+ frame_(0),
+ current_buffer_(0),
+ available_rainbow_width_(0),
+ px_per_frame_(0),
+ x_offset_(0),
+ background_brush_(QColor(0x38, 0x88, 0x00)) {
+ memset(history_, 0, arraysize(history_) * sizeof(*history_));
+
+ for (int i = 0; i < kRainbowBands; ++i) {
+ colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255),
+ kRainbowHeight / kRainbowBands, Qt::SolidLine,
+ Qt::FlatCap, Qt::RoundJoin);
+
+ // pow constants computed so that
+ // | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
+ band_scale_[i] =
+ -std::cos(M_PI * i / (kRainbowBands - 1)) * 0.5 * std::pow(2.3, i);
+ }
+}
+
+void RainbowDashAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
+
+void RainbowDashAnalyzer::timerEvent(QTimerEvent* e) {
+ if (e->timerId() == timer_id_) {
+ frame_ = (frame_ + 1) % kDashFrameCount;
+ } else {
+ Analyzer::Base::timerEvent(e);
+ }
+}
+
+void RainbowDashAnalyzer::resizeEvent(QResizeEvent* e) {
+ // Invalidate the buffer so it's recreated from scratch in the next paint
+ // event.
+ buffer_[0] = QPixmap();
+ buffer_[1] = QPixmap();
+
+ available_rainbow_width_ = width() - kDashWidth + kRainbowOverlap;
+ px_per_frame_ = float(available_rainbow_width_) / (kHistorySize - 1) + 1;
+ x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
+}
+
+void RainbowDashAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
+ bool new_frame) {
+ // Discard the second half of the transform
+ const int scope_size = s.size() / 2;
+
+ if ((new_frame && is_playing_) ||
+ (buffer_[0].isNull() && buffer_[1].isNull())) {
+ // Transform the music into rainbows!
+ for (int band = 0; band < kRainbowBands; ++band) {
+ float* band_start = history_ + band * kHistorySize;
+
+ // Move the history of each band across by 1 frame.
+ memmove(band_start, band_start + 1, (kHistorySize - 1) * sizeof(float));
+ }
+
+ // 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;
+ for (int band = 0; band < kRainbowBands; ++band) {
+ float accumulator = 0.0;
+ for (int i = 0; i < samples_per_band; ++i) {
+ accumulator += s[sample++];
+ }
+ history_[(band + 1) * kHistorySize - 1] = accumulator * band_scale_[band];
+ }
+
+ // Create polylines for the rainbows.
+ QPointF polyline[kRainbowBands * kHistorySize];
+ QPointF* dest = polyline;
+ float* source = history_;
+
+ const float top_of_Dash = float(height()) / 2 - float(kRainbowHeight) / 2;
+ for (int band = 0; band < kRainbowBands; ++band) {
+ // Calculate the Y position of this band.
+ const float y =
+ float(kRainbowHeight) / (kRainbowBands + 1) * (band + 0.5) +
+ top_of_Dash;
+
+ // Add each point in the line.
+ for (int x = 0; x < kHistorySize; ++x) {
+ *dest = QPointF(px_per_frame_ * x, y + *source * kPixelScale);
+ ++dest;
+ ++source;
+ }
+ }
+
+ // Do we have to draw the whole rainbow into the buffer?
+ if (buffer_[0].isNull()) {
+ for (int i = 0; i < 2; ++i) {
+ buffer_[i] = QPixmap(QSize(width() + x_offset_, height()));
+ buffer_[i].fill(background_brush_.color());
+ }
+ current_buffer_ = 0;
+
+ QPainter buffer_painter(&buffer_[0]);
+ buffer_painter.setRenderHint(QPainter::Antialiasing);
+ for (int band = kRainbowBands - 1; band >= 0; --band) {
+ buffer_painter.setPen(colors_[band]);
+ buffer_painter.drawPolyline(&polyline[band * kHistorySize],
+ kHistorySize);
+ buffer_painter.drawPolyline(&polyline[band * kHistorySize],
+ kHistorySize);
+ }
+ } else {
+ const int last_buffer = current_buffer_;
+ current_buffer_ = (current_buffer_ + 1) % 2;
+
+ // We can just shuffle the buffer along a bit and draw the new frame's
+ // data.
+ QPainter buffer_painter(&buffer_[current_buffer_]);
+ buffer_painter.setRenderHint(QPainter::Antialiasing);
+
+ 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,
+ kDashWidth - kRainbowOverlap + px_per_frame_, height(),
+ background_brush_);
+
+ for (int band = kRainbowBands - 1; band >= 0; --band) {
+ buffer_painter.setPen(colors_[band]);
+ buffer_painter.drawPolyline(&polyline[(band + 1) * kHistorySize - 3],
+ 3);
+ }
+ }
+ }
+
+ // Draw the buffer on to the widget
+ p.drawPixmap(0, 0, buffer_[current_buffer_], x_offset_, 0, 0, 0);
+
+ if (!is_playing_) {
+ // Ssshhh!
+ p.drawPixmap(SleepingDashDestRect(), dash_, SleepingDashSourceRect());
+ } else {
+ p.drawPixmap(DashDestRect(), dash_, DashSourceRect());
+ }
+}
diff --git a/src/analyzers/rainbowdashanalyzer.h b/src/analyzers/rainbowdashanalyzer.h
new file mode 100644
index 000000000..bc69d091b
--- /dev/null
+++ b/src/analyzers/rainbowdashanalyzer.h
@@ -0,0 +1,106 @@
+/* This file is part of Clementine.
+ Copyright 2010, David Sansome
+
+ 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 .
+*/
+
+#ifndef RAINBOWDASHANALYZER_H
+#define RAINBOWDASHANALYZER_H
+
+#include "analyzerbase.h"
+
+#include
+
+class RainbowDashAnalyzer : public Analyzer::Base {
+ Q_OBJECT
+
+ public:
+ Q_INVOKABLE RainbowDashAnalyzer(QWidget* parent);
+
+ static const char* kName;
+
+ protected:
+ void transform(Analyzer::Scope&);
+ void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
+
+ void timerEvent(QTimerEvent* e);
+ void resizeEvent(QResizeEvent* e);
+
+ private:
+ static const int kDashHeight = 30;
+ static const int kDashWidth = 54;
+ static const int kRainbowHeight = 16;
+ static const int kDashFrameCount = 1;
+ static const int kRainbowOverlap = 15;
+ static const int kSleepingDashHeight = 30;
+
+ static const int kHistorySize = 128;
+ static const int kRainbowBands = 6;
+ static const float kPixelScale;
+
+ static const int kFrameIntervalMs = 150;
+
+ private:
+ inline QRect DashSourceRect() const {
+ return QRect(0, kDashHeight * frame_, kDashWidth, kDashHeight);
+ }
+
+ inline QRect SleepingDashSourceRect() const {
+ return QRect(0, kDashHeight * kDashFrameCount, kDashWidth,
+ kSleepingDashHeight);
+ }
+
+ inline QRect DashDestRect() const {
+ return QRect(width() - kDashWidth, (height() - kDashHeight) / 2, kDashWidth,
+ kDashHeight);
+ }
+
+ inline QRect SleepingDashDestRect() const {
+ return QRect(width() - kDashWidth, (height() - kSleepingDashHeight) / 2,
+ kDashWidth, kSleepingDashHeight);
+ }
+
+ private:
+ // "constants" that get initialised in the constructor
+ float band_scale_[kRainbowBands];
+ QPen colors_[kRainbowBands];
+
+ QPixmap dash_;
+
+ // For the animation
+ int timer_id_;
+ int frame_;
+
+ // The y positions of each point on the rainbow.
+ float history_[kHistorySize * kRainbowBands];
+
+ // A cache of the last frame's rainbow, so it can be used in the next frame.
+ QPixmap buffer_[2];
+ int current_buffer_;
+
+ // Geometry information that's updated on resize:
+ // The width of the widget minus the space for the pony
+ int available_rainbow_width_;
+
+ // X spacing between each point in the polyline.
+ int px_per_frame_;
+
+ // Amount the buffer_ is shifted to the left (off the edge of the widget) to
+ // make the rainbow extend from 0 to available_rainbow_width_.
+ int x_offset_;
+
+ QBrush background_brush_;
+};
+
+#endif // RAINBOWDASHANALYZER_H