SASS
A Music-Oriented Programming Language

Contents

·         Introduction

o        What is SASS?

o        Supported Targets

·         Linguistics

o        Constants

o        Comments

o        Timing

o        Instruments and Sound Effects

o        Music Tracks

 

 

♫ Introduction ♫

 

What is SASS?

Sound ASSembly is a music-oriented programming language designed for use with embedded platforms, particularly game consoles.

 

As the hardware of SASS targets may vary wildly, the SASS Standard only encompasses macro instrument structure and basic music script decoding (note on, note off, loops, calls, etc.). Many of the richer features (and inconvenient limitations) are platform-specific, so it’s best to check the manual for your target’s SASS compiler for more details on its capabilities.

 

SASS may be viewed as an alternative to existing music languages such as MML (Music Macro Language) and is a derivative of Epyx’s SPL (Sound Programming Language). Users coming from an MML background should pay careful attention to the macro instrument structure, music blocks, and timing as these differ significantly from how MML is usually written.

 

Of course, SASS does not have to be written completely by hand. Basic music scripts may be generated from a MIDI source using MID2SASS, which may also serve as a good starting point for the SASS language.

 

 

Supported Targets

The SASS language is available for use with the platforms listed below; more details are available in the each of their sound drivers’ respective documentation.

 

Platform

Type

Sound Driver

Microsoft Windows

Wavetable

BupBoop

Atari Lynx

PSG + PCM

HandyMusic

NEC TurboGrafx-16

WSG + PCM

HuSound

 

 

♫ Linguistics ♫

 

Constants

Values may be entered in decimal, hexadecimal, and binary using the following syntax:

       32            ; Decimal (No prefix)

       -32           ; Negative Decimal (Leading “-”)

       $20           ; Hexadecimal (Leading “$”)

       %100000       ; Binary (Leading “%”)

 

As most SASS targets use fractional-integer values (i.e. 16.8 or 8.8 precision math), a decimal may be assigned to each value as follows:

       32.16         ; Decimal

       -32.16        ; Negative Decimal

       $20.10        ; Hexadecimal

       %100000.10000 ; Binary

The format of the decimal matches that of its leading integer. Values written without a decimal are assumed to have one with value zero.

 

 

Comments

Follow a semicolon “;” and span a single line:

       ; This is a comment, it will span the whole line

       as4 60 ; Comments may also follow commands and values

 

 

Timing

All values quantifying time (note on, rests, waits, etc.) are given in driver ticks. The exact rate of the driver ticks is dependent upon the current target and driver implementation. Most targets will use a 60Hz base tick. Following this assumption we can write the note sequence below:

       f.3 60        ; Play an F in the third octave for one second

       rest 20       ; Note off and rest for 1/3 second

       as5 40        ; Play an A sharp in the fifth octave for 2/3 second

 

If the driver tick was 240Hz, the example above would have to be changed:

       f.3 240       ; Play an F in the third octave for one second

       rest 80       ; Note off and rest for 1/3 second

       as5 160       ; Play an A sharp in the fifth octave for 2/3 second

 

 

Instruments and Sound Effects

As the decoding of instruments and sound effects is identical in SASS targets, their definitions are also similar. Instruments are expected to be used in (and usually included with) music tracks under the control of the music playback routine. Sound effects are available for playback at any time by the user or game routine. Therefore priorities are exclusive to sound effects, while note offs and tuning are only available to instruments.

name priority                            ; <- Label

       volume value                      ; <- Header / Init

       frequency value

       tuning value

       {

              rest ticks                 ; <- Body

 

              volume value

              frequency value

              loop count

                     ; (Loop Body)

              endloop

 

              noteoff

              end

       }

 

All definitions begin with a label, which contains its name and priority (if a sound effect). Names are composed of one or more ASCII characters (such as “square,” “explosion,” or “powpow”), are used to identify instruments inside of music scripts, and generate equates for sound effects. Priorities are used only for sound effects and represent the importance of the particular sound effect relative to both the music tracks and other sound effects. For instrument definitions, the priority should be excluded.

 

Following the label is the definition’s header data. This specifies the initial state of the instrument or sound effect through various parameter adjustment commands. Nearly all of these parameter adjustment commands are platform specific, so it is best to check the proper usage for your target. Some common parameter adjustment commands are volume, frequency, and tuning. Tuning is a special case, as it is only used for instruments, and ignored for sound effects.

 

The remaining portion of the definition is the body, which specifies how the sound will change over time and is composed of one or more note, flow control, and parameter adjustment commands within { Curly Braces }. Most of these are available for both instruments and sound effects, but noteoff only applies to instruments, and will have no effect on sound effects.

As single-channel sound effects can quickly become a limiting factor in game audio fidelity, some SASS targets support the definition of multi-channel sound effects. These resemble a normal sound effect definition, except with a { Curly Brace } region immediately after the label, containing one or more channel headers and bodies:

name priority                            ; <- Label
{

       volume value                      ; <- 1st Channel Header / Init

       frequency value

       {

              rest ticks                 ; <- 1st Channel Body

              loop count

                     ; (Loop Body)

              endloop

              end

       }

       volume value                      ; <- 2nd Channel Header / Init

       frequency value

       {

              rest ticks                 ; <- 2nd Channel Body

              end

       }
}


Commands may be divided into three categories: note, parameter adjustment, and flow control. Note commands control delays and note off actions. Parameter adjustments control how the instrument or sound effect will actually create its sound, these are also the only commands which may be used in the channel header area. Flow control commands include loops and end markers. A basic command listing follows:

 

       ...           ; When a rest command is encountered in

       noteoff       ; the music script while an instrument

       rest 8        ; is playing, the instrument will start

       end           ; executing the portion of its script

       ...           ; directly following the noteoff command.

 

       rest 14       ; Wait 14 ticks

       end           ; Stop



Platform-specific playback volume and frequency controls.

       ...
      
loop -1                     ; Repeat outer loop forever

              loop 10              ; Inner loop 10 times

                     rest 6

              endloop

       endloop

       end                        ; Stop

 

       ...
      
loop -1                     ; Repeat forever

              rest 18

       endloop                    ; Marks end of above loop

       end                        ; Stop

 

       ...
      
end                        ; Stop



Music Tracks

Music tracks are composed of one or more script blocks which contain commands representing how and when to play instruments, samples, or sound effects. A listing of the format structure is shown below:

name priority                            ; <- Main Block

{

       using instrument

       priority value

 

       call name

       loop count

              ; (Loop Body)

       endloop

 

       rest ticks

       as3 ticks

       wait ticks

       end

}

 

name                                     ; <- Sub Block

{

       ...

       return / break

}

 

 

All script blocks start with a label, which at minimum contain the block’s name. Each music track must contain a single main block which is the first to be played and also contains a starting priority in its label. Names are composed of one or more ASCII characters and priorities are an integer value, with higher values indicating a more important music track. Using a priority of zero will cause the music track to never decode.

 

Following the label, and enclosed between two { Curly Braces } are one or more commands composing the block body. The basic command set may be divided into three categories: notes, parameter adjustment, and flow control. Note commands specify when to turn on and off a particular note (ala key on and key off). Parameter adjustment commands allow control over which macro instrument will be used and how it will be played. Flow control commands change how the SASS script will decode, allowing for loops, calls to different music blocks, and termination of playback.

 

A basic command listing follows:

 

       ...

       c.4 60        ; C-Natural 4th octave for 60 ticks

       rest 10

       bs4 20        ; B-Sharp 4th octave for 20 ticks

       rest 10

       bb2 80        ; B-Flat 2nd octave for 80 ticks

       rest 10

       ...

 

       ...

       as4 20

       rest 30       ; Key off, wait for 30 ticks

       ...

       rest 60       ; Wait for 60 ticks

       ...

 

 

       ...

       wait 60       ; Wait for 60 ticks

       ...

 



       ...

       using piano   ; Using instrument “piano”

       g.3 20        ; The following notes will be played with

       rest 10       ; “piano,” with each note request

       b.3 20        ; specifying a key on, and each rest

       rest 10       ; indicating a key off.

       a.3 20

       rest 10

       ...

 

 

       ...

       priority 120  ; Set priority to 120

       ...

       priority 240  ; Set priority to 240 (higher)

       ...

 



       loop -1                     ; Repeat outer loop forever

              loop 10              ; Repeat inner loop 10 times

                     as4 20

                     rest 20

              endloop

              gs3 30

              rest 20

       endloop

       end                        ; Stop

 

       loop -1                     ; Repeat forever

              fs4 20

              rest 30

              call drumSolo

       endloop                    ; Marks end of above loop

       end                        ; Stop

 

       ...

       call pianoSolo       ; Call the script block below

       ...

}

 

pianoSolo

{

       ...

       return

}

 

       ...

       call pianoSolo

       ...

}

 

pianoSolo

{

       ...

       return        ; Resume decoding after “call pianoSolo”

}

 

mainTrack 120

{

       ...

       call pianoSolo

       ...

}

 

pianoSolo

{

       ...

       call drumminThang

}

 

drumminThang

{

       ...

       break         ; Resumes decoding after “call pianoSolo”

}

 

       ...

       end           ; Stop