|
Antonio is an environment and programming language designed for real time and script based sound synthesis. The typical use is within an application, for example, a type of score language implementation, in which a user specifies note sequences and any other parameters for the synthesis of sounds (instruments, amplitude, duration, attack, spectrum effects, etc) in script form; a further example would be an application which responds to a midi-keyboard and offers a dialog to set the parameters, say, for a piano; in this case no scripts are needed, the user specifies parameters via check-boxes, buttons, sliders or small text fields and plays the keyboard. Between these two examples are applications which may be called computer aided music performance, where the performer has real time control over the interpretation, but the computer generates multiple notes on a predefined scheme, for example, the performer defines chords dependent on two concurrently pressed keys, and, the corresponding precedence and weights of the notes in the chord; during the actual performance, velocity, pedal values, wheels, a second keyboard or other input devices and “after touch informations” are used together with the definitions to achieve a real time interpretation of the performed music. In all these cases, implementation is interpretive, algorithms are stored as source text, and, as a consequence, applications can be customized or otherwise modified to conform to the requirements of a user; in other words, the Antonio design is to support experiments with algorithms for sound synthesis and music performance, to turn experimental code into applications and to build new applications from existing applications. Antonio was primarily designed for ease of use in combination with algorithmic flexibility: Antonio commands and statements, mathematical operations such as integration, differentiation, Fourier transform or digital signal processing can easily be visualized and may thus appear less abstract, on the other hand, the algorithmic flexibility of a programming language is maintained. It turns out, that the overhead of interpretation is for many synthesis applications on modern computers tolerable, since most of the processing time in these applications is in inner loops, which can be optimized (and parallel execut ed).Mainly classical piano music has been synthesized with Antonio, some of which is published in archive.org. One of the reasons for the selection of classical music was the goal to assess the quality of synthesized music by comparing it with recorded music; classical music with its large set of high quality recordings was considered to be particularly suited for this purpose, the primary challenge in script based synthesis with Antonio is finding the right interpretation (dynamics, agogics, and instrument specific parameters like sound brightness, hammer effects etc.), while the transformation of a piece of sheet music into a synthesis program or a change of this program according to a desired interpretation is straight forward (with the Antonio Tomaso application, see below). In cantorion.org both, synthesized classical music and high quality recordings of the same musical pieces can be compared. Examples are: Liszt/Liebestraum Chopin/Polonaise Chopin/Scherzo Chopin/Valse Brilliante Beethoven/Appassionata Joplin/The Entertainer and more. This site describes some features of Antonio, code examples and existing Antonio application. A more systematic description of Antonio functions and language rules is in Antonio Help.
 |
Contents
|
|
The Antonio System
The Antonio system provides a self contained, integrated development, testing and execution environment for applications, which are written in the Antonio Programming Language. Data are stored in two types of files: dictionaries (*.vlb) and workspaces (*.vws). A dictionary is a container for objects, a workspace is a list of links to the views of objects. The following picture shows a workspace view of “Alessandro.vws”, the wave edit view of a wave object with name “w1” and a dictionary view of “Alessandro.vlb”. 
Alessandro is an example of a permanent dictionary, there are also temporary dictionaries created dynamically during expression evaluation and named dictionaries, also called structures, which are contained in permanent dictionaries, temporary dictionaries or in structures. Antonio is self contained in the sense, that it integrates a text editor, a data management component (using windows files), window management functions, multi-threading with synchronization, timer support and other operating system functions. Exchange with windows files is via import and export commands, and, it is possible to run console commands from within Antonio expressions; for example, a *.mp3 file may be converted to a *.wav file by calling lame.exe, *.wav files can be imported. In summary, Antonio provides or integrates functions, which are useful in the context of real time and script based sound synthesis
back
Programming Language
The syntax is similar to familiar programming languages. Antonio functions may be invoked in dot-notation, a.f(17) is equivalent to f(a,17). Like in macro languages, identifiers are the result of evaluations (for n == 13, a#n# evaluates to a13, or for s == “bc”, a#s#d evaluates to abcd). Source text interpretation is dynamic, subroutine interpretation (expressions or functions) is defined by text substitution (subroutines run by default in the environment of the caller); variables may be constant (reassignment ignored) or optionally typed (assigned value forced to the type); otherwise their type is derived from the assigned value. An important feature specific to Antonio is the possibility to influence name resolution, or, in Antonio terms, the chain of dictionaries in which the system searches for the definition of names; the notation “c4.n(t4)“ may mean playing the note c4 for the duration of a quarter note, on which instrument is determined by the definition of n; a score language in Antonio would support switching between different versions of n by switching between environments (different chains of dictionaries); the same applies to switching between voices for the same instrument; this allows the specification of position-dependent default values for parameters like amplitude, sound color, attack and several others per voice and thus reduces significantly the number of parameters to be specified for an individual note. The following text describes briefly some of the functionality of the Antonio Programming Language. back
Objects and Handles
Antonio supports objects such as double values, wave objects (time series processed as a whole in main memory), sound objects (time series ready for replay and stored on disk), strings, structures, expression references and function references; these values are defined by data and attributes and may be assigned to variables. Variables may have synonyms, and, synonyms may be reassigned, There is also the notion of a dictionary reference, which arises during expression evaluation and can be returned to a caller. The numeric types double, wave or sound can be of real type or of complex type; if a complex sound or wave is replayed, the real part is used for the left channel and the imaginary part for the right channel. Text objects are used to specify source code, to specify strings, or, to convey information to the user. Source code is contained in either an expression text object or in a function text object. Typically functions and expression objects are defined in other functions or expression objects, but ultimately there is a function or expression text object which is in a permanent dictionary and into which all contained text objects are considered as “embedded”; embedded text objects are modified by editing of the containing text object. Function and expression objects can be (re)-defined at run time, even by declaring calculated text as function or expression object. Handles are placeholder for abstract objects and are implemented as double objects with attributes, which determine the operations that can be performed on them. Timers, queues, tasks (windows threads), midi keyboards, incremental players and incremental signal processes are accessed via handles. back
Wave Arithmetics
Waves have a number of attributes, such as “in F-Domain” (FD == 1, in frequency domain) or “in T-Domain” (FD == 0, in time domain), S (start-time / frequency ), E (end time / frequency), L (length), N (number of intervals), SN (number of sample values), P (periodic), IP (interpolation method) and several more; these attributes are not independent, some can be explicitly assigned, others are derived. Antonio distinguishes between waves as (discrete) vectors and waves as continuous functions (of time / frequency, with interpolation). A continuous wave in time domain may be periodic, closed or open at one or both of its end points; in each of these cases waves have a finite length; the question is, what is the value w(t) of a wave w where t is either to the left of the start-point or to the right of the end-point; for periodic waves the answer is clear, for closed waves the answer is 0, and for left-open waves (LO == 1) or right open waves (RO == 1) the answer is the value of the wave at the start-point or the value of the wave at the end-point, depending on whether the point t is to the left of the start-point or to the right of the end-point. Continuous waves may be rated (attribute R == 1). If a wave is rated, the sample rate attribute SR may be assigned, and, the start time / frequency S and the length L are rounded to integer multiples of 1/SR, while for non-rated waves the rate SR is derived from the L and N attributes. Antonio assumes, that the rate of rated waves is set high enough for all operations; the rules for non-rated continuous waves and vector arithmetics are such that, once the attributes of operand waves are set, the result of arithmetic operations are as accurate as the operands and should not need attribute adjustment. An example is the PV (positive valued) attribute, which is inherited on assignment and after multiplication with a constant or a positive valued wave; positive valued waves may be used as divisors (w(x).abs > 0 for x = -∞ to +∞). With these rules it is possible, to add, subtract, multiply, etc. waves much like it is possible with scalars (scalars may be viewed as waves with a time / frequency independent value); in addition there are a number of useful operations (“methods”) on waves supported such as sorting and others, which make only sense with (sampled) functions: discrete or interpolating convolution and linear filters; for example, the canonical form of linear signal processing of input in into output out can be written as: . where a and b are vectors of coefficients, and, c and d are vectors of time distances; with Antonio, out can be calculated as out = in.FIR(a,c).IIR(0,b,d); where FIR (finite impulse response) and IIR (infinite impulse response) are wave methods (built into Antonio for the correspondingly named filters). back
Incremental Signal Processing (ISP)
Most of the code in real-time or script based sound synthesis applications is for coordination, administration, etc., in other words, for the management of the synthesis-process and does not need signal processing; but essential for the characteristic of synthesized sounds are often a few lines of digital signal processing; and, in many cases, the execution time used within signal processing exceeds by far the time used in the surrounding code. Signal processing in Antonio is incremental, that means, signal processing algorithms are set up as coroutines. For example, on a key-down message from the keyboard a signal processing algorithm is set up according to the parameters in the message and the instrument definitions; then a first piece of musical sound is generated, which is immediately passed to an (incremental) player, which in turn begins immediately to send audio signals to the loudspeakers; while increments are being replayed, the next increments are calculated by calls to the signal processing coroutine; the incremental interface allows for short latencies from the key-down event to the triggered audio-output; special casing makes signal processing efficient. Signals are waves which are closed, real (not complex), and, rated; within a signal process, all signals have the same rate, and at a specific point in time the same start-time and the same length, but the start attribute varies from increment to increment for the calculation of successive output increments. Signal processing can be understood as the solution to a set of equations in a specific form; for example, the following graph shows a signal variable source, which sends (sample) values multiplied with a factor and delayed for 16 intervals to a signal variable target: 
the equation behind this graph says: sample values of the source at time n multiplied with a factor of .9 must reappear as component of the target sample value at time n + 16 (as component, because there may be many more similar connections to target). In Antonio syntax, signal references are prefixed with a § sign, the solution to the corresponding equation within an Antonio ISP body looks like: §source[-16] * .9 +> §target[0]; or, more generally: §source[-b_dist] * factor +>§target[f_dist]; In this general form, the total delay is the sum b_dist + f_dist intervals (of backward distance and forward distance); in other words, delaying of signals can be achieved by gathering values from the past or by scattering values into the future. In general, gathering is more efficient and for linear signal processing both approaches are equivalent; but for some non-linear processing statements, scattering has different and more interesting effects than gathering, therefore both approaches are supported in Antonio; the typical scatter instruction looks like (§source ° hip >> §distw) * factor +> §target; where hip is an interpolating convolution function and distw is a normal time varying signal, which may be initialized / calculated as any other signal; the fractional part of distw sample values are used for interpolation as defined in hip; of course, source and distw may have backward indexes and target may have a forward index. Antonio supports arithmetic operations on signals (add, subtract, multiply , divide), comparisons leading to boolean signals, complex multiplication between pairs of real signal, or, between a pair of signals and a pair of factors; ISP instruction may be prefixed with a condition signal, ( they are executed on the current sample position only if the condition signal is not 0) and for most instructions value propagation with conditional execution is supported (which means propagation of the preceding value, if the condition signal value is 0). back
Multitasking, Queues, Timer
Modern computers have four or more processors, which can be utilized via multi-threading, thus multitasking offers the chance to increase compute power by almost the number-of-processors as factor, since most synthesis algorithms are in their inner loops independent, thus the overhead for synchronization or serialization is minimal. And, as a second justification, multitasking opens up a further dimension for structuring an application into functional units with a well defined interface. In Antonio, there are three types of tasks, each type is realized as a Windows thread. The Win-Task is started by Windows when Antonio is started and responsible for the execution of commands in views. Issuing a Run command in the edit mode of a permanent expression text object causes the creation of a main-task (a new thread); main-tasks are always associated with a permanent expression text object and there may be, at any time, at most one main-task associated with one permanent expression text object; a small window, called Expr-Dialog (expression dialog), exists while a main-task is active; the Expr-Dialog contains information on the status of the main-task and buttons to halt or stop the main-task. Main-tasks may also be created during run time by executing a run command with the name of a permanent expression object as parameter; the run command starts the main-task and returns a handle, on which the creator or any other main-task or subtask can wait for completion. Finally, main-tasks can start subtasks by executing one of the fork commands, all of which return a handle to the created subtask. Tasks are independent (each has its own chain of dictionaries), but they share the permanent dictionaries, the temporary dictionary TEMP ( contained as structure in the current permanent dictionary), and, in case of subtasks, those temporary dictionaries of the creator, which existed at the time of creation. Antonio takes care of the handshaking necessary between a thread and its creating thread; however, user-programmed synchronization may become necessary in cases, where several tasks operate on shared variables (update operations in the form var[]+= ... are safe); Antonio supports the interaction between tasks with a queuing mechanism: communication, synchronization, serialization, event signaling (or, semaphores and critical sections) between tasks and subtasks. Queues in Antonio are message queues with a fixed capacity of messages; a message is a structure, which may contain any scalar variable, this includes synonyms of waves, for example. The handle to a queue may be obtained by calling the Queue built-in function with the desired capacity as parameter. Main-tasks or subtasks can wait on a queue; the result of the wait operation is a message with at least one element with name x, which contains the index of the event within the wait-list, from which the message originated, if originating from a queue, it contains exactly the members which were enqueued with the only exception of any component with the name x. Other sources of messages are the keyboard, dialog fields such as buttons or sliders, and the incremental player. With the help of timers, it is possible to wait on a list of events with a timeout limit. back
Graphical User Interface
Antonio supports the creation of a dialog-view during execution; as example, the following figure shows a section of the dialog-view created in the Domenico Chord Piano application:  the dialog-elements include text-fields, buttons, sliders, check-boxes and combo-boxes. Associated with each active element is a queue, to which messages are sent on actions. Typically applications create subtasks as servers for these queues. The positioning of elements can be arbitrary within a header section, and within “lines” and “columns” in the area below the header section; this gives more flexibility in rearranging the fields. Elements can be dynamically shown or hidden and enabled or disabled. back
Window Management and Testing
View windows can be dynamically activated and dynamically resized or repositioned, minimization is possible for a specific window or for all windows, similarly, the cascade or tile commands can be issued during execution. With this functionality almost any desired layout is possible, during development, testing and actual runtime. Often only a few lines of code are sufficient to achieve the desired layout of windows. Testing of applications in Antonio is supported with stepwise execution, by the collection of status information into special text objects, and, by program modification (the latter is often sufficient and needs no extra support; due to the direct interpretation of source-text, it is very efficient). As soon as a main-task is put into a halt-state, stepwise execution of the main-task or any of its subtasks is possible; when a main-task is in halt-state, all its subtasks are also put into a halt-state and vice versa. While in a halt-state, the invocation stack (Text-Stack) can be inspected and the current position in the code or the point of invocation can be marked, if necessary, the corresponding view is made the top view, the environment (chain-of-dictionaries) and each contained dictionary can be inspected, similarly all this information can be activated for any task in a task-list, be it subtask or main-task. There are three ways to put a main-task or subtask into a halt-state: by execution of the Halt command, by setting halt points in the code (without text modification and possibly while the application is executing) or by clicking the Halt button in the Expr-Dialog. Once in halt-state, the Expr-Dialog offers single step, single step on the same invocation level, as many steps as necessary up to 1 level higher, and, finally, to continue execution until termination. The Application section of this site describes an application Leonardo IDE (Integrated Development Environment), which makes relevant information available within the Antonio main window. back
|