Group

The group construction gathers several actions logically within one block that shares common properties of tempo, synchronization and errors handling strategies in order to create polyphonic phrases.

The actions of a group are launched sequentially and the group organizes their precise temporal relationships: a group is a timeline and the actions of the group are actions scheduled on this timeline. Each group has a temporal scope which defines how time passes in the timeline. More generally, a temporal scope can be specified for all compound actions. Temporal scopes are implicitly defined through attributes of compound actions: @tempo and synchronization attributes. This notion will be developped in the next chapter Management of Time. In this chapter, we focus on the syntax and the hierarchical structure of compound actions.

The general syntax of a group is defined by the following diagram:

The specification of the label, Attributes and end clause are optional. The label is a simple identifier that acts as a label for the group.

There is a short notation for a group without optional parameters and attributes: its actions are simply be written between braces. For example:

          action₁
          { 0.5 action₂ }
          action₃

is equivalent to

          action₁
          Group {
              0.5 action₂
          }
          action₃

Implicit Groups

Some groups are implicit. For example, the actions following an event are members of an implicit group named top_gfwd_xxx where xxx is a unique number in the score 1. And all child actions of a compound action take place in a implicit group, often called the body of this compound action (e.g., the body of a loop, the body of a process, etc.).

By default, the implicit top-level groups are created with the @loose synchronization strategy. This behavior can be changed in favor of the @tight synchronization strategy, using the command top_level_groups_are_tight at the begining of the score.

Action Sequence

The actions of a group are arranged in a sequence. Two consecutive actions in this sequence are launched together, in parallel. For instance

        let $x := 0
        print $x

will be launched in the same instant. Nevertheless, actions that occur in the same instant are ordered: this is the synchrony hypothesis. So, in the previous example, a 0 will be printed.

The temporal scope of a group is used to interpret the relative delays that appears optionally in front of an action. For example:

       Group G @tempo := 120
       {
           1 action₁
           2 action₂
       }

With the launch of group G, the delay of the first action is evaluated into 1, and then nothing happens until 1 beat at tempo 120 is elapsed. At this moment action₁ is launched and the delay preceeding action₂ is evaluated. Etc.

The quantity of physical time corresponding to a relative delay is specified by the tempo of the group. The way of counting this quantity depends of the synchronization strategy of the group.

The sequencing of actions in a group can be modified using continuation operators. The ==> operator is used to launch an action after the end of the preceding one and +=> is used to launches at the end of the previous one including its children. The end of a group is the launch of the last action in its action sequence (if this action has a delay, the group ends with the start of this delay). For example

       let $start := $RNOW
       Group G
       {
             1 action₁
             2 action₂
             Group GG
             {
                   1 action₃
                   1 action₄
             }
       }
       ==> print OK ($RNOW - $start)

will print OK 3 ($RNOW gives the relative time) because G ends at soon as GG is started and GG is started with action₂. But, if the continuation operator ==> is changed for +=>:

       let $start := $RNOW
       Group G
       {
             1 action₁
             2 action₂
             Group GG
             {
                   1 action₃
                   1 action₄
             }
       }
       +=> print OK ($RNOW - $start)

then OK 5 will be printed because the +=> operator will execute the print message at the end of all actions spanned directly or indirectly by G.

These features will be discussed more in depth in chapters continuation and temporal scope.

The Nested Structure of Groups

Groups, and more generally compound actions, can be nested arbitrarily. We illustrate below the nesting of groups specified by

        Group G
        {
                action₀
              1 action₁
                Group G1
                {
                        action₂
                      1 action₃
                        action₄
                }
              2 Group G2
                {
                      3 action₅
                        action₆
                }
                action₇
                Group G3
                {
                      action₈
                      Group G31
                      {
                            2 action₉
                              action₁₀
                      }
                      action₁₁ 
                }

        }

as a tree making explicit the father/child relationships

nested structure of a group

and as a timeline showing the temporal organization

exemple temporal organization of nested groups

In this last diagram, the width of an action actionᵢ (abbreviated aᵢ) is not relevant. A group is pictured as a rectangle containing its childs but this is merely a graphical convention: a group ends with the start of its last action.

Instances of a Group

A group is related to either an event or another action. When the event occurs or the action is triggered, the group waits for the expiration of its delay before sequentially launching the actions that comprise it. We say that an instance of the group is created and launched. The instance is considered alive while there is an action of the group waiting to be launched. In other words, an instance expires when the last action of the corresponding group is performed. It is possible to refer to the instance of a group through a special kind of values called Exec.

We make a distinction between the group and its instances because several instances of the same group can exist and can even be alive simultaneously. Such instances are created by loop, forall, etc. These constructions are described in the rest of this chapter.

Local variables

Variables local to a sequence of actions can be declared using the @local keyword. A @local declaration is not an action, and can appear anywhere in the sequence. The introduced variable is:

  • local to each instance of the action sequence (two instances do not share the variable);

  • its scope (where the variable's name is recognized) is the whole sequence where it is defined and all enclosed actions;

  • and its lifetime (when the variable can be read and written) is the lifetime of the sequence and its children. The variable cease to exist once the last nested action has expired.

Notice that a local variable can be safely accessed by a child action, even if the group where it has been defined has expired.

See section Variables for further information.

Aborting a Group

There are several ways to provoque the premature end of a group, or more generally, of any compound action:

Note that when the name of a group is used in an abort action, all alive instances of this group are killed. It is possible to kill a specific instance using the exec that refers to this instance.

The two last mechanisms are called end clauses.

The until and the while Clause

The specification of a group may include an optional until clause that is checked before the triggering of an action of the group:

          $x := false
          Group G {
              1 $x := true
              1 print DONE
          } until ($x)

The word DONE will never be printed because the group is aborted when $x becomes true. More exactly the expression $x is checked each time a action must be launched. And if true, the group is terminated instead of proceeding with the action. So, with the following program:

          {
                $x := false
              1 $x := true
              1 $x := false
          }
          Group G {
              3 print DONE
          } until ($x)

the word DONE will be printed even if the $x variable has been set to true. As a matter of fact, at date 3 beats, the variable is false again (notice that in { ... } Group G { .... } there are two groups that are spanned in parallel).

There is another way to represent the until keyword: using the contrary while statement. Thus,

          group ... { ... } until (exp)

is equivalent to

          group ... { ... } while (! exp)

The during Clause

A during clause specifies temporal validity, i.e. the time a group is active. When this time is exhausted, the group is aborted. This time can be specified

  • in beats (relative time): [d]

  • in (milli-)seconds (absolute time): [d s] or [d ms ]

  • or in number of logical instants: [d #].

For instance:

          Group G {
               1 $x := true
               1 print DONE
          } during [1.5]

will launch the assignment 1 beat after the launching of but the message print is never sent because is aborted 1.5 beats after its start.

The notation of a duration follows the notation used for the access to the history of a variable. So

          Group G {
               ; ...
          } during [1.5 s]

will execute the actions specified by the group, up to 1.5 seconds after its start. And

          Group G {
               ; ...
          } during [1 #]

will execute the group only once. For example

       Group GG
       {
             print GG 1
           1 print GG 2
           1 print GG 3
           1 print GG 4
           1 print GG 5
           1 print GG 6
           1 print GG 7
       } during [4#]
 
   will print:

     GG 1
     GG 2
     GG 3
     GG 4

 
 

because 4 logical instants after its activation, the group GG is aborted.

This last logical duration may seems useless for a group, but is very convenient to specify the number of iterations of a loop or the maximal number of triggering of a whenever.

The @abort clause

Every compound action may have an abort handler specified through the @abort attribute. The abort handler is a sequence of actions performed when the compound action is terminated via an abort. It is not performed when the group reaches is natural end or if it is terminated via an end clause.

The scope of the handler is the scope introduced by the compound actions (if any): local variables introduced by the compound action are accessible in the handler.

For example

   Group G @abort { print "DONE" }
   {
          print G 0
        1 print G 1
        1 print G 2
        1 print G 3
   }
   1.8 abort G
 
 
   will print:

  G 0
  G 1
  DONE



A typical example of an abort handler is illustrated in section abort handler: they are used to stop a curve arbitrarily with a “fade” leading the parameter to reach a final value irrespectively of its value when the abort occurs.

The @exclusive Clause

The last figures of the previous section show that multiple instances of the same group spanned by a compound action may overlap in time. Sometimes it is necessary to avoid this behavior: this can be achieved using an @exclusive attribute on the compound action.

The effect of @exclusive is to abort any previous instances of the group (if they are still active) when a new instance is triggered. The termination includes the eventual children of the previous instances. Abort handlers are activated if there are any. The new instance is triggered after the termination process.

Synchronization Attributes

Synchronization strategies like @loose and @tight

          group ... @loose ...  { ... }
          group ... @tight ...  { ... }

and error strategies like @local and @global

          group ... @global ...  { ... }
          group ... @local ...   { ... }

can be specified for a group and also for every compound action using the corresponding attributes. If they are not explicitly defined, the attributes of an action are inherited from the enclosing action. Thus, using compound actions, the composer can easily create nested hierarchies (groups inside groups) sharing homogeneous behaviors.

Synchronization strategies are described in chapter Synchronization Strategies.

Local Tempo

A local tempo can be defined for a group using the attribute @tempo:

          group G @tempo := exp ...  { ... }

exp is an arbitrary expression that defines the passing of time for the delay of the actions of that are expressed in relative time in the group, see chapter Management of Time.

With a local tempo, you can create, for example, an accelerando. In the next example, we use a variable to specify a local tempo and we control this variable with a curve (see Curve). That way, we can write a group where all durations are equal. It’s the variation of the local tempo variable that creates the accelerando.

          curve tempVariation @grain := 0.05s
          {   $localtemp
                 { { 60 } 1 { 120 } }   
          }

          group G @tempo := $localtemp
          {
                    action1
                1/4 action2
                1/4 action3
                1/4 action4
                1/4 action5
                1/4 action6
                1/4 action7
                1/4 action8
          }




  1. This name may appears in some error messages or in the listing generated by the command antescofo::printfwd following the verborsity level.