Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / visualizations / XBMCProjectM / Main.cpp
1 /*
2  *      Copyright (C) 2007-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 /*
22 xmms-projectM v0.99 - xmms-projectm.sourceforge.net
23 --------------------------------------------------
24
25 Lead Developers:  Carmelo Piccione (cep@andrew.cmu.edu) &
26                   Peter Sperl (peter@sperl.com)
27
28 We have also been advised by some professors at CMU, namely Roger B. Dannenberg.
29 http://www-2.cs.cmu.edu/~rbd/
30
31 The inspiration for this program was Milkdrop by Ryan Geiss. Obviously.
32
33 This code is distributed under the GPL.
34
35
36 THANKS FOR THE CODE!!!
37 -------------------------------------------------
38 The base for this program was andy@nobugs.org's XMMS plugin tutorial
39 http://www.xmms.org/docs/vis-plugin.html
40
41 We used some FFT code by Takuya OOURA instead of XMMS' built-in fft code
42 fftsg.c - http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html
43
44 For font rendering we used GLF by Roman Podobedov
45 glf.c - http://astronomy.swin.edu.au/~pbourke/opengl/glf/
46
47 and some beat detection code was inspired by Frederic Patin @
48 www.gamedev.net/reference/programming/features/beatdetection/
49 --
50
51 "ported" to XBMC by d4rk
52 d4rk@xbmc.org
53
54 */
55
56 #include "addons/include/xbmc_vis_dll.h"
57 #include "addons/include/xbmc_addon_cpp_dll.h"
58 #include <GL/glew.h>
59 #include "libprojectM/projectM.hpp"
60 #include <string>
61 #include <utils/log.h>
62
63 projectM *globalPM = NULL;
64
65 // some projectm globals
66 int maxSamples=512;
67 int texsize=512;
68 int gx=40,gy=30;
69 int fps=100;
70 char *disp;
71 char g_visName[512];
72 char **g_presets=NULL;
73 unsigned int g_numPresets = 0;
74 projectM::Settings g_configPM;
75
76 bool g_UserPackFolder;
77 char lastPresetDir[1024];
78 bool lastLockStatus;
79 int lastPresetIdx;
80 unsigned int lastLoggedPresetIdx;
81
82 //-- Create -------------------------------------------------------------------
83 // Called once when the visualisation is created by XBMC. Do any setup here.
84 //-----------------------------------------------------------------------------
85 extern "C" ADDON_STATUS ADDON_Create(void* hdl, void* props)
86 {
87   if (!props)
88     return ADDON_STATUS_UNKNOWN;
89
90   VIS_PROPS* visprops = (VIS_PROPS*)props;
91
92   strcpy(g_visName, visprops->name);
93   g_configPM.meshX = gx;
94   g_configPM.meshY = gy;
95   g_configPM.fps = fps;
96   g_configPM.textureSize = texsize;
97   g_configPM.windowWidth = visprops->width;
98   g_configPM.windowHeight = visprops->height;
99   g_configPM.aspectCorrection = true;
100   g_configPM.easterEgg = 0.0;
101   g_configPM.windowLeft = visprops->x;
102   g_configPM.windowBottom = visprops->y;
103   lastLoggedPresetIdx = lastPresetIdx;
104
105   return ADDON_STATUS_NEED_SAVEDSETTINGS;
106 }
107
108 //-- Start --------------------------------------------------------------------
109 // Called when a new soundtrack is played
110 //-----------------------------------------------------------------------------
111 extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName)
112 {
113   //printf("Got Start Command\n");
114 }
115
116 //-- Audiodata ----------------------------------------------------------------
117 // Called by XBMC to pass new audio data to the vis
118 //-----------------------------------------------------------------------------
119 extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
120 {
121   if (globalPM)
122     globalPM->pcm()->addPCMfloat(pAudioData, iAudioDataLength);
123 }
124
125 //-- Render -------------------------------------------------------------------
126 // Called once per frame. Do all rendering here.
127 //-----------------------------------------------------------------------------
128 extern "C" void Render()
129 {
130   if (globalPM)
131   {
132     globalPM->renderFrame();
133     if (g_presets)
134     {
135       unsigned preset;
136       globalPM->selectedPresetIndex(preset);
137       if (lastLoggedPresetIdx != preset)
138         CLog::Log(LOGDEBUG,"PROJECTM - Changed preset to: %s",g_presets[preset]);
139       lastLoggedPresetIdx = preset;
140     }
141   }
142 }
143
144 //-- GetInfo ------------------------------------------------------------------
145 // Tell XBMC our requirements
146 //-----------------------------------------------------------------------------
147 extern "C" void GetInfo(VIS_INFO* pInfo)
148 {
149   pInfo->bWantsFreq = false;
150   pInfo->iSyncDelay = 0;
151 }
152
153 //-- OnAction -----------------------------------------------------------------
154 // Handle XBMC actions such as next preset, lock preset, album art changed etc
155 //-----------------------------------------------------------------------------
156 extern "C" bool OnAction(long flags, const void *param)
157 {
158   bool ret = false;
159
160   if (!globalPM)
161     return false;
162
163   if (flags == VIS_ACTION_LOAD_PRESET && param)
164   {
165     int pindex = *((int *)param);
166     globalPM->selectPreset(pindex);
167     ret = true;
168   }
169   else if (flags == VIS_ACTION_NEXT_PRESET)
170   {
171 //    switchPreset(ALPHA_NEXT, SOFT_CUT);
172     if (!globalPM->isShuffleEnabled())
173       globalPM->key_handler(PROJECTM_KEYDOWN, PROJECTM_K_n, PROJECTM_KMOD_CAPS); //ignore PROJECTM_KMOD_CAPS
174     else
175       globalPM->key_handler(PROJECTM_KEYDOWN, PROJECTM_K_r, PROJECTM_KMOD_CAPS); //ignore PROJECTM_KMOD_CAPS
176     ret = true;
177   }
178   else if (flags == VIS_ACTION_PREV_PRESET)
179   {
180 //    switchPreset(ALPHA_PREVIOUS, SOFT_CUT);
181     if (!globalPM->isShuffleEnabled())
182       globalPM->key_handler(PROJECTM_KEYDOWN, PROJECTM_K_p, PROJECTM_KMOD_CAPS); //ignore PROJECTM_KMOD_CAPS
183     else
184       globalPM->key_handler(PROJECTM_KEYDOWN, PROJECTM_K_r, PROJECTM_KMOD_CAPS); //ignore PROJECTM_KMOD_CAPS
185
186     ret = true;
187   }
188   else if (flags == VIS_ACTION_RANDOM_PRESET)
189   {
190     globalPM->setShuffleEnabled(g_configPM.shuffleEnabled);
191     ret = true;
192   }
193   else if (flags == VIS_ACTION_LOCK_PRESET)
194   {
195     globalPM->setPresetLock(!globalPM->isPresetLocked());
196     unsigned preset;
197     globalPM->selectedPresetIndex(preset);
198     globalPM->selectPreset(preset);
199     ret = true;
200   }
201   return ret;
202 }
203
204 //-- GetPresets ---------------------------------------------------------------
205 // Return a list of presets to XBMC for display
206 //-----------------------------------------------------------------------------
207 extern "C" unsigned int GetPresets(char ***presets)
208 {
209   g_numPresets = globalPM ? globalPM->getPlaylistSize() : 0;
210   if (g_numPresets > 0)
211   {
212     g_presets = (char**) malloc(sizeof(char*)*g_numPresets);
213     for (unsigned i = 0; i < g_numPresets; i++)
214     {
215       g_presets[i] = (char*) malloc(strlen(globalPM->getPresetName(i).c_str())+2);
216       if (g_presets[i])
217         strcpy(g_presets[i], globalPM->getPresetName(i).c_str());
218     }
219     *presets = g_presets;
220   }
221   return g_numPresets;
222 }
223
224 //-- GetPreset ----------------------------------------------------------------
225 // Return the index of the current playing preset
226 //-----------------------------------------------------------------------------
227 extern "C" unsigned GetPreset()
228 {
229   if (g_presets)
230   {
231     unsigned preset;
232     if(globalPM && globalPM->selectedPresetIndex(preset))
233       return preset;
234   }
235   return 0;
236 }
237
238 //-- IsLocked -----------------------------------------------------------------
239 // Returns true if this add-on use settings
240 //-----------------------------------------------------------------------------
241 extern "C" bool IsLocked()
242 {
243   if(globalPM)
244     return globalPM->isPresetLocked();
245   else
246     return false;
247 }
248
249 //-- Stop ---------------------------------------------------------------------
250 // Do everything before unload of this add-on
251 // !!! Add-on master function !!!
252 //-----------------------------------------------------------------------------
253 extern "C" void ADDON_Stop()
254 {
255   if (globalPM)
256   {
257     delete globalPM;
258     globalPM = NULL;
259   }
260   if (g_presets)
261   {
262     for (unsigned i = 0; i <g_numPresets; i++)
263     {
264       free(g_presets[i]);
265     }
266     free(g_presets);
267     g_presets = NULL;
268   }
269   g_numPresets = 0;
270 }
271
272 //-- Destroy-------------------------------------------------------------------
273 // Do everything before unload of this add-on
274 // !!! Add-on master function !!!
275 //-----------------------------------------------------------------------------
276 extern "C" void ADDON_Destroy()
277 {
278 }
279
280 //-- HasSettings --------------------------------------------------------------
281 // Returns true if this add-on use settings
282 // !!! Add-on master function !!!
283 //-----------------------------------------------------------------------------
284 extern "C" bool ADDON_HasSettings()
285 {
286   return true;
287 }
288
289 //-- GetStatus ---------------------------------------------------------------
290 // Returns the current Status of this visualisation
291 // !!! Add-on master function !!!
292 //-----------------------------------------------------------------------------
293 extern "C" ADDON_STATUS ADDON_GetStatus()
294 {
295   return ADDON_STATUS_OK;
296 }
297
298 //-- GetSettings --------------------------------------------------------------
299 // Return the settings for XBMC to display
300 //-----------------------------------------------------------------------------
301
302 extern "C" unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
303 {
304   return 0;
305 }
306
307 //-- FreeSettings --------------------------------------------------------------
308 // Free the settings struct passed from XBMC
309 //-----------------------------------------------------------------------------
310
311 extern "C" void ADDON_FreeSettings()
312 {
313 }
314
315 void ChooseQuality (int pvalue)
316 {
317   switch (pvalue)
318   {
319     case 0:
320       g_configPM.textureSize = 256;
321       break;
322     case 1:
323       g_configPM.textureSize = 512;
324       break;
325     case 2:
326       g_configPM.textureSize = 1024;
327       break;
328     case 3:
329       g_configPM.textureSize = 2048;
330       break;
331   }
332 }
333
334 void ChoosePresetPack(int pvalue)
335 {
336   g_UserPackFolder = false;
337   if (pvalue == 0)
338     g_configPM.presetURL = "zip://special%3A%2F%2Fxbmc%2Faddons%2Fvisualization%2Eprojectm%2Fresources%2Fpresets%2Ezip";
339   else if (pvalue == 1) //User preset folder has been chosen
340     g_UserPackFolder = true;
341 }
342
343 void ChooseUserPresetFolder(std::string pvalue)
344 {
345   if (g_UserPackFolder)
346   {
347     pvalue.erase(pvalue.length()-1,1);  //Remove "/" from the end
348     g_configPM.presetURL = pvalue;
349   }
350 }
351
352 bool InitProjectM()
353 {
354   if (globalPM) delete globalPM; //We are re-initalizing the engine
355   try
356   {
357     globalPM = new projectM(g_configPM);
358     if (g_configPM.presetURL == lastPresetDir)  //If it is not the first run AND if this is the same preset pack as last time
359     {
360       globalPM->setPresetLock(lastLockStatus);
361       globalPM->selectPreset(lastPresetIdx);
362     }
363     else
364     {
365       //If it is the first run or a newly chosen preset pack we choose a random preset as first
366       if (globalPM->getPlaylistSize())
367         globalPM->selectPreset((rand() % (globalPM->getPlaylistSize())));
368     }
369     return true;
370   }
371   catch (...)
372   {
373     printf("exception in projectM ctor");
374     return false;
375   }
376 }
377
378 //-- UpdateSetting ------------------------------------------------------------
379 // Handle setting change request from XBMC
380 //-----------------------------------------------------------------------------
381 extern "C" ADDON_STATUS ADDON_SetSetting(const char* id, const void* value)
382 {
383   if (!id || !value)
384     return ADDON_STATUS_UNKNOWN;
385
386   if (strcmp(id, "###GetSavedSettings") == 0) // We have some settings to be saved in the settings.xml file
387   {
388     if (!globalPM)
389     {
390       return ADDON_STATUS_UNKNOWN;
391     }
392     if (strcmp((char*)value, "0") == 0)
393     {
394       strcpy((char*)id, "lastpresetfolder");
395       strcpy((char*)value, globalPM->settings().presetURL.c_str());
396     }
397     if (strcmp((char*)value, "1") == 0)
398     {
399       strcpy((char*)id, "lastlockedstatus");
400       strcpy((char*)value, (globalPM->isPresetLocked() ? "true" : "false"));
401     }
402     if (strcmp((char*)value, "2") == 0)
403     {
404       strcpy((char*)id, "lastpresetidx");
405       unsigned int lastindex;
406       globalPM->selectedPresetIndex(lastindex);
407       sprintf ((char*)value, "%i", (int)lastindex);
408     }
409     if (strcmp((char*)value, "3") == 0)
410     {
411       strcpy((char*)id, "###End");
412     }
413     return ADDON_STATUS_OK;
414   }
415   // It is now time to set the settings got from xbmc
416   if (strcmp(id, "quality")==0)
417     ChooseQuality (*(int*)value);
418   else if (strcmp(id, "shuffle")==0)
419     g_configPM.shuffleEnabled = *(bool*)value;
420   
421   else if (strcmp(id, "lastpresetidx")==0)
422     lastPresetIdx = *(int*)value;
423   else if (strcmp(id, "lastlockedstatus")==0)
424     lastLockStatus = *(bool*)value;
425   else if (strcmp(id, "lastpresetfolder")==0)
426     strcpy(lastPresetDir, (char*)value);
427   
428   else if (strcmp(id, "smooth_duration")==0)
429     g_configPM.smoothPresetDuration = (*(int*)value * 5 + 5);
430   else if (strcmp(id, "preset_duration")==0)
431     g_configPM.presetDuration = (*(int*)value * 5 + 5);
432   else if (strcmp(id, "preset pack")==0)
433     ChoosePresetPack(*(int*)value);
434   else if (strcmp(id, "user preset folder") == 0)
435     ChooseUserPresetFolder((char*)value);
436   else if (strcmp(id, "beat_sens")==0)
437   {
438     g_configPM.beatSensitivity = *(int*)value * 2;
439     if (!InitProjectM())    //The last setting value is already set so we (re)initalize
440       return ADDON_STATUS_UNKNOWN;
441   }
442   return ADDON_STATUS_OK;
443 }
444
445 //-- GetSubModules ------------------------------------------------------------
446 // Return any sub modules supported by this vis
447 //-----------------------------------------------------------------------------
448 extern "C" unsigned int GetSubModules(char ***names)
449 {
450   return 0; // this vis supports 0 sub modules
451 }
452
453 //-- Announce -----------------------------------------------------------------
454 // Receive announcements from XBMC
455 //-----------------------------------------------------------------------------
456 extern "C" void ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data)
457 {
458 }