/** * 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 "wipemalloc.h" #include "fatal.h" #include "Common.hpp" #include "compare.h" #ifdef WIN32 #include "win32-dirent.h" #define CMAKE_INSTALL_PREFIX "" #endif #include "timer.h" #include #ifdef LINUX #include "time.h" #endif #ifdef WIN32 #include #endif //#include #include #include "projectM.hpp" #include "BuiltinFuncs.hpp" #include "BeatDetect.hpp" #include "Eval.hpp" #include "Param.hpp" #include "Parser.hpp" #include "Preset.hpp" #include "PerPixelEqn.hpp" #include "PresetMerge.hpp" //#include "menu.h" #include "PCM.hpp" //Sound data handler (buffering, FFT, etc.) #include "CustomWave.hpp" #include "CustomShape.hpp" #include "IdlePreset.hpp" #include #include "Renderer.hpp" #include "PresetFrameIO.hpp" #include "PresetChooser.hpp" #include "TextureManager.hpp" #include "TimeKeeper.hpp" #ifdef USE_THREADS #include "pthread.h" #endif #include "SectionLock.h" /* DLLEXPORT projectM::projectM ( int gx, int gy, int fps, int texsize, int width, int height, std::string preset_url,std::string title_fonturl, std::string title_menuurl ) :beatDetect ( 0 ), renderer ( 0 ), settings.presetURL ( preset_url ), title_fontURL ( title_fonturl ), menu_fontURL ( menu_fontURL ), smoothFrame ( 0 ), m_presetQueuePos(0) { presetURL = preset_url; projectM_reset(); projectM_init ( gx, gy, fps, texsize, width, height ); projectM_resetGL ( width, height ); } */ projectM::~projectM() { #ifdef USE_THREADS pthread_mutex_lock( &mutex ); running = false; pthread_cond_signal(&condition); pthread_mutex_unlock( &mutex ); pthread_join(thread, NULL); pthread_cond_destroy(&condition); pthread_mutex_destroy( &mutex ); #endif destroyPresetTools(); if ( renderer ) delete ( renderer ); if ( beatDetect ) delete ( beatDetect ); if ( _pcm ) { delete ( _pcm ); _pcm = 0; } } DLLEXPORT unsigned projectM::initRenderToTexture() { return renderer->initRenderToTexture(); } DLLEXPORT void projectM::projectM_resetTextures() { renderer->ResetTextures(); } DLLEXPORT projectM::projectM ( Settings config_pm, int flags) : beatDetect ( 0 ), renderer ( 0 ), m_presetPos(0), _pcm(0), m_flags(flags) { readConfig ( config_pm ); projectM_reset(); projectM_resetGL ( _settings.windowWidth, _settings.windowHeight); } void projectM::readConfig (const Settings configpm ) { _settings.meshX = configpm.meshX; _settings.meshY = configpm.meshY; _settings.textureSize = configpm.textureSize; _settings.fps = configpm.fps; _settings.windowWidth = configpm.windowWidth; _settings.windowHeight = configpm.windowHeight; _settings.windowLeft = configpm.windowLeft; _settings.windowBottom = configpm.windowBottom; _settings.smoothPresetDuration = configpm.smoothPresetDuration; _settings.presetDuration = configpm.presetDuration; _settings.presetURL = configpm.presetURL; _settings.shuffleEnabled = configpm.shuffleEnabled; _settings.useFBO = false; _settings.easterEgg = configpm.easterEgg; projectM_init ( _settings.meshX, _settings.meshY, _settings.fps, _settings.textureSize, _settings.windowWidth,_settings.windowHeight, _settings.windowLeft, _settings.windowBottom, _settings.useFBO ); _settings.beatSensitivity = beatDetect->beat_sensitivity = configpm.beatSensitivity; if ( configpm.aspectCorrection ) _settings.aspectCorrection = renderer->correction = true; else _settings.aspectCorrection = renderer->correction = false; } #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 ); while (true) { pthread_cond_wait( &condition, &mutex ); if(!running) { pthread_mutex_unlock( &mutex ); return NULL; } evaluateSecondPreset(); } } #endif void projectM::evaluateSecondPreset() { Preset* p = m_activePreset2.get(); if (p) { setupPresetInputs(&p->presetInputs()); p->presetInputs().frame = timeKeeper->PresetFrameB(); p->presetInputs().progress= timeKeeper->PresetProgressB(); p->evaluateFrame(); renderer->PerPixelMath ( &p->presetOutputs(), &presetInputs2 ); renderer->WaveformMath ( &p->presetOutputs(), &presetInputs2, true ); } else printf("projectm: %s - preset was NULL\n", __FUNCTION__); } void projectM::setupPresetInputs(PresetInputs *inputs) { inputs->ResetMesh(); inputs->time = timeKeeper->GetRunningTime(); inputs->bass = beatDetect->bass; inputs->mid = beatDetect->mid; inputs->treb = beatDetect->treb; inputs->bass_att = beatDetect->bass_att; inputs->mid_att = beatDetect->mid_att; inputs->treb_att = beatDetect->treb_att; } DLLEXPORT void projectM::renderFrame() { #ifdef DEBUG char fname[1024]; FILE *f = NULL; int index = 0; int x, y; #endif timeKeeper->UpdateTimers(); mspf= ( int ) ( 1000.0/ ( float ) presetInputs.fps ); //milliseconds per frame setupPresetInputs(&m_activePreset->presetInputs()); m_activePreset->presetInputs().frame = timeKeeper->PresetFrameA(); m_activePreset->presetInputs().progress= timeKeeper->PresetProgressA(); beatDetect->detectFromSamples(); //m_activePreset->evaluateFrame(); if ( renderer->noSwitch==false && !m_presetChooser->empty() ) { if ( timeKeeper->PresetProgressA()>=1.0 && !timeKeeper->IsSmoothing()) { CSectionLock lock(&mutex); timeKeeper->StartSmoothing(); switchPreset(m_activePreset2, &m_activePreset->presetInputs() == &presetInputs ? presetInputs2 : presetInputs, &m_activePreset->presetOutputs() == &presetOutputs ? presetOutputs2 : presetOutputs); presetSwitchedEvent(false, **m_presetPos); } else if ( ( beatDetect->vol-beatDetect->vol_old>beatDetect->beat_sensitivity ) && timeKeeper->CanHardCut() ) { switchPreset(m_activePreset, presetInputs, presetOutputs); timeKeeper->StartPreset(); presetSwitchedEvent(true, **m_presetPos); } } if ( timeKeeper->IsSmoothing() && timeKeeper->SmoothRatio() <= 1.0 && !m_presetChooser->empty() ) { assert ( m_activePreset.get() ); #ifdef USE_THREADS pthread_cond_signal(&condition); #endif m_activePreset->evaluateFrame(); renderer->PerPixelMath ( &m_activePreset->presetOutputs(), &presetInputs ); renderer->WaveformMath ( &m_activePreset->presetOutputs(), &presetInputs, true ); #ifndef USE_THREADS evaluateSecondPreset(); #endif CSectionLock lock(&mutex); PresetMerger::MergePresets ( m_activePreset->presetOutputs(),m_activePreset2->presetOutputs(),timeKeeper->SmoothRatio(),presetInputs.gx, presetInputs.gy ); } else { if ( timeKeeper->IsSmoothing() && timeKeeper->SmoothRatio() > 1.0 ) { CSectionLock lock(&mutex); m_activePreset = m_activePreset2; switchPreset(m_activePreset2, presetInputs2, presetOutputs2); timeKeeper->EndSmoothing(); } m_activePreset->evaluateFrame(); renderer->PerPixelMath ( &m_activePreset->presetOutputs(), &presetInputs ); renderer->WaveformMath ( &m_activePreset->presetOutputs(), &presetInputs, false ); } renderer->RenderFrame ( &m_activePreset->presetOutputs(), &presetInputs ); 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 ) { int sleepTime = ( unsigned int ) ( this->mspf-timediff ) * 1000; if ( sleepTime > 0 && sleepTime < 100000 ) { if ( usleep ( sleepTime ) != 0 ) {}} } this->timestart=getTicks ( &timeKeeper->startTime ); #endif /** !WIN32 */ } void projectM::projectM_reset() { /** Default variable settings */ // this->wvw = 512; // this->wvh = 512; /** More other stuff */ this->mspf = 0; this->timed = 0; this->timestart = 0; this->count = 0; this->fpsstart = 0; projectM_resetengine(); } void projectM::projectM_init ( int gx, int gy, int fps, int texsize, int width, int height, int xpos, int ypos, bool usefbo ) { /** Initialise engine variables */ projectM_initengine(); presetInputs.Initialize ( gx, gy ); presetInputs2.Initialize ( gx, gy ); presetOutputs.Initialize ( gx, gy ); presetOutputs2.Initialize ( gx, gy ); /** Initialise start time */ timeKeeper = new TimeKeeper(_settings.presetDuration,_settings.smoothPresetDuration, _settings.easterEgg); /** Nullify frame stash */ /** Initialise per-pixel matrix calculations */ presetInputs.fps = fps; presetInputs2.fps = fps; /** 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 ); this->presetInputs.gx = gx; this->presetInputs.gy = gy; this->presetInputs2.gx = gx; this->presetInputs2.gy = gy; this->renderer = new Renderer ( width, height, gx, gy, texsize, beatDetect, settings().presetURL, settings().titleFontURL, settings().menuFontURL, xpos, ypos, usefbo); initPresetTools(); if ( presetInputs.fps > 0 ) mspf= ( int ) ( 1000.0/ ( float ) presetInputs.fps ); else mspf = 0; running = true; #ifdef USE_THREADS pthread_mutex_init(&mutex, NULL); pthread_cond_init(&condition, NULL); if (pthread_create(&thread, NULL, thread_callback, this) != 0) { std::cerr << "failed to allocate a thread! try building with option USE_THREADS turned off" << std::endl;; exit(1); } #endif renderer->setPresetName ( m_activePreset->presetName() ); timeKeeper->StartPreset(); assert(pcm()); } void projectM::projectM_initengine() { /* PER FRAME CONSTANTS BEGIN */ this->presetOutputs.zoom=1.0; this->presetOutputs.zoomexp= 1.0; this->presetOutputs.rot= 0.0; this->presetOutputs.warp= 0.0; this->presetOutputs.sx= 1.0; this->presetOutputs.sy= 1.0; this->presetOutputs.dx= 0.0; this->presetOutputs.dy= 0.0; this->presetOutputs.cx= 0.5; this->presetOutputs.cy= 0.5; this->presetOutputs.decay=.98; this->presetOutputs.wave_r= 1.0; this->presetOutputs.wave_g= 0.2; this->presetOutputs.wave_b= 0.0; this->presetOutputs.wave_x= 0.5; this->presetOutputs.wave_y= 0.5; this->presetOutputs.wave_mystery= 0.0; this->presetOutputs.ob_size= 0.0; this->presetOutputs.ob_r= 0.0; this->presetOutputs.ob_g= 0.0; this->presetOutputs.ob_b= 0.0; this->presetOutputs.ob_a= 0.0; this->presetOutputs.ib_size = 0.0; this->presetOutputs.ib_r = 0.0; this->presetOutputs.ib_g = 0.0; this->presetOutputs.ib_b = 0.0; this->presetOutputs.ib_a = 0.0; this->presetOutputs.mv_a = 0.0; this->presetOutputs.mv_r = 0.0; this->presetOutputs.mv_g = 0.0; this->presetOutputs.mv_b = 0.0; this->presetOutputs.mv_l = 1.0; this->presetOutputs.mv_x = 16.0; this->presetOutputs.mv_y = 12.0; this->presetOutputs.mv_dy = 0.02; this->presetOutputs.mv_dx = 0.02; //this->presetInputs.meshx = 0; //this->presetInputs.meshy = 0; this->presetInputs.progress = 0; this->presetInputs.frame = 1; this->presetInputs2.progress = 0; this->presetInputs2.frame = 1; //bass_thresh = 0; /* PER_FRAME CONSTANTS END */ this->presetOutputs.fRating = 0; this->presetOutputs.fGammaAdj = 1.0; this->presetOutputs.fVideoEchoZoom = 1.0; this->presetOutputs.fVideoEchoAlpha = 0; this->presetOutputs.nVideoEchoOrientation = 0; this->presetOutputs.nWaveMode = 7; this->presetOutputs.bAdditiveWaves = 0; this->presetOutputs.bWaveDots = 0; this->presetOutputs.bWaveThick = 0; this->presetOutputs.bModWaveAlphaByVolume = 0; this->presetOutputs.bMaximizeWaveColor = 0; this->presetOutputs.bTexWrap = 0; this->presetOutputs.bDarkenCenter = 0; this->presetOutputs.bRedBlueStereo = 0; this->presetOutputs.bBrighten = 0; this->presetOutputs.bDarken = 0; this->presetOutputs.bSolarize = 0; this->presetOutputs.bInvert = 0; this->presetOutputs.bMotionVectorsOn = 1; this->presetOutputs.fWaveAlpha =1.0; this->presetOutputs.fWaveScale = 1.0; this->presetOutputs.fWaveSmoothing = 0; this->presetOutputs.fWaveParam = 0; this->presetOutputs.fModWaveAlphaStart = 0; this->presetOutputs.fModWaveAlphaEnd = 0; this->presetOutputs.fWarpAnimSpeed = 0; this->presetOutputs.fWarpScale = 0; this->presetOutputs.fShader = 0; /* PER_PIXEL CONSTANTS BEGIN */ /* PER_PIXEL CONSTANT END */ /* Q AND T VARIABLES START */ this->presetOutputs.q1 = 0; this->presetOutputs.q2 = 0; this->presetOutputs.q3 = 0; this->presetOutputs.q4 = 0; this->presetOutputs.q5 = 0; this->presetOutputs.q6 = 0; this->presetOutputs.q7 = 0; this->presetOutputs.q8 = 0; /* Q AND T VARIABLES END */ } /* Reinitializes the engine variables to a default (conservative and sane) value */ void projectM::projectM_resetengine() { this->presetOutputs.zoom=1.0; this->presetOutputs.zoomexp= 1.0; this->presetOutputs.rot= 0.0; this->presetOutputs.warp= 0.0; this->presetOutputs.sx= 1.0; this->presetOutputs.sy= 1.0; this->presetOutputs.dx= 0.0; this->presetOutputs.dy= 0.0; this->presetOutputs.cx= 0.5; this->presetOutputs.cy= 0.5; this->presetOutputs.decay=.98; this->presetOutputs.wave_r= 1.0; this->presetOutputs.wave_g= 0.2; this->presetOutputs.wave_b= 0.0; this->presetOutputs.wave_x= 0.5; this->presetOutputs.wave_y= 0.5; this->presetOutputs.wave_mystery= 0.0; this->presetOutputs.ob_size= 0.0; this->presetOutputs.ob_r= 0.0; this->presetOutputs.ob_g= 0.0; this->presetOutputs.ob_b= 0.0; this->presetOutputs.ob_a= 0.0; this->presetOutputs.ib_size = 0.0; this->presetOutputs.ib_r = 0.0; this->presetOutputs.ib_g = 0.0; this->presetOutputs.ib_b = 0.0; this->presetOutputs.ib_a = 0.0; this->presetOutputs.mv_a = 0.0; this->presetOutputs.mv_r = 0.0; this->presetOutputs.mv_g = 0.0; this->presetOutputs.mv_b = 0.0; this->presetOutputs.mv_l = 1.0; this->presetOutputs.mv_x = 16.0; this->presetOutputs.mv_y = 12.0; this->presetOutputs.mv_dy = 0.02; this->presetOutputs.mv_dx = 0.02; if ( beatDetect != NULL ) { beatDetect->reset(); } this->presetInputs.progress = 0; this->presetInputs.frame = 1; this->presetInputs2.progress = 0; this->presetInputs2.frame = 1; // bass_thresh = 0; /* PER_FRAME CONSTANTS END */ this->presetOutputs.fRating = 0; this->presetOutputs.fGammaAdj = 1.0; this->presetOutputs.fVideoEchoZoom = 1.0; this->presetOutputs.fVideoEchoAlpha = 0; this->presetOutputs.nVideoEchoOrientation = 0; this->presetOutputs.nWaveMode = 7; this->presetOutputs.bAdditiveWaves = 0; this->presetOutputs.bWaveDots = 0; this->presetOutputs.bWaveThick = 0; this->presetOutputs.bModWaveAlphaByVolume = 0; this->presetOutputs.bMaximizeWaveColor = 0; this->presetOutputs.bTexWrap = 0; this->presetOutputs.bDarkenCenter = 0; this->presetOutputs.bRedBlueStereo = 0; this->presetOutputs.bBrighten = 0; this->presetOutputs.bDarken = 0; this->presetOutputs.bSolarize = 0; this->presetOutputs.bInvert = 0; this->presetOutputs.bMotionVectorsOn = 1; this->presetOutputs.fWaveAlpha =1.0; this->presetOutputs.fWaveScale = 1.0; this->presetOutputs.fWaveSmoothing = 0; this->presetOutputs.fWaveParam = 0; this->presetOutputs.fModWaveAlphaStart = 0; this->presetOutputs.fModWaveAlphaEnd = 0; this->presetOutputs.fWarpAnimSpeed = 0; this->presetOutputs.fWarpScale = 0; this->presetOutputs.fShader = 0; /* PER_PIXEL CONSTANTS BEGIN */ this->presetInputs2.x_per_pixel = 0; this->presetInputs2.y_per_pixel = 0; this->presetInputs2.rad_per_pixel = 0; this->presetInputs2.ang_per_pixel = 0; this->presetInputs.x_per_pixel = 0; this->presetInputs.y_per_pixel = 0; this->presetInputs.rad_per_pixel = 0; this->presetInputs.ang_per_pixel = 0; /* PER_PIXEL CONSTANT END */ /* Q VARIABLES START */ this->presetOutputs.q1 = 0; this->presetOutputs.q2 = 0; this->presetOutputs.q3 = 0; this->presetOutputs.q4 = 0; this->presetOutputs.q5 = 0; this->presetOutputs.q6 = 0; this->presetOutputs.q7 = 0; this->presetOutputs.q8 = 0; /* Q VARIABLES END */ } /** Resets OpenGL state */ DLLEXPORT void projectM::projectM_resetGL ( int w, int h ) { /** Stash the new dimensions */ renderer->reset ( w,h ); } /** Sets the title to display */ DLLEXPORT void projectM::projectM_setTitle ( std::string title ) { if ( title != renderer->title ) { renderer->title=title; renderer->drawtitle=1; } } int projectM::initPresetTools() { /* Initializes the builtin function database */ BuiltinFuncs::init_builtin_func_db(); /* Initializes all infix operators */ Eval::init_infix_ops(); /* 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 ( url) ) == 0 ) { m_presetLoader = 0; std::cerr << "[projectM] error allocating preset loader" << std::endl; return PROJECTM_FAILURE; } if ( ( m_presetChooser = new PresetChooser ( *m_presetLoader ) ) == 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(); // Load idle preset //std::cerr << "[projectM] Allocating idle preset..." << std::endl; if (m_presetChooser->empty()) m_activePreset = IdlePreset::allocate ( presetInputs, presetOutputs ); else switchPreset(m_activePreset, presetInputs, presetOutputs); // Case where no valid presets exist in directory. Could also mean // playlist initialization was deferred //if ( m_presetChooser->empty() ) //{ //std::cerr << "[projectM] warning: no valid files found in preset directory \"" //<< m_presetLoader->directoryName() << "\"" << std::endl; //} //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 ); /// @slow might not be necessary m_presetPos = 0; if ( m_presetChooser ) delete ( m_presetChooser ); /// @slow might not be necessary m_presetChooser = 0; if ( m_presetLoader ) delete ( m_presetLoader ); /// @slow might not be necessary m_presetLoader = 0; Eval::destroy_infix_ops(); BuiltinFuncs::destroy_builtin_func_db(); } /// @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->begin(chooserIndex); std::cerr << "deleted active preset!"; *m_presetPos = m_presetChooser->end(); } } unsigned int projectM::addPresetURL ( const std::string & presetURL, const std::string & presetName, int rating ) { bool restorePosition = false; if (*m_presetPos == m_presetChooser->end()) restorePosition = true; int index = m_presetLoader->addPresetURL ( presetURL, presetName, rating); if (restorePosition) *m_presetPos = m_presetChooser->end(); return index; } void projectM::selectPreset ( unsigned int index ) { if ( m_presetChooser->empty() ) return; assert ( index < m_presetLoader->getNumPresets() ); assert ( index >= 0 ); *m_presetPos = m_presetChooser->begin ( index ); m_activePreset = m_presetPos->allocate ( presetInputs, presetOutputs ); renderer->setPresetName ( m_activePreset->presetName() ); timeKeeper->StartPreset(); } void projectM::switchPreset(std::auto_ptr & targetPreset, PresetInputs & inputs, PresetOutputs & outputs) { if (_settings.shuffleEnabled) *m_presetPos = m_presetChooser->weightedRandom(); else m_presetChooser->nextPreset(*m_presetPos); targetPreset = m_presetPos->allocate( inputs, outputs ); // Set preset name here- event is not done because at the moment this function is oblivious to smooth/hard switches renderer->setPresetName ( targetPreset->presetName() ); } 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 { return m_presetLoader->getPresetRating(index); } std::string projectM::getPresetName ( unsigned int index ) const { return m_presetLoader->getPresetName(index); } void projectM::clearPlaylist ( ) { m_presetLoader->clear(); *m_presetPos = m_presetChooser->end(); } /// Sets preset iterator position to the passed in index 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->getNumPresets(); } void projectM:: changePresetRating (unsigned int index, int rating) { m_presetLoader->setRating(index, rating); } void projectM::insertPresetURL(unsigned int index, const std::string & presetURL, const std::string & presetName, int rating) { bool atEndPosition = false; int newSelectedIndex = index; 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, rating); if (atEndPosition) *m_presetPos = m_presetChooser->end(); else *m_presetPos = m_presetChooser->begin(newSelectedIndex); }