Audio Flow Combiner

"Audio Flow Combiner" (AFC) is a program demonstrating some ways of using Transd as a front-end language. That is, a language that is used for extending, scripting, configuring, etc. some existing program. This short article provides analysis of the program's sorce code and a brief overview of some features of Transd language. The AFC program itself, with source code and description, is located here.

Transd has several features that make it convenient for using it as a front-end language. One such feature is built-in functionality for working with "Transd Structured Data" (TSD). TSD is a textual format for unified representation of various kinds of data. It represents data in the form of named blocks of name/value pairs (such blocks are called TSD objects):

"someObject_1" : {
    someString: "string1",
    someInteger: 25,
    someFloat: 1.23,
    someStrings: ["string2", "string3", "string4"],
    someIntegers: [30, 40, 50]
}

Due to its simplicity, this format can be used for storing the programs' configuration information, which is supposed to be user editable. Transd has built-in support for quick converting the information from textual TSD objects into program state.

Such conversion is done in two steps. First, we read the contents of a file with TSD data using (read-tsd-file) function, which returns a vector of Object type objects. Object is a built-in type, representing a TSD object: it has a name, and stores the name/value pairs as data fields, whose values can be read or modified.

The second step is converting the Object object into a program object of some class. This second step is done by creating a new object of some class and calling on it the (load-from-object) method, to which an Object object from the previous step is passed. This method initializes the fields of the new object using the name/value pairs of the Object object.

For example, AFC defines the "Flow" class, which contains a list of streams' names (objects of "Stream" type) and a list of time delays between the streams:

class Flow: {
  streams: Vector<String>(),
  delays: Vector<Double>(),
  streamObjs: Vector<Stream>(),

  play: (lambda ...)
}

An object of this class is initialized in the program from a TSD object, defined by the user in a configuration file:

"flow1" : {
    class: "Flow",
    streams: ["stream1", "stream2"],
    delays: [20.0, 30.0]
}

On program start, the "Flow" object is initialized in the "MainModule::_start()" function with the following lines:

MainModule: {
    flowObj: Flow(),
    objs: Index<String Vector<Object>>(),

_start(): (lambda
    ...
    (rebind objs (group-by 
                (read-tsd-file FLfile) 
                (λ ob Object() -> String() 
                    (get-String ob "name"))))
    (load-from-object flowObj (get (snd (get objs flowName)) 0))
    ...