1 /*
2  *  Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
3  *  Copyright (C) 2005-2020 Team Kodi (https://kodi.tv)
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSE.md for more information.
7  */
8 
9 /*
10  *  Wed May 24 10:49:37 CDT 2000
11  *  Fixes to threading/context creation for the nVidia X4 drivers by
12  *  Christian Zander <phoenix@minion.de>
13  */
14 
15 /*
16  *  Ported to XBMC by d4rk
17  *  Also added 'm_hSpeed' to animate transition between bar heights
18  *
19  *  Ported to GLES 2.0 by Gimli
20  */
21 
22 #define __STDC_LIMIT_MACROS
23 
24 #include <kodi/addon-instance/Visualization.h>
25 #include <kodi/gui/gl/GL.h>
26 #include <kodi/gui/gl/Shader.h>
27 
28 #include <string.h>
29 #include <math.h>
30 #include <stdint.h>
31 #include <cstddef>
32 
33 #include <glm/glm.hpp>
34 #include <glm/gtc/type_ptr.hpp>
35 
36 #ifndef M_PI
37 #define M_PI 3.141592654f
38 #endif
39 
40 #define NUM_BANDS 16
41 
42 class ATTRIBUTE_HIDDEN CVisualizationSpectrum
43   : public kodi::addon::CAddonBase,
44     public kodi::addon::CInstanceVisualization,
45     public kodi::gui::gl::CShaderProgram
46 {
47 public:
48   CVisualizationSpectrum();
49   ~CVisualizationSpectrum() override = default;
50 
51   bool Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName) override;
52   void Stop() override;
53   void Render() override;
54   void AudioData(const float* audioData, int audioDataLength, float* freqData, int freqDataLength) override;
55   ADDON_STATUS SetSetting(const std::string& settingName, const kodi::CSettingValue& settingValue) override;
56 
57   void OnCompiledAndLinked() override;
58   bool OnEnabled() override;
59 
60 private:
61   void SetBarHeightSetting(int settingValue);
62   void SetSpeedSetting(int settingValue);
63   void SetModeSetting(int settingValue);
64 
65   GLfloat m_heights[16][16];
66   GLfloat m_cHeights[16][16];
67   GLfloat m_scale;
68   GLenum m_mode;
69   float m_y_angle, m_y_speed, m_y_fixedAngle;
70   float m_x_angle, m_x_speed;
71   float m_z_angle, m_z_speed;
72   float m_hSpeed;
73 
74   void draw_bar(GLfloat x_offset, GLfloat z_offset, GLfloat height, GLfloat red, GLfloat green, GLfloat blue);
75   void draw_bars(void);
76 
77   // Shader related data
78   glm::mat4 m_projMat;
79   glm::mat4 m_modelMat;
80   GLfloat m_pointSize = 0.0f;
81   std::vector<glm::vec3> m_vertex_buffer_data;
82   std::vector<glm::vec3> m_color_buffer_data;
83 
84 #ifdef HAS_GL
85   GLuint m_vertexVBO[2] = {0};
86 #endif
87 
88   GLint m_uProjMatrix = -1;
89   GLint m_uModelMatrix = -1;
90   GLint m_uPointSize = -1;
91   GLint m_hPos = -1;
92   GLint m_hCol = -1;
93 
94   bool m_startOK = false;
95 };
96 
CVisualizationSpectrum()97 CVisualizationSpectrum::CVisualizationSpectrum()
98   : m_mode(GL_TRIANGLES),
99     m_y_angle(45.0f),
100     m_y_speed(0.5f),
101     m_x_angle(20.0f),
102     m_x_speed(0.0f),
103     m_z_angle(0.0f),
104     m_z_speed(0.0f),
105     m_hSpeed(0.05f)
106 {
107   m_scale = 1.0 / log(256.0);
108 
109   SetBarHeightSetting(kodi::GetSettingInt("bar_height"));
110   SetSpeedSetting(kodi::GetSettingInt("speed"));
111   SetModeSetting(kodi::GetSettingInt("mode"));
112   m_y_fixedAngle = kodi::GetSettingInt("rotation_angle");
113 
114   m_vertex_buffer_data.resize(48);
115   m_color_buffer_data.resize(48);
116 }
117 
Start(int channels,int samplesPerSec,int bitsPerSample,std::string songName)118 bool CVisualizationSpectrum::Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName)
119 {
120   (void)channels;
121   (void)samplesPerSec;
122   (void)bitsPerSample;
123   (void)songName;
124 
125   std::string fraqShader = kodi::GetAddonPath("resources/shaders/" GL_TYPE_STRING "/frag.glsl");
126   std::string vertShader = kodi::GetAddonPath("resources/shaders/" GL_TYPE_STRING "/vert.glsl");
127   if (!LoadShaderFiles(vertShader, fraqShader) || !CompileAndLink())
128   {
129     kodi::Log(ADDON_LOG_ERROR, "Failed to create or compile shader");
130     return false;
131   }
132 
133   int x, y;
134 
135   for(x = 0; x < 16; x++)
136   {
137     for(y = 0; y < 16; y++)
138     {
139       m_heights[y][x] = 0.0f;
140       m_cHeights[y][x] = 0.0f;
141     }
142   }
143 
144   m_x_speed = 0.0f;
145   m_y_speed = 0.5f;
146   m_z_speed = 0.0f;
147   m_x_angle = 20.0f;
148   m_y_angle = 45.0f;
149   m_z_angle = 0.0f;
150 
151   m_projMat = glm::frustum(-1.0f, 1.0f, -1.0f, 1.0f, 1.5f, 10.0f);
152 
153 #ifdef HAS_GL
154   glGenBuffers(2, m_vertexVBO);
155 #endif
156 
157   m_startOK = true;
158   return true;
159 }
160 
Stop()161 void CVisualizationSpectrum::Stop()
162 {
163   if (!m_startOK)
164     return;
165 
166   m_startOK = false;
167 
168 #ifdef HAS_GL
169   glBindBuffer(GL_ARRAY_BUFFER, 0);
170   glDeleteBuffers(2, m_vertexVBO);
171   m_vertexVBO[0] = 0;
172   m_vertexVBO[1] = 0;
173 #endif
174 }
175 
176 //-- Render -------------------------------------------------------------------
177 // Called once per frame. Do all rendering here.
178 //-----------------------------------------------------------------------------
Render()179 void CVisualizationSpectrum::Render()
180 {
181   if (!m_startOK)
182     return;
183 
184 #ifdef HAS_GL
185   glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO[0]);
186   glVertexAttribPointer(m_hPos, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, nullptr);
187   glEnableVertexAttribArray(m_hPos);
188 
189   glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO[1]);
190   glVertexAttribPointer(m_hCol, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, nullptr);
191   glEnableVertexAttribArray(m_hCol);
192 #else
193   // 1rst attribute buffer : vertices
194   glEnableVertexAttribArray(m_hPos);
195   glVertexAttribPointer(m_hPos, 3, GL_FLOAT, GL_FALSE, 0, &m_vertex_buffer_data[0]);
196 
197   // 2nd attribute buffer : colors
198   glEnableVertexAttribArray(m_hCol);
199   glVertexAttribPointer(m_hCol, 3, GL_FLOAT, GL_FALSE, 0, &m_color_buffer_data[0]);
200 #endif
201 
202   glDisable(GL_BLEND);
203 #ifdef HAS_GL
204   glEnable(GL_PROGRAM_POINT_SIZE);
205 #endif
206   glEnable(GL_DEPTH_TEST);
207   glDepthFunc(GL_LESS);
208 
209   // Clear the screen
210   glClear(GL_DEPTH_BUFFER_BIT);
211 
212   m_x_angle += m_x_speed;
213   if(m_x_angle >= 360.0f)
214     m_x_angle -= 360.0f;
215 
216   if (m_y_fixedAngle < 0.0f)
217   {
218     m_y_angle += m_y_speed;
219     if(m_y_angle >= 360.0f)
220       m_y_angle -= 360.0f;
221   }
222   else
223   {
224     m_y_angle = m_y_fixedAngle;
225   }
226 
227   m_z_angle += m_z_speed;
228   if(m_z_angle >= 360.0f)
229     m_z_angle -= 360.0f;
230 
231   m_modelMat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -0.5f, -5.0f));
232   m_modelMat = glm::rotate(m_modelMat, glm::radians(m_x_angle), glm::vec3(1.0f, 0.0f, 0.0f));
233   m_modelMat = glm::rotate(m_modelMat, glm::radians(m_y_angle), glm::vec3(0.0f, 1.0f, 0.0f));
234   m_modelMat = glm::rotate(m_modelMat, glm::radians(m_z_angle), glm::vec3(0.0f, 0.0f, 1.0f));
235 
236   EnableShader();
237 
238   draw_bars();
239 
240   DisableShader();
241 
242   glDisableVertexAttribArray(m_hPos);
243   glDisableVertexAttribArray(m_hCol);
244 
245   glDisable(GL_DEPTH_TEST);
246 #ifdef HAS_GL
247   glDisable(GL_PROGRAM_POINT_SIZE);
248 #endif
249   glEnable(GL_BLEND);
250 }
251 
OnCompiledAndLinked()252 void CVisualizationSpectrum::OnCompiledAndLinked()
253 {
254   // Variables passed directly to the Vertex shader
255   m_uProjMatrix = glGetUniformLocation(ProgramHandle(), "u_projectionMatrix");
256   m_uModelMatrix = glGetUniformLocation(ProgramHandle(), "u_modelViewMatrix");
257   m_uPointSize = glGetUniformLocation(ProgramHandle(), "u_pointSize");
258   m_hPos = glGetAttribLocation(ProgramHandle(), "a_position");
259   m_hCol = glGetAttribLocation(ProgramHandle(), "a_color");
260 }
261 
OnEnabled()262 bool CVisualizationSpectrum::OnEnabled()
263 {
264   // This is called after glUseProgram()
265   glUniformMatrix4fv(m_uProjMatrix, 1, GL_FALSE, glm::value_ptr(m_projMat));
266   glUniformMatrix4fv(m_uModelMatrix, 1, GL_FALSE, glm::value_ptr(m_modelMat));
267   glUniform1f(m_uPointSize, m_pointSize);
268 
269   return true;
270 }
271 
draw_bar(GLfloat x_offset,GLfloat z_offset,GLfloat height,GLfloat red,GLfloat green,GLfloat blue)272 void CVisualizationSpectrum::draw_bar(GLfloat x_offset, GLfloat z_offset, GLfloat height, GLfloat red, GLfloat green, GLfloat blue )
273 {
274   GLfloat width = 0.1f;
275   m_vertex_buffer_data =
276   {
277     // Bottom
278     { x_offset + width, 0.0f,   z_offset + width },
279     { x_offset,         0.0f,   z_offset },
280     { x_offset + width, 0.0f,   z_offset },
281     { x_offset + width, 0.0f,   z_offset + width },
282     { x_offset,         0.0f,   z_offset + width },
283     { x_offset,         0.0f,   z_offset },
284 
285     { x_offset,         0.0f,   z_offset + width },
286     { x_offset + width, 0.0f,   z_offset },
287     { x_offset + width, 0.0f,   z_offset + width },
288     { x_offset,         0.0f,   z_offset + width },
289     { x_offset + width, 0.0f,   z_offset },
290     { x_offset,         0.0f,   z_offset },
291 
292     // Side
293     { x_offset,         0.0f,   z_offset },
294     { x_offset,         0.0f,   z_offset + width },
295     { x_offset,         height, z_offset + width },
296     { x_offset,         0.0f,   z_offset },
297     { x_offset,         height, z_offset + width },
298     { x_offset,         height, z_offset },
299 
300     { x_offset + width, height, z_offset },
301     { x_offset,         0.0f,   z_offset },
302     { x_offset,         height, z_offset },
303     { x_offset + width, height, z_offset },
304     { x_offset + width, 0.0f,   z_offset },
305     { x_offset,         0.0f,   z_offset },
306 
307     { x_offset,         height, z_offset + width },
308     { x_offset,         0.0f,   z_offset + width },
309     { x_offset + width, 0.0f,   z_offset + width },
310     { x_offset + width, height, z_offset + width },
311     { x_offset,         height, z_offset + width },
312     { x_offset + width, 0.0f,   z_offset + width },
313 
314     { x_offset + width, height, z_offset + width },
315     { x_offset + width, 0.0f,   z_offset },
316     { x_offset + width, height, z_offset },
317     { x_offset + width, 0.0f,   z_offset },
318     { x_offset + width, height, z_offset + width },
319     { x_offset + width, 0.0f,   z_offset + width },
320 
321     // Top
322     { x_offset + width, height, z_offset + width },
323     { x_offset + width, height, z_offset },
324     { x_offset,         height, z_offset },
325     { x_offset + width, height, z_offset + width },
326     { x_offset,         height, z_offset },
327     { x_offset,         height, z_offset + width },
328 
329     { x_offset,         height, z_offset + width },
330     { x_offset + width, height, z_offset },
331     { x_offset,         height, z_offset },
332     { x_offset + width, height, z_offset },
333     { x_offset + width, height, z_offset + width },
334     { x_offset,         height, z_offset + width }
335   };
336 
337   float sideMlpy1, sideMlpy2, sideMlpy3, sideMlpy4;
338   if (m_mode == GL_TRIANGLES)
339   {
340     sideMlpy1 = 0.5f;
341     sideMlpy2 = 0.25f;
342     sideMlpy3 = 0.75f;
343     sideMlpy4 = 0.5f;
344   }
345   else
346   {
347     sideMlpy1 = sideMlpy2 = sideMlpy3 = sideMlpy4 = 1.0f;
348   }
349 
350   // One color for each vertex. They were generated randomly.
351   m_color_buffer_data =
352   {
353     // Bottom
354     { red, green, blue },
355     { red, green, blue },
356     { red, green, blue },
357     { red, green, blue },
358     { red, green, blue },
359     { red, green, blue },
360 
361     { red, green, blue },
362     { red, green, blue },
363     { red, green, blue },
364     { red, green, blue },
365     { red, green, blue },
366     { red, green, blue },
367 
368     // Side
369     { red * sideMlpy1, green * sideMlpy1, blue * sideMlpy1 },
370     { red * sideMlpy1, green * sideMlpy1, blue * sideMlpy1 },
371     { red * sideMlpy1, green * sideMlpy1, blue * sideMlpy1 },
372     { red * sideMlpy1, green * sideMlpy1, blue * sideMlpy1 },
373     { red * sideMlpy1, green * sideMlpy1, blue * sideMlpy1 },
374     { red * sideMlpy1, green * sideMlpy1, blue * sideMlpy1 },
375 
376     { red * sideMlpy2, green * sideMlpy2, blue * sideMlpy2 },
377     { red * sideMlpy2, green * sideMlpy2, blue * sideMlpy2 },
378     { red * sideMlpy2, green * sideMlpy2, blue * sideMlpy2 },
379     { red * sideMlpy2, green * sideMlpy2, blue * sideMlpy2 },
380     { red * sideMlpy2, green * sideMlpy2, blue * sideMlpy2 },
381     { red * sideMlpy2, green * sideMlpy2, blue * sideMlpy2 },
382 
383     { red * sideMlpy3, green * sideMlpy3, blue * sideMlpy3 },
384     { red * sideMlpy3, green * sideMlpy3, blue * sideMlpy3 },
385     { red * sideMlpy3, green * sideMlpy3, blue * sideMlpy3 },
386     { red * sideMlpy3, green * sideMlpy3, blue * sideMlpy3 },
387     { red * sideMlpy3, green * sideMlpy3, blue * sideMlpy3 },
388     { red * sideMlpy3, green * sideMlpy3, blue * sideMlpy3 },
389 
390     { red * sideMlpy4, green * sideMlpy4, blue * sideMlpy4 },
391     { red * sideMlpy4, green * sideMlpy4, blue * sideMlpy4 },
392     { red * sideMlpy4, green * sideMlpy4, blue * sideMlpy4 },
393     { red * sideMlpy4, green * sideMlpy4, blue * sideMlpy4 },
394     { red * sideMlpy4, green * sideMlpy4, blue * sideMlpy4 },
395     { red * sideMlpy4, green * sideMlpy4, blue * sideMlpy4 },
396 
397     // Top
398     { red, green, blue },
399     { red, green, blue },
400     { red, green, blue },
401     { red, green, blue },
402     { red, green, blue },
403     { red, green, blue },
404 
405     { red, green, blue },
406     { red, green, blue },
407     { red, green, blue },
408     { red, green, blue },
409     { red, green, blue },
410     { red, green, blue },
411   };
412 
413 #ifdef HAS_GL
414   glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO[0]);
415   glBufferData(GL_ARRAY_BUFFER, m_vertex_buffer_data.size()*sizeof(glm::vec3), &m_vertex_buffer_data[0], GL_STATIC_DRAW);
416   glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO[1]);
417   glBufferData(GL_ARRAY_BUFFER, m_color_buffer_data.size()*sizeof(glm::vec3), &m_color_buffer_data[0], GL_STATIC_DRAW);
418 #endif
419   glDrawArrays(m_mode, 0, m_vertex_buffer_data.size()); /* 12*3 indices starting at 0 -> 12 triangles + 4*3 to have on lines show correct */
420 }
421 
draw_bars(void)422 void CVisualizationSpectrum::draw_bars(void)
423 {
424   int x, y;
425   GLfloat x_offset, z_offset, r_base, b_base;
426 
427   for(y = 0; y < 16; y++)
428   {
429     z_offset = -1.6 + ((15 - y) * 0.2);
430 
431     b_base = y * (1.0 / 15);
432     r_base = 1.0 - b_base;
433 
434     for(x = 0; x < 16; x++)
435     {
436       x_offset = -1.6 + ((float)x * 0.2);
437       if (::fabs(m_cHeights[y][x]-m_heights[y][x])>m_hSpeed)
438       {
439         if (m_cHeights[y][x]<m_heights[y][x])
440           m_cHeights[y][x] += m_hSpeed;
441         else
442           m_cHeights[y][x] -= m_hSpeed;
443       }
444       draw_bar(x_offset, z_offset, m_cHeights[y][x], r_base - (float(x) * (r_base / 15.0)), (float)x * (1.0 / 15), b_base);
445     }
446   }
447 }
448 
AudioData(const float * pAudioData,int iAudioDataLength,float * pFreqData,int iFreqDataLength)449 void CVisualizationSpectrum::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
450 {
451   int i,c;
452   int y=0;
453   GLfloat val;
454 
455   int xscale[] = {0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 187, 255};
456 
457   for(y = 15; y > 0; y--)
458   {
459     for(i = 0; i < 16; i++)
460     {
461       m_heights[y][i] = m_heights[y - 1][i];
462     }
463   }
464 
465   for(i = 0; i < NUM_BANDS; i++)
466   {
467     for(c = xscale[i], y = 0; c < xscale[i + 1]; c++)
468     {
469       if (c<iAudioDataLength)
470       {
471         if((int)(pAudioData[c] * (INT16_MAX)) > y)
472           y = (int)(pAudioData[c] * (INT16_MAX));
473       }
474       else
475         continue;
476     }
477     y >>= 7;
478     if(y > 0)
479       val = (logf(y) * m_scale);
480     else
481       val = 0;
482     m_heights[0][i] = val;
483   }
484 }
485 
SetBarHeightSetting(int settingValue)486 void CVisualizationSpectrum::SetBarHeightSetting(int settingValue)
487 {
488   switch (settingValue)
489   {
490   case 1://standard
491     m_scale = 1.f / log(256.f);
492     break;
493 
494   case 2://big
495     m_scale = 2.f / log(256.f);
496     break;
497 
498   case 3://real big
499     m_scale = 3.f / log(256.f);
500     break;
501 
502   case 4://unused
503     m_scale = 0.33f / log(256.f);
504     break;
505 
506   case 0://small
507   default:
508     m_scale = 0.5f / log(256.f);
509     break;
510   }
511 }
512 
SetSpeedSetting(int settingValue)513 void CVisualizationSpectrum::SetSpeedSetting(int settingValue)
514 {
515   switch (settingValue)
516   {
517   case 1:
518     m_hSpeed = 0.025f;
519     break;
520 
521   case 2:
522     m_hSpeed = 0.0125f;
523     break;
524 
525   case 3:
526     m_hSpeed = 0.1f;
527     break;
528 
529   case 4:
530     m_hSpeed = 0.2f;
531     break;
532 
533   case 0:
534   default:
535     m_hSpeed = 0.05f;
536     break;
537   }
538 }
539 
SetModeSetting(int settingValue)540 void CVisualizationSpectrum::SetModeSetting(int settingValue)
541 {
542   switch (settingValue)
543   {
544     case 1:
545       m_mode = GL_LINES;
546       m_pointSize = 0.0f;
547       break;
548 
549     case 2:
550       m_mode = GL_POINTS;
551       m_pointSize = kodi::GetSettingInt("pointsize");
552       break;
553 
554     case 0:
555     default:
556       m_mode = GL_TRIANGLES;
557       m_pointSize = 0.0f;
558       break;
559   }
560 }
561 
562 //-- SetSetting ---------------------------------------------------------------
563 // Set a specific Setting value (called from Kodi)
564 // !!! Add-on master function !!!
565 //-----------------------------------------------------------------------------
SetSetting(const std::string & settingName,const kodi::CSettingValue & settingValue)566 ADDON_STATUS CVisualizationSpectrum::SetSetting(const std::string& settingName, const kodi::CSettingValue& settingValue)
567 {
568   if (settingName.empty() || settingValue.empty())
569     return ADDON_STATUS_UNKNOWN;
570 
571   if (settingName == "bar_height")
572   {
573     SetBarHeightSetting(settingValue.GetInt());
574     return ADDON_STATUS_OK;
575   }
576   else if (settingName == "speed")
577   {
578     SetSpeedSetting(settingValue.GetInt());
579     return ADDON_STATUS_OK;
580   }
581   else if (settingName == "mode")
582   {
583     SetModeSetting(settingValue.GetInt());
584     return ADDON_STATUS_OK;
585   }
586   else if (settingName == "rotation_angle")
587   {
588     m_y_fixedAngle = settingValue.GetInt();
589     return ADDON_STATUS_OK;
590   }
591 
592   return ADDON_STATUS_UNKNOWN;
593 }
594 
595 ADDONCREATOR(CVisualizationSpectrum)
596