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