Interactive Swarm Space | |||||||||||
|
Interactive Swarm Space | |||||||||||
|
Basics | ISO Flock | ISO Synth | Networking | API | Publications |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Basic Audio Input and Synthesis Units Table of Contents InputFile Unit Using the InputFile Unit you can read sound files and use them for playback in your ISO Synth Patch. Compatible sound file formats are: AIFF, WAV and RAW. The number of audio channels is equal to the number of audio channels of the used sound file and can be theoretically arbitrarily high. All sample rates and bits per sample (bit rates) are acceptable, as long as the audio hardware in use supports them. Alternatives: InputFile will directly stream a file from your hard disk. This is appropriate for realtime situations, but if you want to build a sample player with prerecorded sounds, we advice you to use either SampleUnit or TriggerSampleUnit. While SampleUnit is great for storing a bunch of samples and switch amongst them at runtime (even with crossfades), TriggerSampleUnit is a multi-channel fixed sample unit, that receives a trigger signal on its control port, making it possible to produce audio-timing accurate. This grooves can be generated using the RhythmUnit. For details please check out these classes in our Doxygen documentation. Control Ports
transpose: This is a factor which influences the playback speed. So a transpose value of 0.5 leads to half the playback speed, which translates into the audio file being played one octave below its original frequency. Switch Ports
play: trigger a playback routine (1.0) - or stop playback by sending this SwitchPort a value of 0.0. looping: if this is set to 1.0, the sound file will loop endlessly. playStart: Marks the absolute time in your sound file at which it will start playback. Neat, if you would like to "scroll around" in your sample material - or remove the attack. However clicking is likely to occur! playStop: Simultaneous to the behavior of playStart, this switch port marks the time at which the sound file will stop playing. Works as well when "looping" is active. Example Syntax InputFile* inputFile = new InputFile("/<directory>/mySound.aiff"); inputFile->set("transpose", 0.5); inputFile->set("amplitude", 0.8); inputFile->connect(outputUnit); ISOSynth makes it very easy to record the audio output of your patch to a file - or the output of any of your units for that matter. Just keep in mind that the operation of writing to your disk might decrease the patch's performance considerably, sometimes even introducing drop-outs (we keep working on that). However these dropouts will not be present in the audio file! OutputFile will start writing to disk as soon as the synth is started - remember the Synth::get().start(); method from the Introduction chapter? Example Syntax Noise* exampleUnit = new Noise(); OutputFile* fileOutUnit = new OutputFile("/<directory>/mySound.aiff"); // where to save // the recording exampleUnit->connect(outputUnit); //play the exampleUnit unit exampleUnit->connect(fileOutUnit); //write the exampleUnits audio stream to disk This unit is similar to the InputUnit for the fact that its main purpose is the playback of audio files (samples). However SampleUnit is much more powerful than the InputUnit, because it can be assigned an arbitrary number of audio files to choose from. A SwitchPort called "sample" will let you choose at runtime which of these samples should be playing. To avoid clicks, a further SwitchPort "sampleFadeDuration" will let you specify a crossfade time span. Please keep in mind, that you shouldn't use this crossfade feature if your sample material contains strong attacks because in the crossfade process the beginning of a sample is being faded in to avoid amplitude clipping. For the same reason the SwitchPort "loopFadeDuration" should be used with caution as well. It serves the purpose of creating smoother loops. There are two situations where this comes in handy: 1) Your original sound file is not prepared to be loopable (e.g. doesn't start and end with a zero-crossing) and 2) you would like to shift start and end points during playback using the "playStart" and "playEnd" SwitchPort. Shifting this edge points will not snap them to the position of the nearest zero-crossing! Control Ports
transpose: This is a factor which influences the playback speed. So a transpose value of 0.5 leads to half the playback speed, which translates into the audio file being played one octave below its original frequency. Switch Ports
play: set active/inactive looping: if this is set to 1.0, the sound file will loop endlessly. loopFadeDuration: crossfade time in ms used when looping sampleFadeDuration: crossfade time in ms used when changing from one sample file to another. playStart: Marks the absolute time at which the sound file will start playing playStop: Simultaneous to the behavior of playStart, this switch port marks the time at which the sound file will stop playing. Example Syntax SampleUnit* sample = new SampleUnit("/<directory>/mySound.aif"); sample->addSample("/<directory>/anotherSample.aif"); sample->set("transpose", 0.5); sample->set("amplitude", 0.8); sample->set("sampleFadeDuration", 20.0); //20 ms crossfade sample->connect(outputUnit); This wavetable oscillator can be used together with a set of default wavetables ("sinewave", "squarewave", "sawtoothwave") or with a user defined wavetable which can be read from a local text file. Per default, there is no interpolation applied. However if you need linear interpolation for your wavetable, add "LINEARINTERPOL" to your constructor arguments of the WaveTableOscil constructor. There are two child units of the WaveTableOscil, BLPulseGen ("band-limited pulse generator") and DCSPulseGen ("dynamically controlled spectrum pulse generator"). Control Ports
frequency: cycles per second, please note that frequencies beyond half of your sample rate will create aliasing (nyquist theorem). phase: the default setting of 0.0 translates into no phase shift at all. 1.0 translates into a full cycle of phase shift (which of course is equivalent to no shift at all). Only positive values for phase are allowed! offset: dc offset value, 0.0 (default) means no offset. Example Syntax WaveTableOscil* osc1 = new WaveTableOscil("sinewave"); //built-in WaveTableOscil* osc2 = new WaveTableOscil("mywavetable", "/<directory>/mytable.txt", LINEARINTERPOL); //user defined wavetable with // linear interpolation osc1->set("frequency", 440.0); osc1->set("amplitude", 0.8); osc2->set("frequency", 1200.0); osc2->set("amplitude", 0.6); osc1->connect(outputUnit); osc2->connect(outputUnit); This unit creates white noise and thus a uniform distribution in the frequency spectrum. There are three alternatives to the Noise unit, namely NoiseH ("sample and hold noise") , NoiseI ("interpolated sample and hold noise") and StochasticNoise ("stochastically distributed noise"). These will be described later. Control Ports
frequency: Only applicable to NoiseH and NoiseI! cycles per second, please note that frequencies beyond half of your sample rate will create aliasing (nyquist theorem). phase: the default setting of 0.0 translates into no phase shift at all. 1.0 translates into a full cycle of phase shift (which of course is equivalent to no shift at all). Only positive values for phase are allowed. offset: dc offset value, 0.0 (default) means no offset. -1.0 means maximal negative offset, 1.0 means maximal positive offset. Please note that offsetting a wavetable can lead to audible clicks when switch the table on and off Example Syntax Noise* whiteNoise = new Noise(); whiteNoise->connect(outputUnit); Alternative Noise Units (PinkNoise, NoiseH, NoiseI and StochasticNoise) PinkNoise PinkNoise is also called 1/f Noise, meaning higher frequencies will have lower amplitudes. Pink Noise has no additional Control- or SwitchPorts. NoiseH NoiseH creates "sample and hold" noise, it creates new random samples at regular intervals. During the intervals, the sample doesn't change. sample and hold noise Control Ports
NoiseI NoiseI also creates "sample and hold" noise but it interpolates between the random samples. Likewise a frequency control value defines the rate at which new samples are set as interpolation targets. interpolated sample and hold noise Control Ports
StochasticNoise Unit that distributes its noise according to a density function - also known as probability distribution. This density function is passed to the StochasticNoise unit as an array of sample values representing the edge points of a piece-wise linear distribution. Creating such an array and passing it to the StochasticNoise unit can be done like this: sample* distribution = new sample[4]; distribution[0] = 0.33333; distribution[1] = 1.0; distribution[2] = 0.1; distribution[3] = 0.0; StochasticNoise* noise = new StochasticNoise(new math::Vector<sample>(4, distribution)); noise->connect(outputUnit); Here is what the resulting sound wave might look like. stochastic noise fitting a piece-wise linear distribution DelayUnit (and MultiDelayUnit) The delay unit will, all but surprisingly, play back a received audio signal with a specified delay. Control Ports
delay: default delay time is 0.0 ms (no delay). phase: the default scale factor is 1.0 - the received audio signal by the delay unit will be scaled with this factor.
When you construct a DelayUnit, you have to call its constructor with the ringbuffer size argument. It specifies the size of the buffer (in samples) and therefore the maximal delay length: DelayUnit* myDelayUnit = new DelayUnit(88200); // buffer size = 88200 samples == 2 second Here, a ringbuffer size of 2*44100 corresponds to exactly two second, because the default sampling rate is 44.1 kHz. As with most other units, you can also provide an argument that depicts the number of channels in this unit: DelayUnit* myStereoDelayUnit = new DelayUnit(2, 44100); // 2-channel delay unit, 1 second buffer size From what you know so far, we believe it is not necessary to write a complete sample code here. Instead we will go one step further and provide you with the syntax of an other unit that makes itself use of several DelayUnits, the MultiDelayUnit. The following patch attempts to simulate the sound of an object bouncing off a surface several times. Each time the object hits the surface it looses momentum due to friction, thus decreasing both amplitudes and time intervals between each hit. This patch shows, that a unit can in fact contain other units by acting as a manager to them. In this MultiDelayUnit example, the manager unit makes sure that all contained DelayUnits use the same buffer (a ring buffer) and mixes all delayed signals together. The control ports of the contained DelayUnits are accessible using the following port names: delay0, delay1, delay2, ..., delayN scale0, scale1, scale2, ..., scaleN where N corresponds with the number of taps (delays) used in your MultiDelayUnit. Example Syntax InputFile* inputFile = new InputFile("/<directory...>/tom.aif"); // short percussive sound MultiDelayUnit* mulDelayUnit = new MultiDelayUnit(1, 100000, numDelays); // max delay roughly 2.5 secs int numDelays = 13; //augment this for longer trails inputFile->setLooping(1.0f); mulDelayUnit->setControlType(FRAMECONTROL); // calculate and set individual delay times and amplitudes float initialDelay = 300.0f; float tempDelay = 0.0f; // initial delay time float tempScale = 0.8f; // initial amplitude for (float d = 0.0f; d < numDelays; d+=1.0f) { tempDelay = tempDelay + (initialDelay / (d+1.0f)); // increase delay by an interval that's becoming progressively smaller mulDelayUnit->set(String("delay%1").arg(d), tempDelay); // set "delay0" to delay13" to this delay time (tempDelay) mulDelayUnit->set(String("scale%1").arg(d), tempScale*=0.8f); // set "scale0" to "scale13", each time tempScale gets multiplied by 0.8 } inputFile->connect(mulDelayUnit); mulDelayUnit->connect(m_output); Instead of setting all delay time and scale control values individually, we used a for-loop to set them. Each iteration, tempDelay adds an interval to its previous value that gets progressively smaller. Amplitude gets smaller each time too, but it is in fact so simple to decrease this value, that we can do it right at the spot where we set the scale control value. tempScale*=0.8f is just a short notation for: tempScale = tempScale * 0.8f which translates into: Decrease tempScale by the factor 0.8 each time you read this instruction, and because we are inside a loop, tempScale will get smaller and smaller. You might have been confused by the following argument: String("scale%1").arg(d). This argument will compose a string that will replace the placeholder %1 with the value of the argument variable d, which happens to be the loop increment index (which iterates from 0 to 13, the number of internal delay units used). So if d = 5 (6th iteration), String("scale%1").arg(d) will produce the String "scale5". Using this dynamic String composition along with setting the delay times and amplitudes accordingly, we can with no effort create a multidelay that contains 200 or much more delay units. |
Last updated: May 15, 2024 |