


                             - 63 -





                            5. CSCORE






     Cscore is a program for generating and manipulating  numeric
score  files.   It  comprises  a  number of function subprograms,
called into operation by a user-written main program.  The  func-
tion  programs augment the C language library functions; they can
optionally read standard numeric score  files,  can  massage  and
expand the data in various ways, then write the data out as a new
score file to be read by a Csound orchestra.

     The user-written main program is  also  in  C.   It  is  not
essential  to  know  the C language well to write a main program,
since the function calls have a simple syntax, and  are  powerful
enough  to do most of the complicated work.  Additional power can
come from C later as the need arises.

Events, Lists, and Operations

     An event in Cscore is equivalent to one statement of a stan-
dard  numeric  score.   It  is  either created or read in from an
existing score file.  An event is comprised of an opcode  and  an
array  of  pfield  values stored somewhere in memory.  Storage is
organized by the following structure:
      struct event {
            char op;          /* opcode */
            char tnum;
            short pcnt;
            float p[PMAX+1];        /* pfields */
      };
Any function subprogram that creates, reads, or copies  an  event
function  will  return a pointer to the storage structure holding
the event data.  The event pointer can be used to access any com-
ponent  of  the structure, in the form of e->op or e->p[n].  Each
newly stored event will  give  rise  to  a  new  pointer,  and  a
sequence  of  new  events  will  generate  a sequence of distinct
pointers that must themselves be stored away.   Groups  of  event
pointers are stored in a list, which has its own structure:
      struct evlist {
            int nslots;       /* size of this list */
            struct event *e[1];     /* list of event pointers */
      };
Any function that creates  or  modifies  a  list  will  return  a
pointer  to the new list.  The list pointer can be used to access
any of its component event pointers,  in  the  form  of  a->e[n].
Event pointers and list pointers are thus primary tools for mani-
pulating the data of a score file.



                        February 20, 1991





                             - 64 -


     Pointers and lists of pointers can be copied  and  reordered
without modifying the data values they refer to.  This means that
notes and phrases can be copied and manipulated from a high level
of  control.  Alternatively, the data within an event or group of
events can  be  modified  without  changing  the  event  or  list
pointers.   Cscore  provides  a library of programming methods or
function subprograms by which scores can be created  and  manipu-
lated in this way.

     In the following summary of Cscore function calls, some sim-
ple naming conventions are used:
      the symbols e, f are pointers to events (notes);
      the symbols a, b are pointers to lists(arrays) of such events;
      the letters ev at the end of a function name signify operation on an event;
      the letter l at the start of a function name signifies operation on a list.

         calling syntax          description

      e = createv(n);         create a blank event with n pfields
      e = defev("...");       defines an event as per the character string ...
      e = copyev(f);          make a new copy of event f
      e = getev();            read the next event in the score input file
      putev(e);               write event e to the score output file
      putstr("...");          write the character string ... to score output

      a = lcreat(n);          create an empty event list with n slots
      a = lappev(a,e);        append event e to list a
      n = lcount(a);          count the events now in list a
      a = lcopy(b);           copy the list b (but not the events)
      a = lcopyev(b);         copy the events of b, making a new list
      a = lget();       read events from score input (to next s or e)
      lput(a);                write the events of list a to score output
      a = lsepf(b);           separate the f statements from list b into list a
      a = lcat(a,b);          concatenate (append) the list b onto the list a
      lsort(a);               sort the list a into chronological order by p[2]
      a = lxins(b,"...");           extract notes of instruments ... (no new events)
      a = lxtimev(b,from,to); extract notes of time-span, creating new events

      relev(e);               release the space of event e
      lrel(a);                release the space of list a (but not events)
      lrelev(a);              release the events of list a, and the list space

Writing a Main program.

     The general format for a main program is:
      #include <csound/cscore.h>
      main()
      {
            /* VARIABLE DECLARATIONS */

            /* PROGRAM BODY */
      }
The include statement will define the event and  list  structures
for  the program.  The following C program will read from a stan-
dard numeric score, up to (but not including) the first  s  or  e


                        February 20, 1991





                             - 65 -


statement, then write that data (unaltered) as output.
      #include <csound/cscore.h>
      main()
      {
            struct evlist *a;       /* a is allowed to point to an event list */

            a = lget();       /* read events in, return the list pointer */
            lput(a);                /* write these events out (unchanged) */
            putstr("e");            /* write the string e to output */
      }

     After execution of lget(), the variable a points to  a  list
of  event  addresses, each of which points to a stored event.  We
have used that same  pointer  to  enable  another  list  function
(lput)  to access and write out all of the events that were read.
If we now define another symbol e to be an  event  pointer,  then
the statement
      e = a->e[4];
will set it to the contents of the 4th slot in the evlist  struc-
ture.   The  contents  is  a pointer to an event, which is itself
comprised of an array of parameter field values.  Thus  the  term
e->p[5] will mean the value of parameter field 5 of the 4th event
in the evlist denoted by a.  The program below will multiply  the
value of that pfield by 2 before writing it out.
      #include <csound/cscore.h>
      main()
      {
            struct event  *e;       /* a pointer to an event */
            struct evlist *a;

            a = lget();       /* read a score as a list of events */
            e = a->e[4];            /* point to event 4 in event list a    */
            e->p[5] *= 2;           /* find pfield 5 and multiply its value by 2 */
            lput(a);                /* write out the list of events*/
            putstr("e");            /* add a "score end" statement*/
      }
Now consider the following score, in  which  p[5]  contains  fre-
quency in cps.
      f 1 0 257 10 1
      f 2 0 257 7 0 300 1 212 .8
      i 1 1 3 0 440 10000
      i 1 4 3 0 256 10000
      i 1 7 3 0 880 10000
      e
If this score were given  to  the  preceding  main  program,  the
resulting output would look like this:
      f 1 0 257 10 1
      f 2 0 257 7 0 300 1 212 .8
      i 1 1 3 0 440 10000
      i 1 4 3 0 512 10000           ; p[5] has become 512 instead of 256.
      i 1 7 3 0 880 10000
      e
Note that the 4th event is in fact the second note of the  score.
So far we have not distinguished between notes and function table
setup in a numeric score.  Both can be classed as  events.   Also


                        February 20, 1991





                             - 66 -


note that our 4th event has been stored in e[4] of the structure.
For compatibility with Csound pfield  notation,  we  will  ignore
p[0]  and  e[0]  of  the event and list structures, storing p1 in
p[1], event 1 in e[1], etc.  The Cscore functions all adopt  this
convention.

     As an extension to the above, we could decide to use a and e
to  examine  each of the events in the list.  Note that e has not
preserved the numeral 4, but  the  contents  of  that  slot.   To
inspect  p5  of the previous listed event we need only redefine e
with the assignment
      e = a->e[3];
More generally, if we declare a new variable f to be a pointer to
a pointer to an event, the statement
      f = &a->e[4];
will set f to the address of the fourth event in the  event  list
a, and *f will signify the contents of the slot, namely the event
pointer itself.  The expression
      (*f)->p[5],
like e->p[5], signifies the fifth pfield of the  selected  event.
However, we can advance to the next slot in the evlist by advanc-
ing the pointer f.  In C this is denoted by f++.

     In the following program we will use the same  input  score.
This  time  we  will separate the ftable statements from the note
statements.  We will next write the three note-events  stored  in
the  list a, then create a second score section consisting of the
original pitch set and a transposed version of itself.  This will
bring about an octave doubling.

     By pointing the variable  f  to  the  first  note-event  and
incrementing  f  inside a while block which iterates n times (the
number of events in the list), one statement can be made  to  act
upon the same pfield of each successive event.
      #include <csound/cscore.h>
      main()
      {
            struct event *e,**f;          /* declarations. see pp.8-9 in the */
            struct evlist *a,*b;          /* C language programming manual */
            int n;

            a = lget();       /* read score into event list "a" */
            b = lsepf(a);           /* separate f statements */
            lput(b);                /* write f statements out to score */
            lrelev(b);              /* and release the spaces used */
            e = defev("t 0 120");   /* define event for tempo statement */
            putev(e);               /* write tempo statement to score */
            lput(a);                /* write the notes */
            putstr("s");            /* section end */
            putev(e);               /* write tempo statement again */
            b = lcopyev(a);         /* make a copy of the notes in "a" */
            n = lcount(b);          /* and count the number copied */
            f = &a->e[1];
            while (n--)       /* iterate the following line n times: */
               (*f++)->p[5] *= .5;  /*   transpose pitch down one octave */


                        February 20, 1991





                             - 67 -


            a = lcat(b,a);          /* now add these notes to original pitches */
            lput(a);
            putstr("e");
      }
The output of this program is:
      f 1 0 257 10 1
      f 2 0 257 7 0 300 1 212 .8
      t 0 120
      i 1 1 3 0 440 10000
      i 1 4 3 0 256 10000
      i 1 7 3 0 880 10000
      s
      t 0 120
      i 1 1 3 0 440 10000
      i 1 4 3 0 256 10000
      i 1 7 3 0 880 10000
      i 1 1 3 0 220 10000
      i 1 4 3 0 128 10000
      i 1 7 3 0 440 10000
      e

     Next we extend the above program by using the  while  state-
ment  to  look  at  p[5]  and  p[6].   In the original score p[6]
denotes amplitude.  To create a diminuendo  in  the  added  lower
octave,  which  is  independent from the original set of notes, a
variable called dim will be used.
      #include <csound/cscore.h>
      main()
      {
            struct event *e,**f;
            struct evlist *a,*b;
            int n, dim;       /* declare new variable as integer */

            a = lget();
            b = lsepf(a);
            lput(b);
            lrelev(b);
            e = defev("t 0 120");
            putev(e);
            lput(a);
            putstr("s");
            putev(e);               /* write out another tempo statement */
            b = lcopyev(a);
            n = lcount(b);
            dim = 0;                /* initialize dim to 0 */
            f = &a->e[1];
            while (n--){
                 (*f)->p[6] -= dim;       /* subtract current value of dim */
                 (*f++)->p[5] *= .5;      /* transpose, move f to next event */
                 dim += 2000;       /* increase dim for each note */
            }
            a = lcat(b,a);
            lput(a);
            putstr("e");
      }


                        February 20, 1991





                             - 68 -


     The increment of f in the above  programs  has  depended  on
certain  precedence  rules  of  C.   Although this keeps the code
tight, the practice can be dangerous for beginners.  Incrementing
may  alternately  be  written  as a separate statement to make it
more clear.
            while (n--){
                (*f)->p[6] -= dim;
                (*f)->p[5] *= .5;
                dim += 2000;
                f++;
            }
Using the same input score again, the output  from  this  program
is:
      f 1 0 257 10 1
      f 2 0 257 7 0 300 1 212 .8
      t 0 120
      i 1 1 3 0 440 10000
      i 1 4 3 0 256 10000
      i 1 7 3 0 880 10000
      s
      t 0 120
      i 1 1 3 0 440 10000           ; Three original notes at
      i 1 4 3 0 256 10000           ; beats 1,4 and 7 with no dim.
      i 1 7 3 0 880 10000
      i 1 1 3 0 220 10000           ; three notes transposed down one octave
      i 1 4 3 0 128 8000            ; also at beats 1,4 and 7 with dim.
      i 1 7 3 0 440 6000
      e

     In the following program the same three-note  sequence  will
be repeated at various time intervals.  The starting time of each
group is determined by the values of the array  cue.   This  time
the  dim.  will  occur  for  each group of notes rather than each
note.  Note the position of the statement  which  increments  the
variable dim outside the inner while block.
      #include <csound/cscore.h>

      int cue[3]={0,10,17};         /* declare array of 3 integers */

      main()
      {
            struct event *e, **f;
            struct evlist *a, *b;
            int n, dim, cuecount, holdn;  /* declare new variables */

            a = lget();
            b = lsepf(a);
            lput(b);
            lrelev(b);
            e = defev("t 0 120");
            putev(e);
            n = lcount(a);
            holdn = n;        /* hold the value of "n" to reset below */
            cuecount = 0;           /* initilize cuecount to "0" */
            dim = 0;


                        February 20, 1991





                             - 69 -


            while (cuecount <= 2) { /* count 3 iterations of inner "while" */
                f = &a->e[1];       /* reset pointer to first event of list "a" */
                n = holdn;          /* reset value of "n" to original note count */
                while (n--) {
                  (*f)->p[6] -= dim;
                  (*f)->p[2] += cue[cuecount];        /* add values of cue */
                  f++;
                }
                printf("%s %d0, "; diagnostic:  cue=", cue[cuecount]);
                cuecount++;
                dim += 2000;
                lput(a);
            }
            putstr("e");
      }


     Here the inner while block looks at the  events  of  list  a
(the notes) and the outer while block looks at each repetition of
the events of list a (the pitch group repetitions).  This program
also  demonstrates  a  useful  trouble-shooting  device  with the
printf function. The semi-colon is first in the character  string
to  produce  a comment statement in the resulting score file.  In
this case the value of cue is being  printed  in  the  output  to
insure  that the program is taking the proper array member at the
proper time. When output data is  wrong  or  error  messages  are
encountered,  the  printf function can help to pinpoint the prob-
lem.

Using the identical input file, the C  program  above  will  gen-
erate:

      f 1 0 257 10 1
      f 2 0 257 7 0 300 1 212 .8
      t 0 120

      ; diagnostic:  cue= 0
      i 1 1 3 0 440 10000
      i 1 4 3 0 256 10000
      i 1 7 3 0 880 10000

      ; diagnostic:  cue= 10
      i 1 11 3 0 440 8000
      i 1 14 3 0 256 8000
      i 1 17 3 0 880 8000

      ; diagnostic:  cue= 17
      i 1 28 3 0 440 4000
      i 1 31 3 0 256 4000
      i 1 34 3 0 880 4000
      e


     Further development of these scores will lead  the  composer
to  techniques  of score manipulation which are similar to serial


                        February 20, 1991





                             - 70 -


techniques of composition.  Pitch sets may be altered with regard
to  any of the parameter fields.  The programing allows for tran-
spositions, time warping, pitch  retrograding  and  dynamic  con-
trols, to name a few.


Compiling a Cscore program

     A Cscore program named example.c can be compiled and  linked
with its library modules by the command:
      $ cc example.c -lcscore
The resulting executable file is called "a.out".  It  is  run  by
typing:
      $ a.out                       (no input, output printed on the screen)

      $ a.out < scorin              (input score named scorin, output on screen)

      $ a.out < scorin > scorout          (input as above, output into a file)







































                        February 20, 1991


