Kernel word recovery crack, Mobile themes website for samsung wave 525, Dekha ek khwab sony tv title song, Song zombies on your lawn, Bloody mary nerve endings mp3, Digittrade tv jukebox, Padosan songs mp3, Gold cup series 70 manual, Nieuwste versie van mozilla firefox en, Cant install spotify on mac, Web brute force password cracker, Belkin g peek driver windows 7

Jam Fabric – Structure

April 17th, 2012 by Martijn Leave a reply »

Determining an optimal structure for the patches in Jam Fabric has been the
subject of quite some reflexion. The goal to be as conceptually simple as
possible to be easy to understand for potential users had to be balanced
against the goal of having structured classes and keeping everything
synchronized, especially in the optic of multiuser usage.

## Problems to be solved

### Workspace structure

Patches consist of a root *Workspace* that can contains *Units*. Units
themselves may have child workspaces attached to them, that can communicate
with the parent workspace through their parent’s unit inlets and outlets.
Things get interesting when a multiple units must refer to the same workspace
structure. For instance, a workspace implementing a synthesizer could be used
multiple times to create chords. If the synthesizer workspace is modified in
one place, the change should be immediately applied to every other instance.
But only the change in structure, as the values have to be different in each
synthesizer instance since we want to create different notes.

In puredata this is handled by updating every workspace loaded from an
external file every time one instance is saved. This has several drawbacks.
When a workspace is reloaded after another instance is saved, the values
are lost. When changes are made in different instances, the workspaces are
out of sync. Saving one instances looses the changes made in the other. This
is especially problematic for collaborative editing where one user may not be
directly aware of changes made by others.

### Serializing values

Another difficulty with workspaces loaded from external sources or multiple
instances of workspaces is serialization. We may choose to limit workspaces to
having a set of fixed values that don’t vary from instance to instance, and to
send every value that needs to be changed from the default when a patch is
loaded from a (unique) parent workspace. This has a few drawbacks as well.
Imagine an external sequencer workspace, containing a visual grid with and
some controls to manipulate the sequenced events. If we want to save the grid
contents for every sequencer instance, we have to store the values in the
patch. The *easiest* way to implement this would be to have the sequencer
workspaces have the functionality to receive/send the grid contents, and store
the grid data in a datastructure contained in the synthesizer’s parent
workspaces contained in the patch. This has the result of adding units to the
parent workspaces, defeating in part the idea of hiding such details in a
subworkspace. This also requires some kind hook that is triggered when a patch
is saved, so the sequencer workspaces can communicate the grid contents to the
parent.

### Unit names

In some cases, units need to refer to other units by name. For instance, in
the case of audio buffer units (eg. containing wave data loaded from file), it
is impractical to send the contents of the buffer every time a consumer unit
requires it. Instead, the consumer unit refers to the buffer by name. This
poses the problem of namespacing. If all names are global, a mechanism is
required to create unique names for units in different workspaces. Sometimes
units may want to access units in other workspaces, requiring a consistent
naming scheme.

Puredata solves this to some extent using the $0 variable which is guaranteed
to be unique inside each workspace. Array objects can be created using the
concatenation of this variable and a name, eg. $0-buf1. Accessing these array
objects from another workspace requires sending the $0 variable to every
workspace using that array. While this solution is workable, it is also a
somewhat cumbersome as it requires extra units not directly related to the
task to be solved.

### Unit types

Units are created by type name. A type defines the functionality of that unit.
A type can be either a registered C++ object, either internal or loaded
through a plugin, a registered script object, or a named external workspace
(abstraction in puredata). If many type names are declared, some kind of
organization is required to avoid naming conflicts, especially if the
naming scheme is flat. Type resolution is also a source of difficulty. Does
the name *granulator* refer to the internal granulator object, or does it
refer to an external workspace in a file named granulator ? In some cases, it
may also be practical to be able to override existing type names, causing
units using the to use a local version instead of the default one. ForWorkspaces share a common
debugging purposes, one might want to wrap the hypothetical system
*table-lookup* object inside a local *table-lookup* object that would dump the
values sent values to the console.

## Implementation

With these difficulties in mind, we proposed a slightly more complex
structure based on the OO approach that separates classes from instances.
A *Network* may be shared between multiple *Workspaces*. Structural changes
are made to that network and not to the workspace. Client applications are
notified of the changes in the networks, and it is up to them to update the
(visual or not) representation of the workspaces instanciating those networks.

Networks contain *Units*. A unit contains the details of a network object: the
number and nature of its inlets and outlets and its configuration data. For
instance a unit implementing a slider will have one inlet to set its value,
one outlet to send its value on change, and minimum and maximum values. These
details are shared between every workspace that is an instance of the same
network.

Workspaces have a Network, and contain *UnitData* objects that hold values for
every input and output pin for the corresponding Unit object in the workspace’s
network. Changes in values are specific to a workspace, and thus the client is
notified of those on a workspace base.

In this model, Networks and Units correspond to C++ classes. The configuration
variable in units correspond to static class data. Workspaces and UnitData
correspond to C++ objects.

A unit itself is an instance of a type. A type is either a C++ object, a
script object or a network. The C++ Unit class is the base class for every
Unit type. The script object API is still to be defined. The default
implementation will be based on ecmascript, since it is included and
well-integrated into the Qt libraries. Network types can refer either to an
internal or to an external network. The unit that encapsulates this network
has a type name that is equal to the fully qualified name of the network.

### Type names

We chose a hierarchical naming scheme for unit type names, loosely based on
perl packages[^1]. Path components are separated by a double column (::). By
convention path components are capitalized.

    Math::Sin
    Control::ForEach

#### Type resolution order

When a unit is created, the type name is searched in the following order:

– local namespace
– override namespace
– global namespace (C++, script)
– include paths

The “local namespace” location needs some explanation. The type name is first
looked up inside the current workspace *file*. This means that the type names
that are registered in the current file are searched. In the case of the root
workspace or any workspace loaded directly from the patch file, this is the
patch file. In the case of an external workspace this the namespace of
the workspace file.

The override namespaces can be used to tweak or replace the behaviour of units
that should not or cannot be modified, ie. system units and external workspace
created by other users. It uses the “override paths” configuration setting to
load external networks. It can be used to set default values that are more
appropriate to a working style, or to wrap newer versions to reproduce old
behaviour to avoid rewriting all patches[^2]. Override paths is used in a
general sense here, since this includes loading plugins and scripts.

Global types are either builtin types, C++ units, scripted units, or type
names in *required* external workspaces. C++ units are loaded and registered
from plugins. Scripted units registered by loaded scripts. External workspaces
can be required, which causes the names of the subworkspaces they contain to
be made available to the entire patch, instead of being limited to the local
file namespace.

Lastly, type names are searched in the include paths.

If the type is not found, the unit is still created but marked as invalid.
This allows files to be loaded even if an external dependency is missing.
Invalid units are reevaluated when a plugin is loaded or if paths are changed.

#### Loading from file paths.

External networks are loaded by path in the “override paths” and “include
paths” system configuration. The first components of the type name are interpreted
as directories.

    // include_paths contains ~/include and /usr/share/jf/include
 
    // search file named ~/include/Foo/Bar.jfm or /usr/share/jf/include/Foo/Bar.jfm
    rootNetwork->addUnit("Foo::Bar");

#### Forcing registered types

The use of a registered type can be forced by prefixing the type name with a
double column (::). Registered types are builtin types or types that were registered
by a plugin. If the registered type is not found, an invalid is created.

    // create a unit of the builtin Math::Sin type
    network->addUnit("::Math::Sin");
 
    // create a unit that may be a local network that overrides Math::Sin
    network->addUnit("Math::Sin");

#### Naming networks

Inside a file namespace, networks are recursively named. The root network is
always called *ROOT* and may never be instanciated twice.

    // subnet0 has type "Foo"
    Unit* subnet0 = rootNetwork->addUnit("System::Network",
        ConfigMap{ Qpair{ "name", "Foo" } });
 
    // subnet1 has type "Foo::Bar"
    Unit* subnet1 = subnet0->addUnit("System::Network",
        ConfigMap{ QPair{ "name", "Bar" } });
 
    // subnet2 has type "Foo::Baz::Bat". Note this may cause conflicts !
    // Unit* subnet2 = subnet0->addUnit("System::Network",
    //     ConfigMap{ QPair{ "name", "Baz::Bat" } });
 
    // subnet3 has type "Bir"
    Unit* subnet3 = subnet0->addUnit("System::Network",
        ConfigMap{ QPair{ "name", "::Bir" } });
 
    // subnet4 has type "Bir::Boom"
    Unit* subnet4 = subnet0->addUnit("System::Network",
        ConfigMap{ QPair{ "name", "::Bir::Boom" } });

Once declared, these types can be used to create new units sharing the same
network.

    Unit* qux = rootNetwork->addUnit("Foo::Baz::Bat");

In case of conflicting names or recursion (a network including itself, either
directly or through the inclusion of subnetworks) results in its parent unit
being invalid.

##### Anonymous networks

When a network is created without explicit name, its name is set to a reserved
unique anonymous name. This name may be queried, and can be used for debugging
purposes, but cannot not be referenced (for instance by addUnit()). The name
may change everytime the network is loaded.

##### Requiring networks

Instead of being loaded implicitely on unit creation, external networks may
also be *required*. This results in the registration of every named network it
contains into the local namespace.

    // load Foo network from a file in the override paths or include paths.
    rootNetwork->addUnit("System::Require", 
            ConfigMap{ QPair{ "name", "Foo" } });

A unit of type “Foo” may now be added to any existing network. The Foo network
may contain sub networks, for instance “Foo::Bar”, or “::Baz” or even
“::Math::FooFunc”. Note that the latter two are not shown as sub networks of
Foo.

Networks may be hidden from exports by prefixing them with an underscore (\_).
The underscore is part of their name, and they aren’t be accessible outside
the network’s local namespace.

The System::Require type can take the “as” config option to rename the
required network to another prefix. This prefix may be empty.

    rootNetwork->addUnit("System::Require", ConfigMap({
        QPair{ "name", "Foo::Bar"},
        QPair{ "as", "Qux" }));
 
    // We can now create a unit of type Qux loaded from Foo/Bar
    rootNetwork->addUnit("Qux");
 
    // Foo::Bar is not a type
    // rootNetwork->addUnit("Foo::Bar");

It also takes the “override” config option to import the network’s types into
the override namespace instead of the local namespace. This is an option to
use with care, as it means that an imported external network may change the
behaviour of types used inside the patch.

### Object names

In Jam Fabric, every object has a unique name. If no name is specified, a
unique name based on the object type is created. Like type names, object names
are hierarchical. Multiple instance of a same unit in a same workspace will
have different names.

    // implicitely set "display_0" name
    rootNetwork->addUnit("IO::Display");
 
    // explicitely set "dumper"
    rootNetwork->addUnit("IO::Display", "dumper");
 
    // assuming a foobarNetwork created earlier
    foobarNetwork->addUnit("Math::Sin", "sin");
    rootNetwork->addUnit("System::Network", "net1",
        ConfigMap{ QPair{"name", "Foo::Bar"} });
    rootNetwork->addUnit("System::Network", "net2", 
        ConfigMap{ QPair{"name", "Foo::Bar"} });
 
    // We now have two units called "net1.sin" and "net2.sin".

This is especially useful in a scripting context, in which the concept of
classes and objects is hidden behind a simpler facade.

    var obj = patch.findObject("net2.sin");
    obj.setInput("input", 3.14);

It is also useful to refer to other units, for instance to access an audio
buffer. Like directory names object paths can be absolute or relative.
Absolute object paths start with a dot (.), relative paths either with a
column (:) or with a name. Each column represent a parent unit.

    fooNet->addUnit("Audio::Buffer", "lady_gaga");
 
    // access local "lady_gaga" buffer
    fooNet->addUnit("Audio::Player", "player", 
        ConfigMap{ QPair{ "buffer", "lady_gaga" } });
 
    // access "lady_gaga" object in other workspace using absolute path
    barNet->addUnit("Audio::Player", "player", 
        ConfigMap{ QPair{ "buffer", ".foo.lady_gaga" } });
 
    // access "lady_gaga" object in grand parent workspace using relative path
    quxNet->addUnit("Audio::Player", "player",
        ConfigMap{ QPair{ "buffer", "::foo.lady_gaga" } });

Wireless communication (inside or between workspaces) is implemented through
System::Bus objects. They are normally not handled directly by the user, but
implicitely created by System::Send objects. Bus objects have names like any
other object, and so listening objects may refer to them using their object
name.

    // send to "schoolbus"
    root.addUnit("System::Send", { bus: "schoolbus" });
 
    // listen to "schoolbus" in local (root) workspace
    root.addUnit("System::Receive", { "bus: "schoolbus" });
 
    // listen to "schoolbus" in root workspace from bar workspace 
    // using absolute path
    bar.addUnit("System::Receive", { "bus": ".schoolbus" });

## Conclusions

This organisation offers the required expressivity that is required to do more
structured programming than what is usually available in visual programming
languages. The added complexity can be hidden in the scripting interface and
in visual client applications. Sharing datastructures saves some memory at the
expense of slightly more indirection and object lifetime management. (With
great power comes great responsability!)

This is work in progress, and some details may change (especially the code
examples). The big missing part here is graph serialization, and how the
values of external networks are saved in patch files. This will be addressed
in the next design document.

If you read all this, you may as well leave me your opinion ! Thanks !

[^1]: Only the sane parts, many aspects can’t be applied to graph-based
programming anyway.
[^2]: Not that authors should break backward compatibility, but in practice it
is good to have a way to handle this.

Advertisement

Leave a Reply

Authorized tags:
[b]bold[/b] [u]underline[/u] [i]italic[/i] [url=http://link.url]link[/url] more