Interactive Swarm Space | |||||||||||
|
Interactive Swarm Space | |||||||||||
|
Basics | ISO Flock | ISO Synth | Networking | API | Publications |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
Events Table of contents What Are Events and When Should I Use Them? What Are Events and When Should I Use Them? An event could be described as an action message sent to a unit at a specific time. Let us mention some examples for you to get a clearer picture: Some events might simply switch a unit on or off, some might set a control port (e.g. "frequency") to a certain value or generate a smooth interpolated ramp between two control values. So you could say, events control the performance of a unit by sending it commands. That said, it is also important to recognize that not all interaction that might fit this profile is actually done by events. Usually when two units interact, there ar no events passed from one to the other. Let's take for instance "frequency modulation" - a very simple version of FM synthesis consists of two oscillators, one carrier and one modulator. The modulator's output is connected to the frequency of the other, hence the name "frequency modulation". For this connection, no events are needed, because you can directly connect the output of the modulator to the frequency control port of the carrier. modulator->connect(carrier, "frequency"); However, once the two units are connected, they just keep on doing whatever they are connected to do, until the synth is stopped. It is obvious that this interaction lacks the timing criteria common to all event-driven cases. So events are appropriate in all situations where either something should happen at a pre-defined time (we shall call this instance a "score event") or where an external control source like your computer keyboard or a MIDI-Fader is controlling a unit in real-time ("real-time event"). Generating score events is rather simple. As shown by the following example syntax, you need to specify what unit will receive the event, on which port, at what time, what the event's target value is and finally how long it should take to reach this target value - linearly interpolating all values in between. All arguments of schedule() are floats, and time values are set in milliseconds. Schedule an Event on a UnitUNIT_NAME->schedule(START_TIME,"PORT_NAME", TARGET_VALUE, INTERPOLATION_TIME); An example: oscillatorUnit->schedule(5000.0f, "frequency", 4156.33f, 3500.0f); "change the frequency of the unit "oscillatorUnit" to 4156.33 Hz starting after 5.0 seconds with an interpolation time of 3.5 seconds." Schedule an Event on a ControlPort or SwitchPortSWITCH_PORT->schedule(START_TIME, TARGET_VALUE, INTERPOLATION_TIME); //SwitchPort CONTROL_PORT->schedule(START_TIME, TARGET_VALUE, INTERPOLATION_TIME); //ControlPort example: oscillatorUnit->switchPort("active")->schedule(2000.0f, 0.0f, 0.0f); oscillatorUnit->controlPort("frequency")->schedule(2500.0f, 112.5f, 1000.0f); "2.0 s after synth start, set the SwitchPort to inactive (0.0), don't interpolate (second 0.0)." Remark: In almost all cases of SwitchPorts, interpolation times greater than 0 make absolutely no sense as you can see in the example above. Schedule an Event on a Generic EventTarget (Rare)Synth::get().eventManager().createEvent(START_TIME, EVENT_TARGET, TARGET_VALUE, INTERPOLATION_TIME); a real-life example: Synth::get().eventManager().createEvent( 10000.0f, someUnit->switchPort("active"), 0.0f, 0.0f); This approach is needed in cases where you don't know what the EventTarget is, be it a SwitchPort or a ControlPort. You can use the above syntax to create real-time events, simply set START_TIME to 0.0f or less, for them to be processed as soon as possible. However this approach is almost exclusively plausible when you create your own units that attempt to send events. In most cases when talking about real-time events, you probably mean the use of real-time controls like MIDI or OSC in your synth. This has been documented in our External Control section. Another juicy feature of ISO is of course the possibility to control your synthesis patch using real time data from your ISOFlock. The Networking chapter of ISOFlock describes the way to get there. ATEUnit(s) - Audio To Event ConversionThis is the base class of all units concerned with observing an audio stream and sending out events to an other unit's EventTarget (ControlPort or SwitchPort) either at regular intervals or when specific conditions in the audio stream occur. Remember that you can also use this unit with ControlPort data streams, because they are being performed at audio-rate as well! EventTargets are initially set when calling the constructors. In fact, all ATEUnit (and subclasses thereof) have the same constructor syntax: The last constructor argument is always a QVector of EventTargets: PinkNoise* noise = new PinkNoise(); WaveTableOscil* carrier = new WaveTableOscil("sinewave"); //sine wave oscil QVector<event::EventTarget*> targets = QVector<event::EventTarget*>(); targets.append(carrier->controlPort("frequency")); // get ControlPort* pointer // (which is by inheritance // an EventTarget) ATEUnit* audio2event = new ATEUnit(targets); // connects audio2event with carrier's // "frequency" ControlPort Notice that there is no connect() Link between the ATEUnit and the carrier. This event-based connection is established by calling the constructor of ATEUnit along with the carrier's EventTarget - here: the ControlPort "frequency" of the sine wave oscillator.
Remark on ATEConditionalUnit: You have to set the conditional statement as operator/argument SwitchPort settings. The operator index represents the the conditional operator that will be used:
The argument SwitchPorts describe the right side of the conditional statement. So if you were to check in regular intervals, if your control data stream is greater than 150.0, you would set the index and operator SwitchPort as follows: myConditionalUnit->set("operator", 4.0); // ">" myConditionalUnit->set("argument", 150.0); You probably noticed, there is a second operator/argument set. This second condition is evaluated only if the first condition is evaluated as true. In pseudo code: if ( (audio_data operator argument) AND (audio_data operator2 argument2) ) { send audio_data } This is useful, for example if you would like to check if your audio stream data is within a certain range - around zero perhaps: myConditionalUnit->set("operator", 4.0); // ">" myConditionalUnit->set("argument", -0.001); // lower boundary myConditionalUnit->set("operator2", 2.0); // "<" myConditionalUnit->set("argument2", 0.001); // upper boundary As already stated, these two operator/argument conditions are connected via a logical AND operator (&&), in cases where you need a logical OR operator, we suggest, you simply create a second unit using the same audio input and sending events to the same EventTarget. Pitfall: interval SwitchPort Setting: You might expect, that an event is sent whenever a condition is met. This is however only true when interval is set to 0. Meaning your audio stream is evaluated each sample. This is of course a little expensive and might in a worst-case scenario lead to your ATEConditionalUnit sending out events each audio sample!! ETAUnit(s) - Event To Audio ConversionLike in the ATEUnit family, ETAUnit is the base class for all event-to-audio converter units. The base classes job is to set it's output audio stream to the event value received by it's SwitchPort "event".
Remark on ETACustomFadeUnits: You can either create your own shape by creating a Channel object and filling it with data points that represent the shape of your fade. Or select one of the default shapes. In order to create smooth fades, your fade shape must start at 0.0 and end with 1.0. These fade shapes are scaled and stretched according to your SwitchPort settings and received event values. ETACustomFadeUnit* fadeUnit = new ETACustomFadeUnit(); ... uses the default sigmoid shape. The following statement is equivalent: Channel* fadeShape = WaveTableManager::get().getWaveTable("sigmoidwave"); ETACustomFadeUnit* fadeUnit = new ETACustomFadeUnit(fadeShape); |
Last updated: May 15, 2024 |