How Csound 6 Works

Steven Yi

2nd Csound Conference 2013

2013.10.25


Agenda


  1. History
  2. Overview
  3. The Orchestra
  4. Events and Runtime
  5. Other Features
  6. Conclusions

History

Traditionally, Music-N systems had phases of compilation, based upon a non-interactive model of processing:

  1. Read in Configuration (i.e. commandline-args)
  2. Load text file
  3. Process Orchestra
  4. Process Score
  5. Run to completion

Realtime Updates

At this time, realtime event happen such that the run-loop checked for new input:

  1. Read in Configuration (i.e. commandline-args)
  2. Load text file
  3. Process Orchestra
  4. Process Score
  5. Run Loop
    • Check for new events
    • Process current events
    • Run one block of audio

Other Notable Changes

These changes mark something important

  • Additions of new types (w-sig, f-sig, Strings)
  • Subinstruments/User-Defined Opcodes
  • if-then blocks (Syntax)
  • Csound 5: API

Csound 6

Today's Csound

  • Updated API
  • Transactional Compilation
  • New Type System
  • Generic Arrays

Subtheme: Introducing low-level pieces to the architecture can yield big results to users and developers

Overview

Core Data Abstractions


Key Data Structures

  • Key File: include/csoundCore.h
  • CSOUND
  • Opcode (OENTRY, OPTXT), Instrument (INSTRTXT), Variables (CS_VARIABLE, CS_TYPE, Raw Memory)
  • Opcode Instance (OPDS), Instrument Instance (INSDS)
  • EVENTS: SRTBLK, EVTBLK, char*

OENTRY

Defines an Opcode


     typedef struct oentry {
        char    *opname;
        uint16  dsblksiz;  
        uint16  flags;
        uint8_t thread;
        char    *outypes;
        char    *intypes;
        int     (*iopadr)(CSOUND *, void *p);
        int     (*kopadr)(CSOUND *, void *p);
        int     (*aopadr)(CSOUND *, void *p);
        void    *useropinfo;    /* user opcode parameters */
    } OENTRY; 

    { "vco2",       sizeof(VCO2),       TR, 5,      "a",    "kkoM",
            (SUBR) vco2set, (SUBR) NULL, (SUBR) vco2                    },


  

The Orchestra

Languages and Compilers


  • Languages define the structure of text, often as a set of rules defined in a specification called a grammar

  • Languages also define their semantics, how words come to have meaning

  • Compilers are programs that interpret text according to a language specification and semantic context to generate an output

Phases of a Compiler

Pre-Processor

  • Initial pass over text
  • Reads in Macro definitions
  • Applies Macro definitions
  • Performs #include's, #ifdef's
  • Output is Text
  • Implemented in Engine/csound_prelex.l (Flex input file)

Lexer

  • Processes individual characters and emits tokens
  • Tokens contain groups of characters and a token type
  • "Creates words from letters"
  • Implemented in Engine/csound_orc.l (Flex input file)

Lexer Example

T|h|e|a|t|e|r| |o|f| |t|h|e| |a|b|s|u|r|d|

  1. TOKEN_WORD : "Theater"
  2. TOKEN_WHITESPACE : " "
  3. TOKEN_WORD : "of"
  4. TOKEN_WHITESPACE : " "
  5. TOKEN_WORD : "the"
  6. TOKEN_WHITESPACE : " "
  7. TOKEN_WORD : absurd

Parsers

  • Reads tokens from a lexer stream
  • Groups tokens according to a set of rules (generally into a tree data structure)
  • The set of rules is called a grammar
  • "Makes sentences out of words"
  • Implemented in Engine/csound_orc.y (Bison input file)

Parser Example

sentence: (WORD | SEMICOLON)+ endPunctuation
endPunctuation:  (PERIOD | QMARK | EXCL_POINT)

This is a sentence.
dog; the?
monkey the run a man.
Theater of the absurd!

Well... technically, these are all sentences, but it doesn't make much sense...

Semantic Analysis

  • Meaning of text - Does it make sense, really?
  • Associate Words with Types and Values
  • Look up or define word within a semantic context (similar to a dictionary)
  • Determine what a word means and how it can function within the semantic context.

word => [define: Noun]
word => [Lookup] => Noun
word2 => [Lookup] => ERROR

Example

Grammar: word word word.
Semantics: noun verb noun.

var Food : Edible
var Cat : LivingBeing
LivingBeing.eats(Edible)

Food eats cat.
Fail: Structurally correct sentence, semantically incorrect.

Cat eats food.
Success: Structurally correct sentence, semantically correct.

Semantic Analysis

  • Verification
  • Filling out of Type Tables
  • TREE Markup
  • Flattening of TREE

Semantic Analysis - Implementation (Variables)

  • Key Files:
    • Engine/csound_orc_semantics.c
    • Engine/csound_type_system.c
    • Engine/csound_standard_types.c
  • Verifies the TREE is semantically meaningful using a type table
    • TYPE_TABLE contains two CS_VAR_POOL's at any time, one for the global variables pool, the other for the current block (instr, user-defined opcode)
    • TYPE_TABLE's contain CS_VARIABLE's, which have a variable name and a CS_TYPE
  • Lookup of a variable is done against the TYPE_TABLE
  • Define a variable uses name's first letter for type (currently), using the global TYPE_POOL to look up CS_TYPE's

Semantic Analysis - Implementation (Opcodes)

  • Key Functions: resolve_opcode(), resolve_opcode_get_outArg()
  • Initial opcode lookup is done using global CSOUND->opcodes (CS_HASH_TABLE full of OENTRY's)
  • Returned list of opcodes is then further filtered using *both* the found in-arg's and out-arg's, using type information discovered earlier (i.e. which oscil to use)
  • For situations where opcode lookup is done by in-arg alone, the type of the synthesized variable is assigned to the output type of the found opcode; if more than one opcode fits, there is an ambiguity (function-call syntax)

Semantic Analysis - Implementation (Expressions)

  • Key File: Engine/csound_orc_expression.c
  • Called by Semantic Analyzer during verification
  • Flattens out tree of expressions into a list of opcodes
     
asig = vco2(.25, k1 * 2) #k0 mul k1, 2
#a0 vco2 .25, #k0
asig = #a0
 
if (k1 < 3) then
    k1 = 3
endif
#b0 lt k1, 3
cngoto #b0, __synthetic_0
k1 = 3
__synthetic_0:

Optimizer

  • Key File: Engine/csound_orc_optimize.c
  • Once a text has been verified to be semantically meaningful, it is considered valid.
  • Optimization reduces unnecessary calculations and/or memory requirements (time/space).
  • Implementation here is to perform transformations of the TREE
  • Currently, not much is done in Csound 6's optimizer phase

Compiler

  • Key File: Engine/csound_orc_compile.c
  • Flat TREE* list representation of Instruments and Opcodes become list of INSTRTXT instances, each with a linked-list of OPTXT instances
  • INSTRTXT and OPTXT are what are used at runtime as "templates" for new instrument instances
  • insprep() creates ARG*'s that cache how to hook up memory to each opcode
  • Memory requirements are calculated using CS_VAR_POOL

Transactional Compilation

  • Compilation is done first to an ENGINE_STATE
  • ENGINE_STATE is then merged with the one held in CSOUND struct
  • Protects against bad compiles, changes during processing of audio block
  • Allows multiple compilations of ORC code (i.e. REPL, Live Coding)

Events

Events

Data that describes actions that will affect the runtime state of the Engine

  • Pre-written SCO
  • Runtime SCO (STDIN, API)
  • ORC Generated Events
  • MIDI
  • Remote

Score Compiling (Pre-written SCO)

  • Key Files: Engine/sread.c, Engine/swritestr.c, Engine/scsort.c
  • sread(): Converts text to SRTBLK's, process most of SCO language
    • scochar(): used by sread(), applies score macro's
  • sort(): sorts the SRTBLK's
  • twarp(): applys tempo curves to SRTBLK's (timewarp)
  • swritestr(): writes the SRTBLK's back into a well-formatted string (our pending events)
  • Note: Code still has global style of coding, difficult to understand

Runtime

  • Key Files: Engine/insert.c, Top/csound.c
  • kperf(): perform one block of audio
    • sensevents()
      • turn off expired instruments
      • read in more score events from pending SCO events
      • handle realtime events that have come in (Realtime SCO Event, MIDI, Remote)
    • Run active instruments

Key Actions

  • insert()/instance(): reuse or insert a new instance of an instrument to the active list, prepares it
  • timeexpire()/beatexpire(): check and turns off an instance if p3 is done and also done releaseing (uses deact())
  • fgens.c:hfgens(): process EVTBLK to create f-Tables

Instances

  • the instance function allocates new INSDS, or reuses an existing one
  • Which OENTRY functions to use are selected
  • Memory is "hooked up": vars, pfields, constants are hooked up to opcode instances (OPDS)
  • INSDS is added to the chain of active instances for an instrument

Realtime SCO Events

  • Key File: Engine/linevent.c, Engine/musmon.c
  • From csoundReadScore(), when line events received from STDIN
  • sensLine(): checks and processes the incoming line events string buffer
  • insert_score_event_at_sample(): Creates EVTBLK's and puts in csound->OrcTrigEvts
  • Note: event opcodes use the same insert_score_event_at_sample() function

MIDI

  • MIDIinsert(): functions like insert(), but sets additional values (MIDI key, vel, MCHNLBLK*) that are used by MIDI Opcodes
  • xturnoff(): calls deact(), also clears from MIDI note ons

Runtime Loop Ends

  • Score Ends
  • MIDI Track Ends
  • e-event received
  • API Application calls csoundStop()

Other Features - User-Defined Opcodes

  • Pre-dated by Subinstruments
    • UDO is first read and added to OENTRY list, arg-types recorded
    • UDO is compiled down to an instrument
    • UDO Called
      • UDO Call line uses internal ##userOpcode opcode
      • opcode in turns instantiates an instance of the UDO Instrument template
      • Instrument instance for UDO has additional memory used for arguments
      • Copy values to in-arg memory space
      • Run OPTXT's, xin/xout read from the additional memory space
      • Read values from out-arg memory space, returned to ##userOpcode

Other Features - Channels

  • Key Files: H/bus.h, OOps/bus.c
  • csound->chn_db is a CS_HASH_TABLE used to hold name->channel entry
  • CHNENTRY : holds metadata and data for channel
  • Data may be unique block of memory or other (i.e. global var exported by chnexport)

Other Features - OSC

  • Implemented as Opcodes
  • Does not participate in senseEvents
  • Uses Opcodes to bring external input into Csound world
  • What would be neceesary for OSC to fire events? Set channel data?

Thank You!

  • stevenyi@gmail.com
  • http://www.kunstmusik.com