Bookshelf Contents Previous Next Glossary Index Search

Common Module Methods

There are a few remaining module methods that are common to all module types but have not been described elsewhere.

Each module has an Open() and Close() method. The Open() method is called when sampling is first started. The Close() method is called when sampling or recording is turned off. During the calculating modes, modules are opened when calculation begins and closed when it ends. Many source modules initiate contact with external hardware during the Open() method phase. The Open() method returns a value of TRUE or FALSE to indicate if a module opened properly. A pipeline will continue on to sampling, only if all the modules in the pipeline return TRUE.

All modules also have a PreSample() and PostSample() method. The PreSample() and PostSample() bracket periods of sampling modes. For example, when MotionSampler transitions from plain sampling to recording, the PostSample() methods are called and then the PreSample() methods are called. PreSample() and PostSample() are short opportunities for modules to adjust to plain sampling versus recording.

Filter Module Methods

Filters are the simplest of the three module types. They are special functions which take a number of input parameters, perform a calculation, and produce a number of output parameters.

The key filter method is Evaluate(). The Evaluate() method is where the filter module performs its action. Evaluate() is used as follows:

void Evaluate(PDLOutlet* outlet);

The Evaluate() method is called indirectly by other modules below a filter in the pipeline. When a module gets a value from an inlet, a dependency stamp is provided. If the dependency stamp matches the one associated with the inlet's current value, the value is returned. If the stamp doesn't match, the inlet requests the value of the outlet it is connected to. This request causes the Evaluate() method of the module owning the connected outlet to be called.

The following example shows the Evaluate() method of a filter module called Clip. The action of this module is to make sure the input value stays within a given range. Evaluate() is called by another module whose outlet is connected to one of clip's inlets. The Clip module uses the dependency stamp of the outlet argument to request the latest value of the Clip module's inlet. This call can cause the Evaluate() method of a module up the pipeline to be called. Eventually an up-to-date value is returned which Clip adjusts if necessary and sets the value of its outlet.

void
Clip::Evaluate(PDLOutlet* outlet)
{
	float f;
	// Get the input value from inlet f_in.
	f_in->Value(f, outlet->Stamp());
	// Clamp the value to satisfy (min <= f <= max).
	if (f < min)
		f = min;
	else if (f > max)
		f = max;
	// Assign the new value to the outlet f_out.
	f_out->SetValue(f);
}

Since the Clip module has only one outlet, the outlet argument to Evaluate() always points to the same outlet as f_out.

Source Module Methods

The primary role of a source module is to sample data from any data generating source external to the MotionSampler pipeline and make it available for processing by pipeline modules downstream. The data can come from almost anything: a file, a function, or an external piece of hardware which measures some quantity. The purpose of the source module is to translate this data into a form other pipeline modules can understand.

Source modules have three responsibilities:

  1. sampling data whenever MotionSampler requests it, usually many time a second.
  2. accumulating samples over time whenever MotionSampler is recording.
  3. providing meaningful data to other modules connected to its outlets when they ask for it.

MotionSampler calls the Sample() method of every source module in the pipeline whenever it samples data. To receive this request, redefine the Sample() method in your source module as follows:

void Sample(PDLTime frametime, PDLTime realtime);

The frametime parameter indicates the current time on the MotionSampler shuttle. The realtime parameter is the time since the last PreSample() call.

PDLSource does most of the work involved in accumulating data samples while recording. However, you provide the size in bytes of a single sample by using the SampleSize() method as follows.

void SampleSize() { return sizeof(int); }

Changes to the value returned from SampleSlice() have no affect while the module is open.

A source module allocates a new sample block by calling NewSampleSlice(). This is like calling malloc() with the size returned by SampleSize(). Calls to NewSampleSlice() are expected to be made inside the module's Sample() method. Make sure you call NewSampleSlice() every time the Sample() method is called. If your module can detect if a sample is good or bad, mark it in the sample data. Do not skip calls to NewSampleSlice().

Example: Using Sample() and NewSampleSlice()

The following example shows how Sample() and NewSampleSlice() are used:

void ExampleSrc::Sample(PDLTime, PDLTime)
{
	// allocate space for this sample
	int* samp_value = (int*) NewSampleSlice();
	// assign a value to the sample
	*samp_value = choose_value_from_thin_air();
}

Sources have an Evaluate() method identical to the Evaluate() method of filters.

Instead of getting input values from inlets as filters do, sources extract values from their internal sample arrays using the SampleSlice() method as follows:

void* SampleSlice(int stamp);

The dependency stamp in the outlet argument is used to choose the correct sample as follows:

void Example::Evaluate(PDLOutlet* outlet)
{
	// get a sample and pass it down the pipeline
	int* s = (int*) SampleSlice(outlet->Stamp());
	if (s)
		my_outlet->SetValue(*s, outlet->Stamp());
}

Source modules have a special method called SampleAdjust() which can operate on the whole array of samples just before they are used for evaluation at the end of recording. SampleAdjust() is used as follows:

void
Example::SampleAdjust(int count, PDLTime*, PDLTime*)
{
	for (int i = 0; i < count; i++)
	{
		int* s = (int*) SampleSlice(i);
			if (*s == 0)
				*s = fix_the_value();
	}
}

Sink Module Methods

Sink modules complement source modules by exporting data from the pipeline. Many sink modules insert data into animated channels which affect playback in MotionSampler and can be saved to Alias wire format files. Other sinks save data directly to files or print information to a UNIX shell window.

Sinks perform three different kinds of evaluation:

  1. calls the EvaluateLive() method in every sink module as follows during live sampling, just before MotionSampler updates the scene displays:
    void EvaluateLive(int stamp);
    

    In EvaluateLive(), the sink receives up-to-date values from its inlets and uses these values to update anything you can see live. If the sink is saving values to a file, there is no reason to do anything here. But if the sink changes information in the MotionSampler scene, it must update it with temporary values. EvaluateLive() should never permanently save its data. This evaluation is only for live feedback. Those sinks which affect MotionSampler animation channels insert their output in a temporary location which affects the display of the scene but does not alter existing channels.

  2. resolves dependencies in inlets and outlets using the stamp argument's value. For every new pipeline evaluation, the stamp changes. If an outlet needs to provide its value more than once in a single pipeline evaluation, only the first time causes further evaluations in the pipeline. Use this stamp as follows when you get values from the sink module's inlets:
void
PrintFloat::EvaluateLive(int stamp)
{
	float f;
	inlet_f->Value(f, stamp);
	cerr << Name() << " " << f << endl;
}
  • calls the Evaluate() method repeatedly when MotionSampler finishes recording until all the samples are evaluated. This Evaluate() method is different from the Evaluate() methods for filters and sources and is used as follows:
    void Evaluate(int idx, int stamp, PDLTime frametime);
    

    Evaluate() methods in sinks which affect MotionSampler animation channels insert their values into the channels at a location determined by frametime as follows:

    void PrintFloat::Evaluate(int idx, int stamp, PDLTime frametime) { float f; inlet_f->Value(f, stamp); cerr << Name() << "[" << idx << "]" << " " << f << " @ " << frametime << endl; }

    Sometimes a sink needs to evaluate a whole range of samples in one block, for example if you want to perform smoothing or interpolation. The methods PreEvaluate() and PostEvaluate() can help with this. PreEvaluate() and PostEvaluate() are used as follows:

    void PreEvaluate(int count, PDLTime* frametimes);
    void PostEvaluate(int count, PDLTime* frametimes);
    

    Before a sink's Evaluate() method is called repeatedly, its PreEvaluate() method is called. The count argument is the total number of sample slices taken and the number of times Evaluate() will be called. The timestamp of each sample slice is contained in the array frametimes.

    When the Evaluate() calls are done, PostEvaluate() is called with arguments identical to those of PreEvaluate().

    Sink modules use these methods by allocating a temporary array for its values during PreEvaluate(). Sinks then fill in this array, one value at a time, during the Evaluate() calls. The idx argument to Evaluate() can be used as an index into the array.

    During PostEvaluate(), the sink has access to all the values and can apply smoothing or other operations before exporting the data.

    Sinks evaluate snapshots as follows:

    void EvaluateSnapshot(int stamp, PDLTime frametime);
    

    The EvaluateSnapshot() method is implemented as if MotionSampler recorded just one frame. In some sink modules you can implement EvaluateSnapshot() by calling Evaluate().



    Bookshelf Contents Previous Next Glossary Index Search

    [email protected]
    Copyright © 1998, Alias|Wavefront, a division of Silicon Graphics Limited. All rights reserved.