Logo
  • Main Page
  • Related Pages
  • Modules
  • Classes
  • Files

mmspulser.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2005-2007 Stefan Schwarzer, Jens Schneider,             *
00003  *                           Matthias Hardt, Guido Madaus                  *
00004  *                                                                         *
00005  *   Copyright (C) 2007-2008 BerLinux Solutions GbR                        *
00006  *                           Stefan Schwarzer & Guido Madaus               *
00007  *                                                                         *
00008  *   Copyright (C) 2009-2013 BerLinux Solutions GmbH                       *
00009  *                                                                         *
00010  *   Authors:                                                              *
00011  *      Stefan Schwarzer   <stefan.schwarzer@diskohq.org>,                 *
00012  *      Matthias Hardt     <matthias.hardt@diskohq.org>,                   *
00013  *      Jens Schneider     <jens.schneider@diskohq.org>,                   *
00014  *      Guido Madaus       <guido.madaus@diskohq.org>,                     *
00015  *      Patrick Helterhoff <patrick.helterhoff@diskohq.org>,               *
00016  *      René Bählkow       <rene.baehlkow@diskohq.org>                     *
00017  *                                                                         *
00018  *   This library is free software; you can redistribute it and/or         *
00019  *   modify it under the terms of the GNU Lesser General Public            *
00020  *   License version 2.1 as published by the Free Software Foundation.     *
00021  *                                                                         *
00022  *   This library is distributed in the hope that it will be useful,       *
00023  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00024  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00025  *   Lesser General Public License for more details.                       *
00026  *                                                                         *
00027  *   You should have received a copy of the GNU Lesser General Public      *
00028  *   License along with this library; if not, write to the                 *
00029  *   Free Software Foundation, Inc.,                                       *
00030  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
00031  **************************************************************************/
00032 
00033 #include "mmstools/mmspulser.h"
00034 #include "mmstools/tools.h"
00035 #include "mmstools/mmserror.h"
00036 #include <math.h>
00037 
00038 MMSPulser::MMSPulser() : MMSThread("MMSPulser") {
00039     // animation is not running
00040     this->animRunning = false;
00041 
00042     // set stacksize to 128kb, will be allocated if started with start(true)
00043     setStacksize(128*1024);
00044 
00045     // set attributes
00046     setStepsPerSecond(25);
00047     setMaxCPUUsage(100);
00048     setMaxFrameRate(25);
00049     setMaxOffset(0);
00050     setDuration(0);
00051 
00052     // reset all other values
00053     reset();
00054 }
00055 
00056 MMSPulser::~MMSPulser() {
00057 }
00058 
00059 void MMSPulser::reset() {
00060     // reset all values
00061     this->cancel                = false;
00062     this->recalc_requested      = true;
00063     this->recalc_cnt            = 0;
00064     this->recalc_interval       = 3;
00065     this->step_len              = 0;
00066     this->offset                = 0;
00067     this->offset_curve          = 0;
00068     this->process_time          = 0;
00069     this->frame_delay           = 0;
00070     this->frame_rate            = 0;
00071     this->frames                = 0;
00072     this->times_buf_pos         = 0;
00073     this->times_buf_cnt         = 0;
00074     this->real_duration         = 0;
00075     this->onAnimation_counter   = 0;
00076 
00077     if (this->max_offset > 0) {
00078         // if max offset is set, recalculate every loop
00079         this->recalc_interval = 1;
00080     }
00081 
00082     // use special seq_modes
00083     switch (this->seq_mode) {
00084     case MMSPULSER_SEQ_LINEAR:
00085     case MMSPULSER_SEQ_LOG_SOFT_START:
00086     case MMSPULSER_SEQ_LOG_SOFT_END:
00087     case MMSPULSER_SEQ_LOG_SOFT_START_AND_END:
00088         this->offset = 1;
00089         this->offset_curve = 0;
00090         calcCurve(this->offset, this->offset_curve);
00091         break;
00092     case MMSPULSER_SEQ_LINEAR_DESC:
00093     case MMSPULSER_SEQ_LOG_DESC_SOFT_START:
00094     case MMSPULSER_SEQ_LOG_DESC_SOFT_END:
00095     case MMSPULSER_SEQ_LOG_DESC_SOFT_START_AND_END:
00096         this->offset = this->max_offset - 1;
00097         this->offset_curve = this->max_offset;
00098         calcCurve(this->offset, this->offset_curve);
00099         break;
00100     default:
00101         break;
00102     }
00103 }
00104 
00105 void MMSPulser::calcCurve(double &offset, double &offset_curve) {
00106     // curve calculation
00107     if (this->max_offset > 0) {
00108         switch (this->seq_mode) {
00109         case MMSPULSER_SEQ_LINEAR:
00110             if (this->seq_start <= 0)
00111                 offset_curve = offset;
00112             else
00113                 offset_curve = this->seq_start + (offset * this->seq_range) / this->max_offset;
00114             break;
00115         case MMSPULSER_SEQ_LINEAR_DESC:
00116             if (this->seq_start <= 0)
00117                 offset_curve = offset;
00118             else
00119                 offset_curve = (offset * this->seq_range) / this->max_offset;
00120             break;
00121         case MMSPULSER_SEQ_LOG_SOFT_START:
00122             // check offset, because log(1) is zero
00123             if (this->max_offset - offset > 1) {
00124                 offset_curve = this->seq_start
00125                                 + this->seq_range * (1 - (log(this->max_offset - offset) / this->max_offset_log));
00126             }
00127             else {
00128                 // last offset
00129                 offset_curve = this->max_offset;
00130             }
00131             break;
00132         case MMSPULSER_SEQ_LOG_DESC_SOFT_START:
00133             // check offset, because log(1) is zero
00134             if (offset > 1) {
00135                 offset_curve = this->seq_start
00136                                 - this->seq_range * (1 - (log(offset) / this->max_offset_log));
00137             }
00138             else {
00139                 // last offset
00140                 offset_curve = 0;
00141             }
00142             break;
00143         case MMSPULSER_SEQ_LOG_SOFT_END:
00144             // check offset, because log(1) is zero
00145             if (offset == 1) offset++;
00146             offset_curve = this->seq_start
00147                             + this->seq_range * (log(offset) / this->max_offset_log);
00148             break;
00149         case MMSPULSER_SEQ_LOG_DESC_SOFT_END:
00150             // check offset, because log(1) is zero
00151             if (this->max_offset - offset == 1) offset--;
00152             offset_curve = this->seq_start
00153                             - this->seq_range * (log(this->max_offset - offset) / this->max_offset_log);
00154             break;
00155         case MMSPULSER_SEQ_LOG_SOFT_START_AND_END:
00156             if (offset_curve < this->max_offset / 2) {
00157                 if (this->max_offset - offset > 0) {
00158                     offset_curve = this->seq_start
00159                                     + this->seq_range * (1 - (log(this->max_offset - offset) / this->max_offset_log));
00160                     if (offset_curve >= this->max_offset / 2) {
00161                         offset = this->max_offset - offset + 1;
00162                         calcCurve(offset, offset_curve);
00163                     }
00164                 }
00165                 else {
00166                     // log(0)
00167                     offset_curve = this->max_offset;
00168                     offset = 1;
00169                     calcCurve(offset, offset_curve);
00170                 }
00171             }
00172             else {
00173                 offset_curve = (this->max_offset - this->seq_range)
00174                                 + this->seq_range * (log(offset) / this->max_offset_log);
00175             }
00176             break;
00177         case MMSPULSER_SEQ_LOG_DESC_SOFT_START_AND_END:
00178             if (offset_curve > this->max_offset / 2) {
00179                 if (offset > 0) {
00180                     offset_curve = (this->max_offset - this->seq_range)
00181                                     + this->seq_range * (log(offset) / this->max_offset_log);
00182                     if (offset_curve <= this->max_offset / 2) {
00183                         offset = this->max_offset - offset - 1;
00184                         calcCurve(offset, offset_curve);
00185                     }
00186                 }
00187                 else {
00188                     // log(0)
00189                     offset_curve = 0;
00190                     offset = this->max_offset - 1;
00191                     calcCurve(offset, offset_curve);
00192                 }
00193             }
00194             else {
00195                 offset_curve = this->seq_range * (1 - (log(this->max_offset - offset) / this->max_offset_log));
00196             }
00197             break;
00198 
00199         default:
00200             offset_curve = 0;
00201             break;
00202         }
00203     }
00204     else
00205         offset_curve = 0;
00206 }
00207 
00208 void MMSPulser::threadMain() {
00209 
00210     // call connected onBeforeAnimation callbacks
00211     if (!this->onBeforeAnimation.emit(this)) {
00212         // one of the connected callbacks has failed, do not start the animation
00213         return;
00214     }
00215 
00216     // get start timestamp
00217     this->anim_start = getMTimeStamp();
00218 
00219     while (1) {
00220         // start and end timestamps for CPU average calculation
00221         unsigned int start_ts;
00222         unsigned int end_ts;
00223 
00224         // get start timestamp if needed
00225         if (this->recalc_requested) {
00226             start_ts = getMTimeStamp();
00227         }
00228 
00229         if (this->cancel) {
00230             // we should stop the animation
00231             break;
00232         }
00233 
00234         // call connected onAnimation callbacks
00235         if (!this->onAnimation.emit(this)) {
00236             // one of the connected callbacks has "failed", stop the animation
00237             break;
00238         }
00239         this->onAnimation_counter++;
00240 
00241         // recalc speed parameters?
00242         if (this->recalc_requested) {
00243             // get end timestamp
00244             end_ts = getMTimeStamp();
00245 
00246             // get process time
00247             // we collect up to MMSPULSER_TIMES_BUF_SIZE times in a ring buffer
00248             // and calculate the CPU average
00249             unsigned int times = 0;
00250             unsigned int diff = getMDiff(start_ts, end_ts);
00251             this->times_buf[times_buf_pos++] = diff;
00252             if (this->times_buf_pos >= MMSPULSER_TIMES_BUF_SIZE) this->times_buf_pos = 0;
00253             if (this->times_buf_cnt < MMSPULSER_TIMES_BUF_SIZE) this->times_buf_cnt++;
00254             for (unsigned int i = 0; i < this->times_buf_cnt; i++) times+= this->times_buf[i];
00255             this->process_time = (times + diff / 2) / this->times_buf_cnt;
00256 
00257             // calculate step length and frame delay
00258             int max_duration = 1000 / this->steps_per_second;
00259             if (this->seq_mode == MMSPULSER_SEQ_LOG_SOFT_START_AND_END || this->seq_mode == MMSPULSER_SEQ_LOG_DESC_SOFT_START_AND_END) {
00260                 // for "soft start and end" modes we have to divide max_duration in halves
00261                 max_duration/= 2;
00262             }
00263             int total_time = (this->process_time * 100) / this->max_cpu_usage;
00264             this->step_len = (total_time + max_duration - 1) / max_duration;
00265             this->frame_delay = max_duration * this->step_len - this->process_time;
00266             if (this->frame_delay <= 0) this->frame_delay = 1;
00267             int frame_duration = this->frame_delay + this->process_time;
00268             this->frame_rate = 1000 / frame_duration;
00269 
00270             if (this->frame_rate > this->max_frame_rate) {
00271                 // calculated frame rate is greater than max frame rate
00272                 // so we have to calculate back the step length and the frame delay
00273                 this->frame_delay = 1000 / this->max_frame_rate - this->process_time;
00274                 frame_duration = this->frame_delay + this->process_time;
00275                 this->step_len = (frame_duration + max_duration - 1) / max_duration;
00276                 this->frame_delay = max_duration * this->step_len - this->process_time;
00277                 if (this->frame_delay <= 0) this->frame_delay = 1;
00278                 frame_duration = this->frame_delay + this->process_time;
00279                 this->frame_rate = 1000 / frame_duration;
00280             }
00281 
00282             // mark as calculated
00283             this->recalc_cnt = 1;
00284             this->recalc_requested = (this->recalc_interval <= 1);
00285         }
00286         else {
00287             // recalc not needed
00288             this->recalc_cnt++;
00289             if (this->recalc_cnt >= this->recalc_interval) {
00290                 // after recalc_interval times, recalc requested
00291                 this->recalc_cnt = 0;
00292                 this->recalc_requested = true;
00293             }
00294         }
00295 
00296         // sleeping...
00297         usleep((this->frame_delay>0)?this->frame_delay*1000:1000);
00298 
00299         // increase the frame counter
00300         this->frames++;
00301 
00302         // increase/decrease offset with step length
00303         switch (this->seq_mode) {
00304         case MMSPULSER_SEQ_LINEAR:
00305         case MMSPULSER_SEQ_LOG_SOFT_START:
00306         case MMSPULSER_SEQ_LOG_SOFT_END:
00307         case MMSPULSER_SEQ_LOG_SOFT_START_AND_END:
00308             this->offset+= this->step_len;
00309             break;
00310         case MMSPULSER_SEQ_LINEAR_DESC:
00311         case MMSPULSER_SEQ_LOG_DESC_SOFT_START:
00312         case MMSPULSER_SEQ_LOG_DESC_SOFT_END:
00313         case MMSPULSER_SEQ_LOG_DESC_SOFT_START_AND_END:
00314             this->offset-= this->step_len;
00315             break;
00316         }
00317 
00318         // curve calculation
00319         calcCurve(this->offset, this->offset_curve);
00320 
00321         // stop the animation?
00322         bool stop = false;
00323         if (this->max_offset > 0) {
00324             if   ((this->seq_mode == MMSPULSER_SEQ_LINEAR)
00325                 ||(this->seq_mode == MMSPULSER_SEQ_LOG_SOFT_START)
00326                 ||(this->seq_mode == MMSPULSER_SEQ_LOG_SOFT_END)
00327                 ||(this->seq_mode == MMSPULSER_SEQ_LOG_SOFT_START_AND_END)) {
00328                 // ascending modes
00329                 if (this->offset_curve > 0) {
00330                     if (this->offset_curve >= this->max_offset) {
00331                         // offset is exceeded, stop the animation
00332                         stop = true;
00333                     }
00334                 }
00335                 else {
00336                     if (this->offset >= this->max_offset) {
00337                         // offset is exceeded, stop the animation
00338                         stop = true;
00339                     }
00340                 }
00341             }
00342             else {
00343                 // descending modes
00344                 if (this->offset_curve != 0) {
00345                     if (this->offset_curve < 0) {
00346                         // offset is exceeded, stop the animation
00347                         stop = true;
00348                     }
00349                 }
00350                 else {
00351                     if (this->offset < 0) {
00352                         // offset is exceeded, stop the animation
00353                         stop = true;
00354                     }
00355                 }
00356             }
00357         }
00358 
00359         // stop the animation?
00360         if ((this->duration) && (this->real_duration > this->duration)) {
00361             // requested duration reached, stop the animation
00362             stop = true;
00363         }
00364 
00365         // get real animation duration
00366         this->anim_end = getMTimeStamp();
00367         this->real_duration = getMDiff(this->anim_start, this->anim_end);
00368 
00369         if (stop) {
00370             // break the loop
00371             break;
00372         }
00373     }
00374 
00375     // call connected onAfterAnimation callbacks
00376     this->onAfterAnimation.emit(this);
00377 }
00378 
00379 bool MMSPulser::start(bool separate_thread, bool wait) {
00380     // waiting for the end of the thread
00381     this->startlock.lock();
00382     while (isRunning()) {
00383         if (wait) {
00384             usleep(10000);
00385         }
00386         else {
00387             this->startlock.unlock();
00388             return false;
00389         }
00390     }
00391 
00392     // reset all values
00393     reset();
00394 
00395     if (separate_thread) {
00396         // start animation in a separate thread context
00397         bool ret = MMSThread::start();
00398         this->startlock.unlock();
00399         return ret;
00400     }
00401     else {
00402         // start animation in the context of the current thread
00403         this->animRunning = true;
00404         this->startlock.unlock();
00405 
00406         try {
00407 
00408             // do the animation
00409             threadMain();
00410 
00411         } catch(MMSError &error) {
00412             DEBUGMSG(this->identity.c_str(), "Abort due to: " + error.getMessage());
00413         }
00414 
00415         this->animRunning = false;
00416     }
00417 
00418     return true;
00419 }
00420 
00421 bool MMSPulser::isRunning() {
00422     if (MMSThread::isRunning()) {
00423         // separate thread is running
00424         return true;
00425     }
00426     else {
00427         // check if running without separate thread
00428         return this->animRunning;
00429     }
00430 }
00431 
00432 void MMSPulser::stop() {
00433     this->startlock.lock();
00434     this->cancel = true;
00435     this->startlock.unlock();
00436 }
00437 
00438 bool MMSPulser::setStepsPerSecond(int steps_per_second) {
00439     // check & set
00440     if (steps_per_second <= 0) return false;
00441     if (steps_per_second > 255) return false;
00442     this->steps_per_second = steps_per_second;
00443 
00444     // recalculation requested
00445     recalc_cnt = 0;
00446     recalc_requested = true;
00447 
00448     return true;
00449 }
00450 
00451 int MMSPulser::getStepsPerSecond() {
00452     return this->steps_per_second;
00453 }
00454 
00455 bool MMSPulser::setMaxCPUUsage(int max_cpu_usage) {
00456     // check & set
00457     if (max_cpu_usage < 10) return false;
00458     if (max_cpu_usage > 100) return false;
00459     this->max_cpu_usage = max_cpu_usage;
00460 
00461     // recalculation requested
00462     recalc_cnt = 0;
00463     recalc_requested = true;
00464 
00465     return true;
00466 }
00467 
00468 int MMSPulser::getMaxCPUUsage() {
00469     return this->max_cpu_usage;
00470 }
00471 
00472 bool MMSPulser::setMaxFrameRate(int max_frame_rate) {
00473     // check & set
00474     if (max_frame_rate < 10) return false;
00475     if (max_frame_rate > 100) return false;
00476     this->max_frame_rate = max_frame_rate;
00477 
00478     // recalculation requested
00479     recalc_cnt = 0;
00480     recalc_requested = true;
00481 
00482     return true;
00483 }
00484 
00485 int MMSPulser::getMaxFrameRate() {
00486     return this->max_frame_rate;
00487 }
00488 
00489 int MMSPulser::getFrameRate() {
00490     return this->frame_rate;
00491 }
00492 
00493 int MMSPulser::getFrameDelay() {
00494     return this->frame_delay;
00495 }
00496 
00497 unsigned int MMSPulser::getFrames() {
00498     return this->frames;
00499 }
00500 
00501 int MMSPulser::getStepLength() {
00502     return this->step_len;
00503 }
00504 
00505 bool MMSPulser::setMaxOffset(double max_offset, MMSPULSER_SEQ seq_mode, double seq_range) {
00506     // check & set
00507     if ((max_offset < 2)&&(max_offset != 0)) return false;
00508     if ((seq_range < 2)&&(seq_range != 0)) return false;
00509     if (seq_range > max_offset) return false;
00510     this->max_offset = max_offset;
00511     this->seq_mode = seq_mode;
00512     this->seq_range = seq_range;
00513 
00514     // get the start point of the sequence
00515     if (this->seq_range <= 0) {
00516         // full, default
00517         switch (this->seq_mode) {
00518         case MMSPULSER_SEQ_LINEAR:
00519         case MMSPULSER_SEQ_LOG_SOFT_START:
00520         case MMSPULSER_SEQ_LOG_SOFT_END:
00521             this->seq_start = 0;
00522             this->seq_range = this->max_offset;
00523             break;
00524         case MMSPULSER_SEQ_LOG_SOFT_START_AND_END:
00525             this->seq_start = 0;
00526             this->seq_range = this->max_offset / 2;
00527             break;
00528         case MMSPULSER_SEQ_LINEAR_DESC:
00529         case MMSPULSER_SEQ_LOG_DESC_SOFT_START:
00530         case MMSPULSER_SEQ_LOG_DESC_SOFT_END:
00531             this->seq_start = this->max_offset;
00532             this->seq_range = this->max_offset;
00533             break;
00534         case MMSPULSER_SEQ_LOG_DESC_SOFT_START_AND_END:
00535             this->seq_start = this->max_offset;
00536             this->seq_range = this->max_offset / 2;
00537             break;
00538         }
00539     }
00540     else {
00541         // sequence should be only a little part of the max_offset range
00542         switch (this->seq_mode) {
00543         case MMSPULSER_SEQ_LINEAR:
00544         case MMSPULSER_SEQ_LOG_SOFT_END:
00545             this->seq_start = this->max_offset - this->seq_range;
00546             break;
00547         case MMSPULSER_SEQ_LINEAR_DESC:
00548         case MMSPULSER_SEQ_LOG_DESC_SOFT_END:
00549             this->seq_start = this->seq_range;
00550             break;
00551         case MMSPULSER_SEQ_LOG_SOFT_START:
00552             this->seq_start = 0;
00553             break;
00554         case MMSPULSER_SEQ_LOG_SOFT_START_AND_END:
00555             this->seq_start = 0;
00556             this->seq_range/= 2;
00557             break;
00558         case MMSPULSER_SEQ_LOG_DESC_SOFT_START:
00559             this->seq_start = this->max_offset;
00560             break;
00561         case MMSPULSER_SEQ_LOG_DESC_SOFT_START_AND_END:
00562             this->seq_start = this->max_offset;
00563             this->seq_range/= 2;
00564             break;
00565         }
00566     }
00567 
00568     // get the natural logarithm
00569     if (this->max_offset >= 2) {
00570         this->max_offset_log = log(this->max_offset);
00571     }
00572     else {
00573         this->max_offset_log = 0;
00574     }
00575 
00576     return true;
00577 }
00578 
00579 double MMSPulser::getOffset() {
00580     return this->offset_curve;
00581 }
00582 
00583 bool MMSPulser::setDuration(unsigned int duration) {
00584     this->duration = duration;
00585     return true;
00586 }
00587 
00588 unsigned int MMSPulser::getDuration() {
00589     return this->duration;
00590 }
00591 
00592 unsigned int MMSPulser::getRealDuration() {
00593     return this->real_duration;
00594 }
00595 
00596 unsigned int MMSPulser::getOnAnimationCounter() {
00597     return this->onAnimation_counter;
00598 }

Generated by doxygen