blob: 2a7fb1b5a39bc6ff4c7bf2fac8401ef442ba8327 [file] [log] [blame]
/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2004 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
#include "RenderItemMatcher.hpp"
#include "RenderItemMergeFunction.hpp"
#include "fatal.h"
#include "Common.hpp"
#ifdef WIN32
#include "win32-dirent.h"
#endif
#include "timer.h"
#include <iostream>
#ifdef LINUX
#include "time.h"
#endif
#ifdef WIN32
#include <time.h>
#endif
#include "PipelineContext.hpp"
#include <iostream>
#include "projectM.hpp"
#include "BeatDetect.hpp"
#include "Preset.hpp"
#include "PipelineMerger.hpp"
#include "PCM.hpp" //Sound data handler (buffering, FFT, etc.)
#include <map>
#include "Renderer.hpp"
#include "PresetChooser.hpp"
#include "ConfigFile.h"
#include "TextureManager.hpp"
#include "TimeKeeper.hpp"
#include "RenderItemMergeFunction.hpp"
#ifdef USE_THREADS
#include "pthread.h"
pthread_mutex_t mutex;
pthread_cond_t condition;
pthread_t thread;
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_t preset_mutex;
#endif
#endif
projectM::~projectM()
{
#ifdef USE_THREADS
std::cout << "[projectM] thread ";
printf ("c");
running = false;
printf ("l");
pthread_cond_signal (&condition);
printf ("e");
pthread_mutex_unlock (&mutex);
printf ("a");
pthread_detach (thread);
printf ("n");
pthread_cond_destroy (&condition);
printf ("u");
pthread_mutex_destroy (&mutex);
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_destroy (&preset_mutex);
#endif
printf ("p");
std::cout << std::endl;
#endif
destroyPresetTools();
if (renderer)
delete (renderer);
if (beatDetect)
delete (beatDetect);
if (_pcm) {
delete (_pcm);
_pcm = 0;
}
delete (_pipelineContext);
delete (_pipelineContext2);
}
unsigned
projectM::initRenderToTexture()
{
return renderer->initRenderToTexture();
}
void
projectM::projectM_resetTextures()
{
renderer->ResetTextures();
}
projectM::projectM (std::string config_file, int flags)
: beatDetect (0),
renderer (0),
_pcm (0),
m_presetPos (0),
m_flags (flags),
_pipelineContext (new PipelineContext()),
_pipelineContext2 (new PipelineContext())
{
readConfig (config_file);
projectM_reset();
projectM_resetGL (_settings.windowWidth, _settings.windowHeight);
}
projectM::projectM (Settings settings, int flags)
: beatDetect (0),
renderer (0),
_pcm (0),
m_presetPos (0),
m_flags (flags),
_pipelineContext (new PipelineContext()),
_pipelineContext2 (new PipelineContext())
{
readSettings (settings);
projectM_reset();
projectM_resetGL (_settings.windowWidth, _settings.windowHeight);
}
bool
projectM::writeConfig (
const std::string & configFile,
const Settings & settings)
{
ConfigFile config (configFile);
config.add ("Mesh X", settings.meshX);
config.add ("Mesh Y", settings.meshY);
config.add ("Texture Size", settings.textureSize);
config.add ("FPS", settings.fps);
config.add ("Window Width", settings.windowWidth);
config.add ("Window Height", settings.windowHeight);
config.add ("Smooth Preset Duration", settings.smoothPresetDuration);
config.add ("Preset Duration", settings.presetDuration);
config.add ("Preset Path", settings.presetURL);
config.add ("Title Font", settings.titleFontURL);
config.add ("Menu Font", settings.menuFontURL);
config.add ("Hard Cut Sensitivity", settings.beatSensitivity);
config.add ("Aspect Correction", settings.aspectCorrection);
config.add ("Easter Egg Parameter", settings.easterEgg);
config.add ("Shuffle Enabled", settings.shuffleEnabled);
config.add ("Soft Cut Ratings Enabled", settings.softCutRatingsEnabled);
std::fstream file (configFile.c_str());
if (file) {
file << config;
return true;
} else
return false;
}
void
projectM::readConfig (const std::string & configFile)
{
std::cout << "[projectM] config file: " << configFile << std::endl;
ConfigFile config (configFile);
_settings.meshX = config.read<int> ("Mesh X", 32);
_settings.meshY = config.read<int> ("Mesh Y", 24);
_settings.textureSize = config.read<int> ("Texture Size", 512);
_settings.fps = config.read<int> ("FPS", 35);
_settings.windowWidth = config.read<int> ("Window Width", 512);
_settings.windowHeight = config.read<int> ("Window Height", 512);
_settings.smoothPresetDuration =
config.read<int> ("Smooth Preset Duration",
config.read<int> ("Smooth Transition Duration", 10));
_settings.presetDuration = config.read<int> ("Preset Duration", 15);
#ifdef LINUX
_settings.presetURL = config.read<string> ("Preset Path", CMAKE_INSTALL_PREFIX "/share/projectM/presets");
#endif
#ifdef __APPLE__
/// @bug awful hardcoded hack- need to add intelligence to cmake wrt bundling - carm
_settings.presetURL = config.read<string> ("Preset Path", "../Resources/presets");
#endif
#ifdef WIN32
_settings.presetURL = config.read<string> ("Preset Path", CMAKE_INSTALL_PREFIX "/share/projectM/presets");
#endif
#ifdef __APPLE__
_settings.titleFontURL = config.read<string>
("Title Font", "../Resources/fonts/Vera.tff");
_settings.menuFontURL = config.read<string>
("Menu Font", "../Resources/fonts/VeraMono.ttf");
#endif
#ifdef LINUX
_settings.titleFontURL = config.read<string>
("Title Font", projectM_FONT_TITLE);
_settings.menuFontURL = config.read<string>
("Menu Font", projectM_FONT_MENU);
#endif
#ifdef WIN32
_settings.titleFontURL = config.read<string>
("Title Font", projectM_FONT_TITLE);
_settings.menuFontURL = config.read<string>
("Menu Font", projectM_FONT_MENU);
#endif
_settings.shuffleEnabled = config.read<bool> ("Shuffle Enabled", true);
_settings.easterEgg = config.read<float> ("Easter Egg Parameter", 0.0);
_settings.softCutRatingsEnabled = config.read<float> ("Soft Cut Ratings Enabled", false);
projectM_init (_settings.meshX, _settings.meshY,
_settings.fps,
_settings.textureSize,
_settings.windowWidth, _settings.windowHeight);
_settings.beatSensitivity =
beatDetect->beat_sensitivity = config.read<float> ("Hard Cut Sensitivity", 10.0);
if (config.read ("Aspect Correction", true))
{
_settings.aspectCorrection = true;
renderer->correction = true;
}
else
{
_settings.aspectCorrection = false;
renderer->correction = false;
}
}
void
projectM::readSettings (const Settings & settings)
{
_settings.meshX = settings.meshX;
_settings.meshY = settings.meshY;
_settings.textureSize = settings.textureSize;
_settings.fps = settings.fps;
_settings.windowWidth = settings.windowWidth;
_settings.windowHeight = settings.windowHeight;
_settings.smoothPresetDuration = settings.smoothPresetDuration;
_settings.presetDuration = settings.presetDuration;
_settings.softCutRatingsEnabled = settings.softCutRatingsEnabled;
_settings.presetURL = settings.presetURL;
_settings.titleFontURL = settings.titleFontURL;
_settings.menuFontURL = settings.menuFontURL;
_settings.shuffleEnabled = settings.shuffleEnabled;
_settings.easterEgg = settings.easterEgg;
projectM_init (_settings.meshX, _settings.meshY,
_settings.fps,
_settings.textureSize,
_settings.windowWidth, _settings.windowHeight);
_settings.beatSensitivity = settings.beatSensitivity;
_settings.aspectCorrection = settings.aspectCorrection;
}
#ifdef USE_THREADS
static void
*thread_callback (void *prjm)
{
projectM *p = (projectM *)prjm;
p->thread_func (prjm);
return NULL;
}
void *
projectM::thread_func (void *vptr_args)
{
pthread_mutex_lock (&mutex);
// printf ("in thread: %f\n", timeKeeper->PresetProgressB());
while (true)
{
pthread_cond_wait (&condition, &mutex);
if (!running)
{
pthread_mutex_unlock (&mutex);
return NULL;
}
evaluateSecondPreset();
}
}
#endif /* USE_THREADS */
void
projectM::evaluateSecondPreset()
{
pipelineContext2().time = timeKeeper->GetRunningTime();
pipelineContext2().frame = timeKeeper->PresetFrameB();
pipelineContext2().progress = timeKeeper->PresetProgressB();
m_activePreset2->Render (*beatDetect, pipelineContext2());
}
void
projectM::renderFrame()
{
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_lock (&preset_mutex);
#endif
#ifdef DEBUG
char fname[1024];
FILE *f = NULL;
int index = 0;
int x, y;
#endif
timeKeeper->UpdateTimers();
/*
if (timeKeeper->IsSmoothing())
{
printf ("Smoothing A:%f, B:%f, S:%f\n",
timeKeeper->PresetProgressA(),
timeKeeper->PresetProgressB(),
timeKeeper->SmoothRatio());
}
else
{
printf (" A:%f\n", timeKeeper->PresetProgressA());
}*/
mspf = (int) (1000.0 / (float) settings().fps); //milliseconds per frame
/// @bug who is responsible for updating this now?"
pipelineContext().time = timeKeeper->GetRunningTime();
pipelineContext().frame = timeKeeper->PresetFrameA();
pipelineContext().progress = timeKeeper->PresetProgressA();
//m_activePreset->Render (*beatDetect, pipelineContext());
beatDetect->detectFromSamples();
//m_activePreset->evaluateFrame();
//if the preset isn't locked and there are more presets
if (renderer->noSwitch == false && !m_presetChooser->empty())
{
//if preset is done and we're not already switching
if (timeKeeper->PresetProgressA() >= 1.0 && !timeKeeper->IsSmoothing())
{
if (settings().shuffleEnabled)
selectRandom (false);
else
selectNext (false);
}
else if ( beatDetect->vol-beatDetect->vol_old>beatDetect->beat_sensitivity
&& timeKeeper->CanHardCut())
{
// printf ("Hard Cut\n");
if (settings().shuffleEnabled)
selectRandom (true);
else
selectNext (true);
}
}
if ( timeKeeper->IsSmoothing()
&& timeKeeper->SmoothRatio() <= 1.0
&& !m_presetChooser->empty())
{
// printf ("start thread\n");
assert (m_activePreset2.get());
#ifdef USE_THREADS
pthread_cond_signal (&condition);
pthread_mutex_unlock (&mutex);
#endif
m_activePreset->Render (*beatDetect, pipelineContext());
#ifdef USE_THREADS
pthread_mutex_lock (&mutex);
#else
evaluateSecondPreset();
#endif
Pipeline pipeline;
pipeline.setStaticPerPixel (settings().meshX, settings().meshY);
assert (_matcher);
PipelineMerger::mergePipelines (m_activePreset->pipeline(),
m_activePreset2->pipeline(),
pipeline,
_matcher->matchResults(),
*_merger,
timeKeeper->SmoothRatio());
renderer->RenderFrame (pipeline, pipelineContext());
pipeline.drawables.clear();
/*
while (!pipeline.drawables.empty()) {
delete (pipeline.drawables.back());
pipeline.drawables.pop_back();
} */
}
else
{
if (timeKeeper->IsSmoothing() && timeKeeper->SmoothRatio() > 1.0)
{
//printf ("End Smooth\n");
m_activePreset = m_activePreset2;
timeKeeper->EndSmoothing();
}
//printf ("Normal\n");
m_activePreset->Render (*beatDetect, pipelineContext());
renderer->RenderFrame (m_activePreset->pipeline(), pipelineContext());
}
// std::cout<< m_activePreset->absoluteFilePath()<<std::endl;
// renderer->presetName = m_activePreset->absoluteFilePath();
count++;
#ifndef WIN32
/** Frame-rate limiter */
/** Compute once per preset */
if (this->count % 100 == 0)
{
this->renderer->realfps = 100.0
/ ((getTicks (&timeKeeper->startTime)
- this->fpsstart) / 1000);
this->fpsstart = getTicks (&timeKeeper->startTime);
}
int timediff = getTicks (&timeKeeper->startTime) - this->timestart;
if (timediff < this->mspf)
{
// printf ("%s:",this->mspf-timediff);
int sleepTime = (unsigned int) (this->mspf - timediff) * 1000;
// DWRITE ("usleep: %d\n", sleepTime);
if (sleepTime > 0 && sleepTime < 100000)
{
if (usleep (sleepTime) != 0) {}
}
}
this->timestart = getTicks (&timeKeeper->startTime);
#endif /** !WIN32 */
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_unlock (&preset_mutex);
#endif
}
void
projectM::projectM_reset()
{
this->mspf = 0;
this->timed = 0;
this->timestart = 0;
this->count = 0;
this->fpsstart = 0;
setlocale (LC_NUMERIC, "C");
projectM_resetengine();
}
void
projectM::projectM_init (
int gx,
int gy,
int fps,
int texsize,
int width,
int height
)
{
setlocale (LC_NUMERIC, "C");
/** Initialise start time */
timeKeeper = new TimeKeeper (_settings.presetDuration,
_settings.smoothPresetDuration,
_settings.easterEgg);
/** Nullify frame stash */
/** Initialise per-pixel matrix calculations */
/** We need to initialise this before the builtin param db otherwise bass/mid etc won't bind correctly */
assert (!beatDetect);
if (!_pcm)
_pcm = new PCM();
assert (pcm());
beatDetect = new BeatDetect (_pcm);
if (_settings.fps > 0)
mspf = (int) (1000.0 / (float) _settings.fps);
else
mspf = 0;
this->renderer = new Renderer (width, height,
gx, gy,
texsize,
beatDetect,
settings().presetURL,
settings().titleFontURL,
settings().menuFontURL);
running = true;
initPresetTools (gx, gy);
#ifdef USE_THREADS
pthread_mutex_init (&mutex, NULL);
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_init (&preset_mutex, NULL);
#endif
pthread_cond_init (&condition, NULL);
if (pthread_create (&thread, NULL, thread_callback, this) != 0)
{
std::cerr << "[projectM] failed to allocate a thread! try building with option USE_THREADS turned off" << std::endl;;
exit (EXIT_FAILURE);
}
pthread_mutex_lock (&mutex);
#endif /* USE_THREADS */
/// @bug order of operatoins here is busted
//renderer->setPresetName (m_activePreset->name());
timeKeeper->StartPreset();
assert (pcm());
pipelineContext().fps = fps;
pipelineContext2().fps = fps;
}
/* Reinitializes the engine variables to a default (conservative and sane) value */
void
projectM::projectM_resetengine()
{
if (beatDetect != NULL)
{
beatDetect->reset();
}
}
/** Resets OpenGL state */
void
projectM::projectM_resetGL (int w, int h)
{
/** Stash the new dimensions */
renderer->reset (w,h);
}
/** Sets the title to display */
void
projectM::projectM_setTitle (std::string title)
{
if (title != renderer->title)
{
renderer->title=title;
renderer->drawtitle=1;
}
}
int projectM::initPresetTools (int gx, int gy)
{
/* Set the seed to the current time in seconds */
srand (time (NULL));
std::string url = (m_flags & FLAG_DISABLE_PLAYLIST_LOAD)
? std::string()
: settings().presetURL;
if ((m_presetLoader = new PresetLoader (gx, gy, url)) == 0)
{
m_presetLoader = 0;
std::cerr << "[projectM] error allocating preset loader" << std::endl;
return PROJECTM_FAILURE;
}
if ((m_presetChooser = new PresetChooser (*m_presetLoader, settings().softCutRatingsEnabled)) == 0)
{
delete (m_presetLoader);
m_presetChooser = 0;
m_presetLoader = 0;
std::cerr << "[projectM] error allocating preset chooser" << std::endl;
return PROJECTM_FAILURE;
}
// Start the iterator
if (!m_presetPos)
m_presetPos = new PresetIterator();
// Initialize a preset queue position as well
// m_presetQueuePos = new PresetIterator();
// Start at end ptr- this allows next/previous to easily be done from this position.
*m_presetPos = m_presetChooser->end();
// Case where no valid presets exist in directory. Could also mean
// playlist initialization was deferred
if (m_presetChooser->empty()) {
// Load idle preset
std::cerr << "[projectM] Allocating idle preset..." << std::endl;
m_activePreset = m_presetLoader->loadPreset
("idle://Rovastar & Fvese - Stranger Minds (idle-ized).milk");
renderer->SetPipeline (m_activePreset->pipeline());
//std::cerr << "[projectM] warning: no valid files found in preset directory \""
//<< m_presetLoader->directoryName() << "\"" << std::endl;
}
else if (settings().shuffleEnabled)
{
/*
* Starting at the same preset every time we launch is boring. Pick a
* preset at random.
*/
selectRandom (true);
}
_matcher = new RenderItemMatcher();
_merger = new MasterRenderItemMerge();
//_merger->add (new WaveFormMergeFunction());
_merger->add (new ShapeMerge());
_merger->add (new BorderMerge());
//_merger->add (new BorderMergeFunction());
/// @bug These should be requested by the preset factories.
_matcher->distanceFunction().addMetric (new ShapeXYDistance());
//std::cerr << "[projectM] Idle preset allocated." << std::endl;
projectM_resetengine();
//std::cerr << "[projectM] engine has been reset." << std::endl;
return PROJECTM_SUCCESS;
}
void
projectM::destroyPresetTools()
{
if (m_presetPos)
delete (m_presetPos);
m_presetPos = 0;
if (m_presetChooser)
delete (m_presetChooser);
m_presetChooser = 0;
if (m_presetLoader)
delete (m_presetLoader);
m_presetLoader = 0;
}
/// @bug queuePreset case isn't handled
void
projectM::removePreset (unsigned int index)
{
unsigned int chooserIndex = **m_presetPos;
m_presetLoader->removePreset (index);
// Case: no more presets, set iterator to end
if (m_presetChooser->empty())
*m_presetPos = m_presetChooser->end();
// Case: chooser index has become one less due to removal of an index below it
else if (chooserIndex > index) {
chooserIndex--;
*m_presetPos = m_presetChooser->begin (chooserIndex);
}
// Case: we have deleted the active preset position
// Set iterator to end of chooser
else if (chooserIndex == index) {
*m_presetPos = m_presetChooser->end();
}
}
unsigned int
projectM::addPresetURL (
const std::string & presetURL,
const std::string & presetName,
const RatingList & ratings
)
{
bool restorePosition = false;
if (*m_presetPos == m_presetChooser->end())
restorePosition = true;
int index = m_presetLoader->addPresetURL (presetURL, presetName, ratings);
if (restorePosition)
*m_presetPos = m_presetChooser->end();
return index;
}
void
projectM::selectPreset (unsigned int index, bool hardCut)
{
if (m_presetChooser->empty())
return;
if (!hardCut) {
timeKeeper->StartSmoothing();
}
*m_presetPos = m_presetChooser->begin (index);
if (!hardCut) {
switchPreset (m_activePreset2);
} else {
switchPreset (m_activePreset);
timeKeeper->StartPreset();
}
presetSwitchedEvent (hardCut, **m_presetPos);
}
void
projectM::selectRandom (const bool hardCut)
{
if (m_presetChooser->empty())
return;
if (!hardCut) {
timeKeeper->StartSmoothing();
}
*m_presetPos = m_presetChooser->weightedRandom (hardCut);
if (!hardCut) {
switchPreset (m_activePreset2);
} else {
switchPreset (m_activePreset);
timeKeeper->StartPreset();
}
presetSwitchedEvent (hardCut, **m_presetPos);
}
void
projectM::selectPrevious (const bool hardCut)
{
if (m_presetChooser->empty())
return;
if (!hardCut) {
timeKeeper->StartSmoothing();
}
m_presetChooser->previousPreset (*m_presetPos);
if (!hardCut) {
switchPreset (m_activePreset2);
} else {
switchPreset (m_activePreset);
timeKeeper->StartPreset();
}
presetSwitchedEvent (hardCut, **m_presetPos);
// m_activePreset = m_presetPos->allocate();
// renderer->SetPipeline (m_activePreset->pipeline());
// renderer->setPresetName (m_activePreset->name());
//timeKeeper->StartPreset();
}
void
projectM::selectNext (const bool hardCut)
{
if (m_presetChooser->empty())
return;
if (!hardCut) {
timeKeeper->StartSmoothing();
std::cout << "start smoothing" << std::endl;
}
m_presetChooser->nextPreset (*m_presetPos);
if (!hardCut) {
switchPreset (m_activePreset2);
} else {
switchPreset (m_activePreset);
timeKeeper->StartPreset();
}
presetSwitchedEvent (hardCut, **m_presetPos);
}
/**
*
* @param targetPreset
*/
void
projectM::switchPreset (std::auto_ptr<Preset> & targetPreset)
{
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_lock (&preset_mutex);
#endif
targetPreset = m_presetPos->allocate();
// Set preset name here- event is not done because at the moment this function is oblivious to smooth/hard switches
renderer->setPresetName (targetPreset->name());
renderer->SetPipeline (targetPreset->pipeline());
#ifdef SYNC_PRESET_SWITCHES
pthread_mutex_unlock (&preset_mutex);
#endif
}
void
projectM::setPresetLock (bool isLocked)
{
renderer->noSwitch = isLocked;
}
bool
projectM::isPresetLocked() const
{
return renderer->noSwitch;
}
std::string
projectM::getPresetURL (unsigned int index) const
{
return m_presetLoader->getPresetURL (index);
}
int
projectM::getPresetRating (unsigned int index, const PresetRatingType ratingType) const
{
return m_presetLoader->getPresetRating (index, ratingType);
}
std::string
projectM::getPresetName (unsigned int index) const
{
return m_presetLoader->getPresetName (index);
}
void
projectM::clearPlaylist()
{
m_presetLoader->clear();
*m_presetPos = m_presetChooser->end();
}
void
projectM::selectPresetPosition (unsigned int index)
{
*m_presetPos = m_presetChooser->begin (index);
}
bool
projectM::selectedPresetIndex (unsigned int & index) const
{
if (*m_presetPos == m_presetChooser->end())
return false;
index = **m_presetPos;
return true;
}
bool
projectM::presetPositionValid() const
{
return (*m_presetPos != m_presetChooser->end());
}
unsigned int
projectM::getPlaylistSize() const
{
return m_presetLoader->size();
}
void
projectM::changePresetRating (unsigned int index, int rating, const PresetRatingType ratingType)
{
m_presetLoader->setRating (index, rating, ratingType);
presetRatingChanged (index, rating, ratingType);
}
void
projectM::insertPresetURL (
unsigned int index,
const std::string & presetURL,
const std::string & presetName,
const RatingList & ratings)
{
bool atEndPosition = false;
int newSelectedIndex;
if (*m_presetPos == m_presetChooser->end()) // Case: preset not selected
{
atEndPosition = true;
}
else if (**m_presetPos < index) // Case: inserting before selected preset
{
newSelectedIndex = **m_presetPos;
}
else if (**m_presetPos > index) // Case: inserting after selected preset
{
newSelectedIndex++;
}
else // Case: inserting at selected preset
{
newSelectedIndex++;
}
m_presetLoader->insertPresetURL (index, presetURL, presetName, ratings);
if (atEndPosition)
*m_presetPos = m_presetChooser->end();
else
*m_presetPos = m_presetChooser->begin (newSelectedIndex);
}
void
projectM::changePresetName (unsigned int index, std::string name)
{
m_presetLoader->setPresetName (index, name);
}
void
projectM::changeTextureSize (int size)
{
_settings.textureSize = size;
delete renderer;
renderer = new Renderer (_settings.windowWidth, _settings.windowHeight,
_settings.meshX, _settings.meshY,
_settings.textureSize,
beatDetect,
_settings.presetURL,
_settings.titleFontURL,
_settings.menuFontURL);
}
void
projectM::changePresetDuration (int seconds)
{
timeKeeper->ChangePresetDuration (seconds);
}