Next: , Previous: Writing a new Timer, Up: Using and Extending the Scheduler


9.6.3 Writing a new Event

Suppose we want to fade the volume down to silence using a Gain MarSystem. We could accomplish this using the scheduler and several EvValUpd events. Assuming the gain control is at 1.0 to begin we just issue 10 EvValUpd events each with a progressively lower volume amount: 0.9, 0.8, 0.7, ... and each at a time that is a little bit later than the previous. This is messy and repetitive. Why not make a new event, called EvRampCtrl, that encapsulates this behaviour.

We start by defining a new EvRampCtrl class that inherits from the EvEvent class. We define a constructor that takes a MarSystem to act upon, a control to modify, a starting value, an ending value, and a step amount. The implicit assumption is that this event will only work on values of type mrs_real so that values we supply must all be of the correct type. The header and cpp files are supplied below.

We will need an additional variable to save the current adjustment value. Also, the event will need to repeat so we will maintain a repeat flag that is true until we pass the end value. We override the repeat() method of EvEvent to return the current value of the repeat flag. The required variables are shown in the header file below.

     #ifndef MARSYAS_EV_RAMPCTRL_H
     #define MARSYAS_EV_RAMPCTRL_H
     
     #include <string>
     
     #include "MarControl.h"
     #include "EvEvent.h"
     #include "TmControlValue.h"
     
     namespace Marsyas
     {
       class MarSystem; // forward declaration
     
       class EvRampCtrl : public EvEvent {
       protected:
         MarSystem* target_;
         std::string cname_;
         double start_, end_, value_, rate_;
         bool repeat_flag_;
     
       public:
         // Constructors
         EvRampCtrl(MarSystem* m, std::string cname, double start, double end, double rate);
         EvRampCtrl(EvRampCtrl& e);
         virtual ~EvRampCtrl();
     
         virtual EvRampCtrl* clone();
     
         // Set/Get methods
         void set(MarSystem* ms, std::string cname, double s, double e, double r);
     
         bool repeat() { return repeat_flag_; };
     
         // Event dispatch
         void dispatch();
     
       };
     
     }//namespace Marsyas
     
     #endif
     

Figure 9.7: EvRampCtrl event header file example.

     #ifndef MARSYAS_EV_RAMPCTRL_H
     #define MARSYAS_EV_RAMPCTRL_H
     
     #include <string>
     
     #include "MarControl.h"
     #include "EvEvent.h"
     #include "TmControlValue.h"
     
     namespace Marsyas
     {
       class MarSystem; // forward declaration
     
       class EvRampCtrl : public EvEvent {
       protected:
         MarSystem* target_;
         std::string cname_;
         double start_, end_, value_, rate_;
         bool repeat_flag_;
     
       public:
         // Constructors
         EvRampCtrl(MarSystem* m, std::string cname, double start, double end, double rate);
         EvRampCtrl(EvRampCtrl& e);
         virtual ~EvRampCtrl();
     
         virtual EvRampCtrl* clone();
     
         // Set/Get methods
         void set(MarSystem* ms, std::string cname, double s, double e, double r);
     
         bool repeat() { return repeat_flag_; };
     
         // Event dispatch
         void dispatch();
     
       };
     
     }//namespace Marsyas
     
     #endif

Figure 9.8: EvRampCtrl event header file example.

The main logic for our event is contained in the dispatch method. Basically, we check to see if we have passed the end value during ramping and if so set the repeat flag to false. The next time that the scheduler checks to see if the event repeats the scheduler will read the false value and delete the event. If we have not passed the end value then we set the specified control to the current ramp value and decrement the current value by the ramp amount. The scheduler will see that the event is to be repeated, it will read the repeat rate amount, and repost the event to the queue.

     #include "EvRampCtrl.h"
     #include "MarSystem.h"
     
     using namespace std;
     using namespace Marsyas;
     
     EvRampCtrl::EvRampCtrl(MarSystem* m, std::string cname, double start, double end, double rate) : EvEvent("EvRampCtrl","rc")
     {
         set(m,cname,start,end,rate);
     }
     EvRampCtrl::EvRampCtrl(EvRampCtrl& e) : EvEvent("EvRampCtrl","rc")
     {
         set(e.target_,e.cname_,e.start_,e.end_,e.rate_);
     }
     
     EvRampCtrl::~EvRampCtrl() { }
     
     EvRampCtrl*
     EvRampCtrl::clone() { return new EvRampCtrl(*this); }
     
     void
     EvRampCtrl::set(MarSystem* ms, string cname, double start, double end, double rate)
     {
         target_=ms; cname_=cname;
         start_=start; end_=end; rate_=rate;
         value_=start_; repeat_flag_=true;
     }
     
     void
     EvRampCtrl::dispatch()
     {
         if (target_ !=NULL) {
           cout << "target_->updctrl(" << cname_ << ", " << value_ << ")\n";
           if(value_<end_) repeat_flag_=false;
           else {
             target_->updctrl(cname_,value_);
             value_ = value_ - rate_;
           }
         }
     }
     
     

Figure 9.9: EvRampCtrl event C++ source file example.

The fade1 method shows our EvRampCtrl event in action. We set the repeat rate of the event to 0.2 seconds.

     void fade1() {
       MarSystemManager mng;
     
       MarSystem* series = mng.create("Series", "series");
       series->addMarSystem(mng.create("SineSource", "src"));
       MarSystem* gain = mng.create("Gain", "g");
       series->addMarSystem(gain);
       series->addMarSystem(mng.create("AudioSink", "snk"));
       series->updctrl("AudioSink/snk/mrs_bool/initAudio", true);
       series->updctrl("SineSource/src/mrs_real/frequency",440.0);
       series->updctrl("Gain/g/mrs_real/gain",1.0);
     
       EvRampCtrl* ev = new EvRampCtrl(gain,"mrs_real/gain",1.0,0.0,0.05);
       ev->set_repeat(Repeat("0.2s"));
     
       series->updctrl(TmTime("TmSampleCount/Virtual","2s"),ev);
     
       while(true) {
         series->tick();
       }
     }

Figure 9.10: Program using the EvRampCtrl example event.

9.6.3.1 Expression Events

For a large number of events there is commonality. The Expression syntax was developed to allow the creation of events without having to code new custom event classes. In order to accomplish this there is a built in compiler for the syntax that is invoked when supplying the expression as a string to the Ex class.

     #include <stdio.h>
     #include "MarSystemManager.h"
     #include "EvExpr.h"
     
     using namespace std;
     using namespace Marsyas;
     
     void sched1() {
       MarSystemManager mng;
     
       MarSystem* fanin = mng.create("Fanin", "fanin");
       fanin->addMarSystem(mng.create("SineSource", "src1"));
       fanin->addMarSystem(mng.create("SineSource", "src2"));
       fanin->updctrl("SineSource/src1/mrs_real/frequency",3000.0);
       fanin->updctrl("SineSource/src2/mrs_real/frequency",1000.0);
     
       MarSystem* series = mng.create("Series", "series");
       series->addMarSystem(fanin);
     
       series->addMarSystem(mng.create("AudioSink", "dest"));
       series->updctrl("AudioSink/dest/mrs_bool/initAudio", true);
     
     #if 0
       // using aliases makes this a little more readable, see the next bit
       EvExpr* e = new EvExpr(series,
         Ex("Fanin/fanin/SineSource/src1/mrs_real/frequency << 120. + 3000. * R.rand(), \
             Fanin/fanin/SineSource/src2/mrs_real/frequency << 120. + 800. * R.rand(),"
            "'src1='+Fanin/fanin/SineSource/src1/mrs_real/frequency+ \
             ' src2='+Fanin/fanin/SineSource/src2/mrs_real/frequency+'\n'>>Stream.op"),
         Rp("true"));
     
     #else
       EvExpr* e = new EvExpr(series,
             // First line to Ex will be the init expression, run once, when event is posted
         Ex("Fanin/fanin/SineSource/src1/mrs_real/frequency >> @freq1, \
             Fanin/fanin/SineSource/src2/mrs_real/frequency >> @freq2 ",
            // Second line to Ex is the expression, repeated each time the event is posted
            "freq1 << 120. + 3000. * R.rand(),\
             freq2 << 120. + 800. * R.rand(),\
             'src1=' + freq1 + ' src2=' + freq2 + '\n' >> Stream.op"),
         Rp("true"));
     #endif
     
       // set event to repeat every 1/4 second
       e->set_repeat(Repeat("0.25s"));
       // post the event
       series->updctrl(TmTime("TmSampleCount/Virtual","0s"), e);
     
       while(true) series->tick();
     
       delete series;
     }

Figure 9.11: Random sine waves using the Expression syntax.