Next: , Previous: Using the Scheduler, Up: Using and Extending the Scheduler


9.6.2 Writing a new Timer

Timers control the scheduling of events with respect to some control rate. When writing new timers we don't want to have to worry about this scheduling activity. Instead we wish to define new control rates. No problem. All we have to do is inherit from the TmTimer class. For lack of a more creative idea this section will explain a slightly modified TmSampleCount timer.

When creating new timers we first create a class that inherits from TmTimer. This class is placed in the src/marsyas directory and added to the build system. Our example, requires a constructor that takes a MarSystem to read the time from. Our timer will read the "mrs_natural/onSamples" control to find out how long the interval of time is, in samples, between successive ticks. This will be used to advance our timer. On construction we call setReadCtrl which gets the control as a MarControlPtr for faster access than calling getctrl on each tick.

Whenever the scheduler is ticked, it will tick each of the timers it controls. These timers will call their readTimerSrc() method to advance their clocks. Our readTimerSrc method will read the onSamples control and return this value (elapsed time since last tick). Our timer is now operational.

Timers may also require the definition of special time units. In the case of real time we may want to define what milliseconds or seconds mean with respect to sample count. To do this we must override the mrs_natural intervalsize(string interval) method. For our timer, we will simply call the static method time2samples(string) defined in Conversions and pass it the string and the current sample rate. Now our timer supports time defined in samples, microseconds, milliseconds, seconds, minutes, and hours.

     #ifndef MARSYAS_TM_SAMPLE_COUNT_H
     #define MARSYAS_TM_SAMPLE_COUNT_H
     
     #include "TmTimer.h"
     #include "MarControlValue.h"
     #include "MarSystem.h"
     
     namespace Marsyas
     {
       // forward declaration of MarSystem allows Scheduler.getctrl("insamples")
       // for scheduler count
       class MarSystem; // forward declaration
     
       class TmSampleCount : public TmTimer {
       protected:
         MarSystem* read_src_;
         MarControlPtr read_ctrl_;
     
       public:
         // Constructors 
         TmSampleCount(MarSystem*);
         TmSampleCount(const TmSampleCount& s);
         virtual ~TmSampleCount();
         TmTimer* clone();
     
         void setReadCtrl(MarSystem* ms);
         mrs_natural readTimeSrc();
         mrs_natural intervalsize(std::string interval);
     
         virtual void updtimer(std::string cname, TmControlValue value);
     };
     
     }//namespace Marsyas
     
     #endif
     

Figure 9.3: TmSampleCount header file example.

     #include "TmSampleCount.h"
     #include "MarSystem.h"
     #include "Scheduler.h"
     
     using namespace std;
     using namespace Marsyas;
     
     TmSampleCount::TmSampleCount(MarSystem* ms) : TmTimer("TmSampleCount","Virtual")
     {
         setReadCtrl(ms);
     }
     TmSampleCount::TmSampleCount(const TmSampleCount& s) : TmTimer(s)
     {
         setReadCtrl(s.read_src_);
     }
     TmSampleCount::~TmSampleCount(){ }
     
     TmTimer* TmSampleCount::clone()
     {
       return new TmSampleCount(*this);
     }
     void TmSampleCount::setReadCtrl(MarSystem* ms)
     {
         read_src_=ms;
         if (read_src_!=NULL) read_ctrl_=read_src_->getctrl("mrs_natural/onSamples");
     }
     
     mrs_natural TmSampleCount::readTimeSrc()
     {
         if (read_src_==NULL) {
             MRSWARN("TmSampleCount::readTimeSrc()  time source is NULL");
             return 0;
         }
         mrs_natural m = read_ctrl_->to<mrs_natural>();
         return m;
     }
     mrs_natural TmSampleCount::intervalsize(string interval)
     {
         return (read_src_==NULL) ? 0 : time2samples(interval,read_src_->getctrl("mrs_real/israte")->to<mrs_real>());
     }
     void
     TmSampleCount::updtimer(std::string cname, TmControlValue value)
     {
         bool type_error=false;
         if (cname=="MarSystem/source") {
             if (value.getType()==tmcv_marsystem) { setReadCtrl(value.toMarSystem()); }
             else type_error=true;
         }
         else
             MRSWARN("TmSampleCount::updtimer(string,TmControlValue)  unsupported control");
         if (type_error)
             MRSWARN("TmSampleCount::updtimer(string,TmControlValue)  wrong type to "+cname);
     }

Figure 9.4: TmSampleCount C++ source file example.

9.6.2.1 Updating timers at run-time

The TmTimer class also supports communication through the updtimer method. An example of this is shown in the TmSampleCount timer above. This is not necessary for the operation of our timer but we might want to support the changing of timer parameters at run-time through the updtimer interface. To do this we simply override the void updtimer(std::string cname, TmControlValue value) method. We can now parse the supplied timer control path and set the appropriate value.

Timer control paths have the same format as MarSystem controls. For example, our timer could be accessed through:

     marsys->updtimer("TmSampleCount/Virtual/MarSystem/source",marsys);

Figure 9.5: Setting timer parameters using the updtimer call.

The TmControlValue defines the allowable values that may be passed to timers. These values are limited to: float, double, int, long, std::string, const char*, bool, MarSystem*. However, one could always modify the TmControlValue class to add additional types. Be careful of values clashing such as NULL pointers and the integer value 0.

9.6.2.2 Timer Factory

New timers can be added to the Timer Factory by modifying TmTimerManager. Doing so allows the use of the addTimer method in MarSystem by simply specifying the type/name of the timer as opposed to creating a new timer.

     net->addTimer("TmSampleCount","counter");

Figure 9.6: Adding a new timer to MarSystem net using the timer factory.

The instructions for adding new timers to the factory are contained in TmTimerManager and repeated here. Basically, a map is created from "TimerName"=>TimerConstructorObject. This makes it possible to use a map for fast access to specific timers and it prevents having to instantiate each Timer type at startup. The constructor object simply wraps the new operator so that it constructs objects only when requested.