1 /* This file is part of Clementine.
2    Copyright 2004, Enrico Ros <eros.kde@email.it>
3    Copyright 2009, David Sansome <davidsansome@gmail.com>
4    Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
5    Copyright 2014, John Maguire <john.maguire@gmail.com>
6 
7    Clementine is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    Clementine is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with Clementine.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 /* Original Author:  Enrico Ros  <eros.kde@email.it>  2004
22  */
23 
24 #include <config.h>
25 
26 #ifdef HAVE_QGLWIDGET
27 
28 #include <cmath>
29 #include <cstdlib>
30 #include "glanalyzer2.h"
31 #include <kdebug.h>
32 #include <kstandarddirs.h>
33 #include <qimage.h>
34 #include <sys/time.h>
35 
GLAnalyzer2(QWidget * parent)36 GLAnalyzer2::GLAnalyzer2(QWidget* parent) : Analyzer::Base3D(parent, 15) {
37   // initialize openGL context before managing GL calls
38   makeCurrent();
39   loadTexture(locate("data", "amarok/data/dot.png"), dotTexture);
40   loadTexture(locate("data", "amarok/data/wirl1.png"), w1Texture);
41   loadTexture(locate("data", "amarok/data/wirl2.png"), w2Texture);
42 
43   show.paused = true;
44   show.pauseTimer = 0.0;
45   show.rotDegrees = 0.0;
46   frame.rotDegrees = 0.0;
47 }
48 
~GLAnalyzer2()49 GLAnalyzer2::~GLAnalyzer2() {
50   freeTexture(dotTexture);
51   freeTexture(w1Texture);
52   freeTexture(w2Texture);
53 }
54 
initializeGL()55 void GLAnalyzer2::initializeGL() {
56   // Set a smooth shade model
57   glShadeModel(GL_SMOOTH);
58 
59   // Disable depth test (all is drawn on a 2d plane)
60   glDisable(GL_DEPTH_TEST);
61 
62   // Set blend parameters for 'composting alpha'
63   glBlendFunc(GL_SRC_ALPHA, GL_ONE);  // superpose
64   // glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );        //fade
65   glEnable(GL_BLEND);
66 
67   // Clear frame with a black background
68   glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
69   glClear(GL_COLOR_BUFFER_BIT);
70 }
71 
resizeGL(int w,int h)72 void GLAnalyzer2::resizeGL(int w, int h) {
73   // Setup screen. We're going to manually do the perspective projection
74   glViewport(0, 0, (GLint)w, (GLint)h);
75   glMatrixMode(GL_PROJECTION);
76   glLoadIdentity();
77   glOrtho(-10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f);
78 
79   // Get the aspect ratio of the screen to draw 'cicular' particles
80   float ratio = static_cast<float>(w) / static_cast<float>(h), eqPixH = 60, eqPixW = 80;
81   if (ratio >= (4.0 / 3.0)) {
82     unitX = 10.0 / (eqPixH * ratio);
83     unitY = 10.0 / eqPixH;
84   } else {
85     unitX = 10.0 / eqPixW;
86     unitY = 10.0 / (eqPixW / ratio);
87   }
88 
89   // Get current timestamp.
90   timeval tv;
91   gettimeofday(&tv, nullptr);
92   show.timeStamp = static_cast<double>(tv.tv_sec) + static_cast<double>(tv.tv_usec) / 1000000.0;
93 }
94 
paused()95 void GLAnalyzer2::paused() { analyze(Scope()); }
96 
analyze(const Scope & s)97 void GLAnalyzer2::analyze(const Scope& s) {
98   bool haveNoData = s.empty();
99 
100   // if we're going into pause mode, clear timers.
101   if (!show.paused && haveNoData) show.pauseTimer = 0.0;
102 
103   // if we have got data, interpolate it (asking myself why I'm doing it here..)
104   if (!(show.paused = haveNoData)) {
105     int bands = s.size(), lowbands = bands / 4, hibands = bands / 3,
106         midbands = bands - lowbands - hibands;
107     Q_UNUSED(midbands);
108     float currentEnergy = 0, currentMeanBand = 0, maxValue = 0;
109     for (int i = 0; i < bands; i++) {
110       float value = s[i];
111       currentEnergy += value;
112       currentMeanBand += static_cast<float>(i) * value;
113       if (value > maxValue) maxValue = value;
114     }
115     frame.silence = currentEnergy < 0.001;
116     if (!frame.silence) {
117       frame.meanBand = 100.0 * currentMeanBand / (currentEnergy * bands);
118       currentEnergy = 100.0 * currentEnergy / static_cast<float>(bands);
119       frame.dEnergy = currentEnergy - frame.energy;
120       frame.energy = currentEnergy;
121       //            printf( "%d  [%f :: %f ]\t%f \n", bands, frame.energy,
122       // frame.meanBand, maxValue         );
123     } else {
124       frame.energy = 0.0;
125     }
126   }
127 
128   // update the frame
129   updateGL();
130 }
131 
paintGL()132 void GLAnalyzer2::paintGL() {
133   // Compute the dT since the last call to paintGL and update timings
134   timeval tv;
135   gettimeofday(&tv, nullptr);
136   double currentTime = static_cast<double>(tv.tv_sec) + static_cast<double>(tv.tv_usec) / 1000000.0;
137   show.dT = currentTime - show.timeStamp;
138   show.timeStamp = currentTime;
139 
140   // Clear frame
141   glClear(GL_COLOR_BUFFER_BIT);
142 
143   // Shitch to MODEL matrix and reset it to default
144   glMatrixMode(GL_MODELVIEW);
145   glLoadIdentity();
146 
147   // Fade the previous drawings.
148   /*    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
149       glBegin( GL_TRIANGLE_STRIP );
150         glColor4f( 0.0f, 0.0f, 0.0f, 0.2f );
151         glVertex2f( 10.0f, 10.0f );
152         glVertex2f( -10.0f, 10.0f );
153         glVertex2f( 10.0f, -10.0f );
154         glVertex2f( -10.0f, -10.0f );
155       glEnd();*/
156 
157   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
158   glEnable(GL_TEXTURE_2D);
159   float alphaN = show.paused ? 0.2 : (frame.energy / 10.0),
160         alphaP = show.paused ? 1.0 : (1 - frame.energy / 20.0);
161   if (alphaN > 1.0) alphaN = 1.0;
162   if (alphaP < 0.1) alphaP = 0.1;
163   glBindTexture(GL_TEXTURE_2D, w2Texture);
164   setTextureMatrix(show.rotDegrees, 0.707 * alphaP);
165   glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
166   glBegin(GL_TRIANGLE_STRIP);
167   glTexCoord2f(1.0, 1.0);
168   glVertex2f(10.0f, 10.0f);
169   glTexCoord2f(0.0, 1.0);
170   glVertex2f(-10.0f, 10.0f);
171   glTexCoord2f(1.0, 0.0);
172   glVertex2f(10.0f, -10.0f);
173   glTexCoord2f(0.0, 0.0);
174   glVertex2f(-10.0f, -10.0f);
175   glEnd();
176   glBindTexture(GL_TEXTURE_2D, w1Texture);
177   setTextureMatrix(-show.rotDegrees * 2, 0.707);
178   glColor4f(1.0f, 1.0f, 1.0f, alphaN);
179   glBegin(GL_TRIANGLE_STRIP);
180   glTexCoord2f(1.0, 1.0);
181   glVertex2f(10.0f, 10.0f);
182   glTexCoord2f(0.0, 1.0);
183   glVertex2f(-10.0f, 10.0f);
184   glTexCoord2f(1.0, 0.0);
185   glVertex2f(10.0f, -10.0f);
186   glTexCoord2f(0.0, 0.0);
187   glVertex2f(-10.0f, -10.0f);
188   glEnd();
189   setTextureMatrix(0.0, 0.0);
190   glDisable(GL_TEXTURE_2D);
191   glBlendFunc(GL_SRC_ALPHA, GL_ONE);
192 
193   // Here begins the real draw loop
194   // some updates to the show
195   show.rotDegrees += 40.0 * show.dT;
196   frame.rotDegrees += 80.0 * show.dT;
197 
198   // handle the 'pause' status
199   if (show.paused) {
200     if (show.pauseTimer > 0.5) {
201       if (show.pauseTimer > 0.6) show.pauseTimer -= 0.6;
202       drawFullDot(0.0f, 0.4f, 0.8f, 1.0f);
203       drawFullDot(0.0f, 0.4f, 0.8f, 1.0f);
204     }
205     show.pauseTimer += show.dT;
206     return;
207   }
208 
209   if (dotTexture) {
210     glEnable(GL_TEXTURE_2D);
211     glBindTexture(GL_TEXTURE_2D, dotTexture);
212   } else {
213     glDisable(GL_TEXTURE_2D);
214   }
215 
216   glLoadIdentity();
217   //    glRotatef( -frame.rotDegrees, 0,0,1 );
218   glBegin(GL_QUADS);
219   //     Particle * particle = particleList.first();
220   //     for (; particle; particle = particleList.next())
221   {
222     glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
223     drawDot(0, 0, kMax(10.0, (10.0 * frame.energy)));
224     glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
225     drawDot(6, 0, kMax(10.0, (5.0 * frame.energy)));
226     glColor4f(0.0f, 0.4f, 1.0f, 1.0f);
227     drawDot(-6, 0, kMax(10.0, (5.0 * frame.energy)));
228   }
229   glEnd();
230 }
231 
drawDot(float x,float y,float size)232 void GLAnalyzer2::drawDot(float x, float y, float size) {
233   float sizeX = size * unitX, sizeY = size * unitY, pLeft = x - sizeX,
234         pTop = y + sizeY, pRight = x + sizeX, pBottom = y - sizeY;
235   glTexCoord2f(0, 0);  // Bottom Left
236   glVertex2f(pLeft, pBottom);
237   glTexCoord2f(0, 1);  // Top Left
238   glVertex2f(pLeft, pTop);
239   glTexCoord2f(1, 1);  // Top Right
240   glVertex2f(pRight, pTop);
241   glTexCoord2f(1, 0);  // Bottom Right
242   glVertex2f(pRight, pBottom);
243 }
244 
drawFullDot(float r,float g,float b,float a)245 void GLAnalyzer2::drawFullDot(float r, float g, float b, float a) {
246   glBindTexture(GL_TEXTURE_2D, dotTexture);
247   glEnable(GL_TEXTURE_2D);
248   glColor4f(r, g, b, a);
249   glBegin(GL_TRIANGLE_STRIP);
250   glTexCoord2f(1.0, 1.0);
251   glVertex2f(10.0f, 10.0f);
252   glTexCoord2f(0.0, 1.0);
253   glVertex2f(-10.0f, 10.0f);
254   glTexCoord2f(1.0, 0.0);
255   glVertex2f(10.0f, -10.0f);
256   glTexCoord2f(0.0, 0.0);
257   glVertex2f(-10.0f, -10.0f);
258   glEnd();
259   glDisable(GL_TEXTURE_2D);
260 }
261 
setTextureMatrix(float rot,float scale)262 void GLAnalyzer2::setTextureMatrix(float rot, float scale) {
263   glMatrixMode(GL_TEXTURE);
264   glLoadIdentity();
265   if (rot != 0.0 || scale != 0.0) {
266     glTranslatef(0.5f, 0.5f, 0.0f);
267     glRotatef(rot, 0.0f, 0.0f, 1.0f);
268     glScalef(scale, scale, 1.0f);
269     glTranslatef(-0.5f, -0.5f, 0.0f);
270   }
271   glMatrixMode(GL_MODELVIEW);
272 }
273 
loadTexture(QString fileName,GLuint & textureID)274 bool GLAnalyzer2::loadTexture(QString fileName, GLuint& textureID) {
275   // reset texture ID to the default EMPTY value
276   textureID = 0;
277 
278   // load image
279   QImage tmp;
280   if (!tmp.load(fileName)) return false;
281 
282   // convert it to suitable format (flipped RGBA)
283   QImage texture = QGLWidget::convertToGLFormat(tmp);
284   if (texture.isNull()) return false;
285 
286   // get texture number and bind loaded image to that texture
287   glGenTextures(1, &textureID);
288   glBindTexture(GL_TEXTURE_2D, textureID);
289   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
290   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
291   glTexImage2D(GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), 0,
292                GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
293   return true;
294 }
295 
freeTexture(GLuint & textureID)296 void GLAnalyzer2::freeTexture(GLuint& textureID) {
297   if (textureID > 0) glDeleteTextures(1, &textureID);
298   textureID = 0;
299 }
300 
301 #endif
302