Wherein we explore the use of classical mechanics to generate control events for use in your musical compositions. Right-click to copy these examples to your workspace.
Physical simulation engines have played an important role in science and entertainment for as long as there have been computers. The Box2D node builds atop the DbBox2D chugin and the Box2D engine to bring the power of physical simulation to your music compositional fingertips.
Imagine assigning a musical role to each of these bouncing shapes. Let's say we trigger a note when a yellow square hits a blue circle. Next, when a note is triggered, perhaps we could assign a note frequency according to its current rotation. The velocity of the particle at the point of contact could control, for example, the volume of note. We could also place the sound in the stereo field according to its location in space. The options are nearly limitless.
To give you an idea of what would this might sound like:
Plot
parameter.So the key takeaway for simulations is:
Running a simulation doesn't intrinsically or automatically produce any sound. Rather, you must design responses to simulation steps and collisions and then route these events into instruments and effects.
Here is the graph we used to make the intro movie for this example.
As you see, only two nodes are required to get simulatin'. The Box2D
node controls the parameters of our simulation and the TimeKeeper node
merely establishes the time duration over which to operate. The most
important parameter of the Box2D is WorldGen
and specifies the name
of an external world-builder script (more details below).
In this example we've used tumbler.ck which
contains world-construction code slightly more elaborate than this:
DbBox2D b;
#(0, 50) => complex center;
#(50, 50) => complex size;
b.worldBegin(#(0, -10)); // gravity is down
b.newRoom(center, size, 5.0, b.dynamicType) => int roomId;
b.newRectangle(#(0, 20), #(.5, .5),
Math.random2f(0, pi), 1., b.dynamicType) => id;
b.worldEnd();
The important worldbuilding take-aways are:
The Mobile example models a wind-chime. Here's the node-graph:
As you'd expect, when two bars collide they produce a note that is performed by the Stk TubeBell instrument.
Notice that each chime produces a consistent pitch. This assignment of
rectangle to note is the responsibility of the OnContact
mode and
ruleset. As mentioned previously, each simulated contact involves
two bodies. As we see above, the id of each chime is present in the plot
and their values range between 1 and 15. These ids are used as indexes into the
OnContact ruleset. For example, body 4 can be seen to produce MIDI note 92.
When more bodies are present than the number of rules in the ruleset we
recycle them "circularly". So with these 5 rules, bodies 0, 5, 10, 15
all produce MIDI note 50. Bodies 1, 6, 11, 16 produce 85 and so on.
A note request of -1 signals Box2D to prevent note events for the
associated body.
ContactMode
allows you to choose how to apply the OnContact ruleset:
BodyId
uses the body id to select a note.Cycle
uses the contact sequence number to select a note.Random
uses a random index into the note list.Upon construction, our mobile has enough internal energy to produce a number
of collisions. Soon thereafter things settle down, the simulation stabilizes
and we may wish to "kick" some energy back into the system. This explains
the presence of the Euclid node in this graph. Euclid's role
is to produce a rhythmic assortment of NoteOn/NoteOff events. These trigger
evaluation of Box2D's OnNotes
ruleset.
So when Euclid produces a note 60 event, the effect is that body 14 will receive an impulse of energy in the X direction. Similarly note 61 kicks body 5 by -20 in X. The units of force are Newtons and the effects of an impulse depend upon the size and density of a body. Typical values for an impulse kick range from 10 to 100 newtons. When you play with this example, locate the Box2D plot and try to identify when kicks occur. See how the effect changes with kick sizes of 200 and 2. Note that the note velocity contributes to the strength of the impulse. Changing Euclid's note-velocity range can have a big impact on the simulation.
For our mobile, a horizontal kick is easy to understand and produces
a predictable result. In more complex scenarios it's often desirable
to specify impulse directions dynamically. ImpulseMode
offers two
options: Static
which applies the impulse in the X, Y directions
given and Velocity
which applies the impulse in the direction of
the body's current velocity scaled by the magnitude of given values.
If you need more precise control over your kicks, you can program
an EventHandler to kick bodies
around wantonly.
So far our focus has been contact, or collision, events. Many interesting simulations may involve no collisions an this raises the question of how to leverage these conditions toward our music-making endeavors.
Let's consider a simple double-pendulum for this discussion. Start by imagining the sound of a pendulum in a cartoon world. The question is, how can we realize the imagined sound?
One observation is that pendula have a characteristic motion. In physics, motion is characterized by velocity and acceleration and these properties have an instantaneous direction and amplitude. If we can periodically sample the motion of bodies in our simulation we could convert these samples into control signals for arbitrary use.
The On Step
ruleset allows us to generate CCs associated with the
dynamic state of bodies in the 2D simulated world. This ruleset is
evaluated after each simulation step and the resulting CCs are
delivered via the Ctrl1
output port (Ctrl0
emits NoteOn events).
Here we see 4 rules that sample properties of bodies 3 and 4 in
our pendulum scene. The tooltip for OnStep
reveals this list
of available fields:
Referring to the pendulum node graph, we see that the Box2D.Ctrl1
is
routed to both CCPlot
and CCRelayer. Note, too, that there are some
Remapper nodes assisting Box2D.
We leave it as an exercise to the reader to explore this graph to learn how all the pieces fit together. The following plot (produced by th CCPlot node) is quite helpful in understanding the role and remapped ranges of CCs in this scene.
Controller
input port for control. Typically you might connect a TimeKeeper
here to signal end of simulation.
WorldGen
the name of a world-generator script.
TimeStep
the time step in seconds that the simulator sees as representing
real-time. 16::ms
is 60 fps and is a typical value used in game engines
and other realtime applications.
TimeScale
a multiplier for the time step. When > 1 you go into "slo-mo"
mode. Values < 1 speed up the simulation but may also incroach upon the
simulation time-budget and shouldn't generally be needed.
ContactMode
one of BodyId
, Cycle
, Random
, selects how to index into
the OnContact
array. If OnContact
is empty this has no effect.
OnContact
controls which notes to emit when Box2D contact occurs.
Because contacts are very short-lived we don't emit
NoteOff events. Rather you should select instruments with
"pluck-like" behavior (ie: short-attack and long-ish decay,
and no release requirements). The choice of the MIDI note
depends on the ContactMode setting. Values represent fractional
MIDI note numbers between 0 and 127. 60 is Middle C (aka C3)
and adjacent integers represent a semitone. When set to -1 the
note for that slot is held back. In all modes, all incoming object
ids are modulo-ed by this array size to ensure successful lookup.
OnNotes
you can "kick" the simulation by connecting Euclid or MidiCC
to our Controller input. A kick is an instantaneous 2D force
(impulse) applied to matching rules in this ruleset. The kick
units are Newtons and their effect depends on the the mass of
your shapes. Values from 10-100 are typical. Impulses can
either be applied in an arbitrary direction or along the
direction of current movement. This is controlled by ImpulseMode.
For an additional dimension of control, the size of the impulse
is multiplied by the incoming note velocity.
ImpulseMode
selects the interpretation of the OnNote XKick, YKick values.
When set to Static, impulses are applied in the direction of the
Kick. When set to Velocity, impulses are applied in the current
body direction and according to the magnitude of [XKick, YKick].
This approach allows you to easily switch between two modes.
OnStep
describes which control events we generate on each simulation
step. For each rule in this list we emit a CC for the field
of the identified body. CC names are constructed by combining
the field and body id, eg. Vmag3. You can remap CC values
via Remap connections or CC names and values with the Relayer node.
Remap
connect Remapper nodes here to modify the values of OnStep CCs.
Plot
, PlotCtx
requests that the simulation be delivered to the
Box2D Plot surface.
Pause
controls whether the simulation is paused.
Mute
controls whether the simulation is muted. If not paused, the simulation
continues to step but triggers no contact or step events.
Example Worlds
WorldBuilding with Box2D API