Csound

THE CSOUND API

An application programming interface (API) is an interface provided by a computer system, library or application that allows users to access functions and routines for a particular task. It gives developers a way to harness the functionality of existing software within a host application. The Csound API can be used to control an instance of Csound through a series of different functions thus making it possible to harness all the power of Csound in one’s own applications. In other words, almost anything that can be done within Csound can be done with the API. The API is written in C, but there are interfaces to other languages as well, such as Python, C++ and Java.   

To use the Csound C API, you have to include csound.h in your source file and to link your code with libcsound. Here is an example of the csound command line application written using the Csound C API:

#include <csound/csound.h>

int main(int argc, char **argv)
{
  CSOUND *csound = csoundCreate(NULL);
  int result = csoundCompile(csound, argc, argv);
  if (result == 0) {
    result = csoundPerform(csound);
  }
  csoundDestroy(csound);
  return (result >= 0 ? 0 : result);
}

First we create an instance of Csound. To do this we call csoundCreate() which returns an opaque pointer that will be passed to most Csound API functions. Then we compile the orc/sco files or the csd file given as input arguments through the argv parameter of the main function. If the compilation is successful (result == 0), we call the csoundPerform() function. csoundPerform() will cause Csound to perform until the end of the score is reached. When this happens csoundPerform() returns a non-zero value and we destroy our instance before ending the program.

On a linux system, with libcsound named libcsound64 (double version of the csound library), supposing that all include and library paths are set correctly, we would build the above example with the following command (notice the use of the -DUSE_DOUBLE flag to signify that we compile against the 64 bit version of the csound library):

gcc -DUSE_DOUBLE -o csoundCommand csoundCommand.c -lcsound64

 The command for building with a 32 bit version of the library would be:

gcc -o csoundCommand csoundCommand.c -lcsound

Within the C or C++ examples of this chapter, we will use the MYFLT type for the audio samples. Doing so, the same source files can be used for both development (32 bit or 64 bit), the compiler knowing how to interpret MYFLT as double if the macro USE_DOUBLE is defined, or as float if the macro is not defined.

The C API has been wrapped in a C++ class for convenience. This gives the Csound basic C++ API. With this API, the above example would become:

#include <csound/csound.hpp>

int main(int argc, char **argv)
{
  Csound *cs = new Csound();
  int result = cs->Compile(argc, argv);
  if (result == 0) {
    result = cs->Perform();
  }
  return (result >= 0 ? 0 : result);
}

Here, we get a pointer to a Csound object instead of the csound opaque pointer. We call methods of this object instead of C functions, and we don't need to call csoundDestroy in the end of the program, because the C++ object destruction mechanism takes care of this. On our linux system, the example would be built with the following command:

g++ -DUSE_DOUBLE -o csoundCommandCpp csoundCommand.cpp -lcsound64

The Csound API has also been wrapped to other languages. The Csound Python API wraps the Csound API to the Python language. To use this API, you have to import the csnd module. The csnd module is normally installed in the site-packages or dist-packages directory of your python distribution as a csnd.py file. Our csound command example becomes:

import sys
import csnd

def csoundCommand(args):
    csound = csnd.Csound()
    arguments = csnd.CsoundArgVList()
    for s in args:
        arguments.Append(s)
    result = csound.Compile(arguments.argc(), arguments.argv())
    if result == 0:
        result = csound.Perform()
    return result

def main():
    csoundCommand(sys.argv)

if __name__ =='__main__':
    main()

We use a Csound object (remember Python has OOp features). Note the use of the CsoundArgVList helper class to wrap the program input arguments into a C++ manageable object. In fact, the Csound class has syntactic sugar (thanks to method  overloading) for the Compile method. If you have less than six string arguments to pass to this method, you can pass them directly. But here, as we don't know the number of arguments to our csound command, we use the more general mechanism of the CsoundArgVList helper class.

The Csound Java API wraps the Csound API to the Java language. To use this API, you have to import the csnd package. The csnd package is located in the csnd.jar archive which has to be known from your Java path. Our csound command example becomes:

import csnd.*;

public class CsoundCommand
{
  private Csound csound = null;
  private CsoundArgVList arguments = null;

  public CsoundCommand(String[] args) {
    csound = new Csound();
    arguments = new CsoundArgVList();
    arguments.Append("dummy");
    for (int i = 0; i < args.length; i++) {
      arguments.Append(args[i]);
    }
    int result = csound.Compile(arguments.argc(), arguments.argv());
    if (result == 0) {
      result = csound.Perform();
    }
    System.out.println(result);
  }


  public static void main(String[] args) {
    CsoundCommand csCmd = new CsoundCommand(args);
  }
}

Note the "dummy" string as first argument in the arguments list. C, C++ and Python expect that the first argument in a program argv input array is implicitly the name of the calling program. This is not the case in Java: the first location in the program argv input array contains the first command line argument if any.  So we have to had this "dummy" string value in the first location of the arguments array so that the C API function called by our csound.Compile method is happy.

This illustrates a fundamental point about the Csound API. Whichever API wrapper is used (C++, Python, Java, etc), it is the C API which is working under the hood. So a thorough knowledge of the Csound C API is highly recommended if you plan to use the Csound API in any of its different flavours. The main source of information about the Csound C API is the csound.h header file which is fully commented.

On our linux system, with csnd.jar located in /usr/local/lib/csound/java, our Java Program would be compiled and run with the following commands:

javac -cp /usr/local/lib/csound/java/csnd.jar CsoundCommand.java
java -cp /usr/local/lib/csound/java/csnd.jar:. CsoundCommand

There also exists an extended Csound C++ API, which adds to the Csound C++ API a CsoundFile class, the CsoundAC C++ API, which provides a class hierarchy for doing algorithmic composition using Michael Gogins' concept of music graphs, and API wrappers for the LISP, LUA and HASKELL languages.

For now, in this chapter we will focus on the basic C/C++ API, and the Python and Java API.

Threading

Before we begin to look at how to control Csound in real time we need to look at threads. Threads are used so that a program can split itself into two or more simultaneously running tasks. Multiple threads can be executed in parallel on many computer systems. The advantage of running threads is that you do not have to wait for one part of your software to finish executing before you start another.

In order to control aspects of your instruments in real time your will need to employ the use of threads. If you run the first example found on this page you will see that the host will run for as long as csoundPerform() returns 0. As soon as it returns non-zero it will exit the loop and cause the application to quit. Once called, csoundPerform() will cause the program to hang until it is finished. In order to interact with Csound while it is performing you will need to call csoundPerform() in a separate unique thread. 

When implementing threads using the Csound API, we must define a special performance function thread. We then pass the name of this performance function to csoundCreateThread(), thus registering our performance-thread function with Csound. When defining a Csound performance-thread routine you must declare it to have a return type uintptr_t, hence it will need to return a value when called. The thread function will take only one parameter, a pointer to void. This pointer to void is quite important as it allows us to pass important data from the main thread to the performance thread. As several variables are needed in our thread function the best approach is to create a user defined data structure that will hold all the information your performance thread will need. For example:

typedef struct { 
  int result;        /* result of csoundCompile() */ 
  CSOUND *csound;    /* instance of csound */
  bool PERF_STATUS;  /* performance status */ 
} userData; 

Below is a basic performance-thread routine. *data is cast as a userData data type so that we can access its members. 

uintptr_t csThread(void *data)
{
  userData *udata = (userData *)data;
  if (!udata->result) {
    while ((csoundPerformKsmps(udata->csound) == 0) &&
           (udata->PERF_STATUS == 1));
    csoundDestroy(udata->csound);
  }
  udata->PERF_STATUS = 0;
  return 1;
}          

In order to start this thread we must call the csoundCreateThread() API function which is declared in csound.h as:  

void *csoundCreateThread(uintptr_t (*threadRoutine (void *),
                         void *userdata);  

If you are building a command line program you will need to use some kind of mechanism to prevent int main() from returning until after the performance has taken place. A simple while loop will suffice. 

The first example presented above can now be rewritten to include a unique performance thread:


#include <stdio.h> 
#include <csound/csound.h> 

uintptr_t csThread(void *clientData); 

typedef struct { 
  int result; 
  CSOUND *csound; 
  int PERF_STATUS; 
} userData; 

int main(int argc, char *argv[]) 
{
  int finish;
  void *ThreadID; 
  userData *ud; 
  ud = (userData *)malloc(sizeof(userData));  
  MYFLT *pvalue; 
  ud->csound = csoundCreate(NULL);  
  ud->result = csoundCompile(ud->csound, argc, argv); 

  if (!ud->result) {  
    ud->PERF_STATUS = 1; 
    ThreadID = csoundCreateThread(csThread, (void *)ud); 
  } 
  else { 
    return 1; 
  }  

  /* keep performing until user types a number and presses enter */
  scanf("%d", &finish);
  ud->PERF_STATUS = 0; 
  csoundDestroy(ud->csound); 
  free(ud);  
  return 0; 
} 

/* performance thread function */
uintptr_t csThread(void *data) 
{ 
  userData *udata = (userData *)data; 
  if (!udata->result) {
    while ((csoundPerformKsmps(udata->csound) == 0) &&
           (udata->PERF_STATUS == 1));
    csoundDestroy(udata->csound); 
  }        
  udata->PERF_STATUS = 0;    
  return 1; 
}  

The application above might not appear all that interesting. In fact it's almost the exact same as the first example presented except that users can now stop Csound by hitting 'enter'.  The real worth of threads can only be appreciated when you start to control your instrument in real time.

 

Channel I/O

The big advantage to using the API is that it allows a host to control your Csound instruments in real time. There are several mechanisms provided by the API that allow us to do this. The simplest mechanism makes use of a 'software bus'.

The term bus is usually used to describe a means of communication between hardware components. Buses are used in mixing consoles to route signals out of the mixing desk into external devices. Signals get sent through the sends and are taken back into the console through the returns. The same thing happens in a software bus, only instead of sending analog signals to different hardware devices we send data to and from different software. 

Using one of the software bus opcodes in Csound we can provide an interface for communication with a host application. An example of one such opcode is chnget. The chnget opcode reads data that is being sent from a host Csound API application on a particular named channel, and assigns it to an output variable. In the following example instrument 1 retrieves any data the host may be sending on a channel named "pitch":

instr 1 
kfreq chnget "pitch" 
asig  oscil  10000, kfreq, 1 
      out    asig
endin 

One way in which data can be sent from a host application to an instance of Csound is through the use of the csoundGetChannelPtr() API function which is defined in csound.h as:   

int csoundGetChannelPtr(CSOUND *, MYFLT **p, const char *name,  int type);

CsoundGetChannelPtr() stores a pointer to the specified channel of the bus in p. The channel pointer p is of type MYFLT *. The argument name is the name of the channel and the argument type is a bitwise OR of exactly one of the following values:  

CSOUND_CONTROL_CHANNEL - control data (one MYFLT value) 
CSOUND_AUDIO_CHANNEL - audio data (ksmps MYFLT values) 
CSOUND_STRING_CHANNEL - string data (MYFLT values with enough space to store csoundGetStrVarMaxLen(CSOUND*) characters, including the NULL character at the end of the string)   

and at least one of these:  

CSOUND_INPUT_CHANNEL - when you need Csound to accept incoming values from a host
CSOUND_OUTPUT_CHANNEL - when you need Csound to send outgoing values to a host 

If the call to csoundGetChannelPtr() is successful the function will return zero. If not, it will return a negative error code. We can now modify our previous code in order to send data from our application on a named software bus to an instance of Csound using csoundGetChannelPtr().

#include <stdio.h> 
#include <csound/csound.h>

/* performance thread function prototype */
uintptr_t csThread(void *clientData);

/* userData structure declaration */
typedef struct {
  int result;
  CSOUND *csound;
  int PERF_STATUS;
} userData;

/*-----------------------------------------------------------
 * main function
 *-----------------------------------------------------------*/
int main(int argc, char *argv[])
{
  int userInput = 200;
  void *ThreadID;
  userData *ud;
  ud = (userData *)malloc(sizeof(userData));
  MYFLT *pvalue;
  ud->csound = csoundCreate(NULL);
  ud->result = csoundCompile(ud->csound, argc, argv);
  if (csoundGetChannelPtr(ud->csound, &pvalue, "pitch",
          CSOUND_INPUT_CHANNEL | CSOUND_CONTROL_CHANNEL) != 0) {
    printf("csoundGetChannelPtr could not get the \"pitch\" channel");
    return 1;
  }
  if (!ud->result) {
    ud->PERF_STATUS = 1;
    ThreadID = csoundCreateThread(csThread, (void*)ud);
  }
  else {
    printf("csoundCompiled returned an error");
    return 1;
  }
  printf("\nEnter a pitch in Hz(0 to Exit) and type return\n");
  while (userInput != 0) {
    *pvalue = (MYFLT)userInput;
    scanf("%d", &userInput);
  }
  ud->PERF_STATUS = 0;
  csoundDestroy(ud->csound);
  free(ud);
  return 0;
}

/*-----------------------------------------------------------
 * definition of our performance thread function
 *-----------------------------------------------------------*/
uintptr_t csThread(void *data)
{
  userData *udata = (userData *)data;
  if (!udata->result) {
    while ((csoundPerformKsmps(udata->csound) == 0) &&
           (udata->PERF_STATUS == 1));
    csoundDestroy(udata->csound);
  }
  udata->PERF_STATUS = 0;
  return 1;
} 

 

Score Events 

Adding score events to the csound instance is easy to do. It requires that csound has its threading done, see the paragraph above on threading. To enter a score event into csound, one calls the following function:

void myInputMessageFunction(void *data, const char *message)
{
  userData *udata = (userData *)data;
  csoundInputMessage(udata->csound, message );
}

Now we can call that function to insert Score events into a running csound instance. The formatting of the message should be the same as one would normally have in the Score part of the .csd file. The example shows the format for the message. Note that if you're allowing csound to print its error messages, if you send a malformed message, it will warn you. Good for debugging. There's an example with the csound source code that allows you to type in a message, and then it will send it.

/*                     instrNum  start  duration   p4   p5   p6  ... pN */
const char *message = "i1        0      1          0.5  0.3  0.1";
myInputMessageFunction((void*)udata, message);


Callbacks

Csound can call subroutines declared in the host program when some special events occur. This is done through the callback mechanism. One has to declare to Csound the existence of a callback routine using an API setter function. Then when a corresponding event occurs during performance, Csound will call the host callback routine, eventually passing some arguments to it.

The example below shows a very simple command line application allowing the user to rewind the score or to abort the performance. This is achieved by reading characters from the keyboard: 'r' for rewind and 'q' for quit. During performance, Csound executes a loop. Each pass in the loop yields ksmps audio frames. Using the API csoundSetYieldCallback function, we can tell to Csound to call our own routine after each pass in its internal loop.

The yieldCallback routine must be non-blocking. That's why it is a bit tricky to force the C getc function to be non-blocking. To enter a character, you have to type the character and then hit the return key.

#include <csound/csound.h>

int yieldCallback(CSOUND *csound)
{
  int fd, oldstat, dummy;
  char ch;

  fd = fileno(stdin);
  oldstat = fcntl(fd, F_GETFL, dummy);
  fcntl(fd, F_SETFL, oldstat | O_NDELAY);
  ch = getc(stdin);
  fcntl(fd, F_SETFL, oldstat);
  if (ch == -1)
    return 1;
  switch (ch) {
  case 'r':
    csoundRewindScore(csound);
    break;
  case 'q':
    csoundStop(csound);
    break;
  }
  return 1;
}

int main(int argc, char **argv)
{
  CSOUND *csound = csoundCreate(NULL);
  csoundSetYieldCallback(csound, yieldCallback);
  int result = csoundCompile(csound, argc, argv);
  if (result == 0) {
    result = csoundPerform(csound);
  }
  csoundDestroy(csound);
  return (result >= 0 ? 0 : result);
}

The user can also set callback routines for file open events, real-time audio events, real-time MIDI events, message events, keyboards events, graph events,  and channel invalue and outvalue events.

CsoundPerformanceThread: a Swiss Knife for the API

Beside the API, Csound provides a helper C++ class to facilitate threading issues: CsoundPerformanceThread. This class performs a score in a separate thread, allowing the host program to do its own processing in its main thread during the score performance. The host program will communicate with the CsoundPerformanceThread class by sending messages to it, calling CsoundPerformanceThread methods. Those messages are queued inside CsoundPerformanceThread and are treated in a first in first out (FIFO) manner.

The example below is equivalent to the example in the callback section. But this time, as the characters are read in a different thread, there is no need to have a non-blocking character reading routine.

#include <csound/csound.hpp>
#include <csound/csPerfThread.hpp>

#include <iostream>
using namespace std;

int main(int argc, char **argv)
{
  Csound *cs = new Csound();
  int result = cs->Compile(argc, argv);
  if (result == 0) {
    CsoundPerformanceThread *pt = new CsoundPerformanceThread(cs);
    pt->Play();
    while (pt->GetStatus() == 0) {
      char c = cin.get();
      switch (c) {
      case 'r':
        cs->RewindScore();
        break;
      case 'q':
        pt->Stop();
        pt->Join();
        break;
      }
    }
  }
  return (result >= 0 ? 0 : result);
}

Because CsoundPerformanceThread is not part of the API, we have to link to libcsnd to get it working:

g++ -DUSE_DOUBLE -o threadPerf threadPerf.cpp -lcsound64 -lcsnd

When using this class from Python or Java, this is not an issue because the csnd.py module and the csnd.jar package include the API functions and classes, and the CsoundPerformanceThread class as well.

Here is a more complete example which could be the base of a frontal application to run Csound. The host application is modeled through the CsoundSession class which has its own event loop (mainLoop). CsoundSession inherits from the API Csound class and it embeds an object of type CsoundPerformanceThread. Most of the CsoundPerformanceThread class methods are used.

#include <csound/csound.hpp>
#include <csound/csPerfThread.hpp>

#include <iostream>
#include <string>
using namespace std;

class CsoundSession : public Csound
{
public:
  CsoundSession(string const &csdFileName = "") : Csound() {
    m_pt = NULL;
    m_csd = "";
    if (!csdFileName.empty()) {
      m_csd = csdFileName;
      startThread();
    }
  };

  void startThread() {
    if (Compile((char *)m_csd.c_str()) == 0 ) {
      m_pt = new CsoundPerformanceThread(this);
      m_pt->Play();
    }
  };

  void resetSession(string const &csdFileName) {
    if (!csdFileName.empty())
      m_csd = csdFileName;
    if (!m_csd.empty()) {
      stopPerformance();
      startThread();
    }
  };

  void stopPerformance() {
    if (m_pt) {
      if (m_pt->GetStatus() == 0)
        m_pt->Stop();
      m_pt->Join();
      m_pt = NULL;
    }
    Reset();
  };

  void mainLoop() {
    string s;
    bool loop = true;
    while (loop) {
      cout << endl << "l)oad csd; e(vent; r(ewind; t(oggle pause; s(top; p(lay; q(uit: ";
      char c = cin.get();
      switch (c) {
      case 'l':
        cout << "Enter the name of csd file:";
        cin >> s;
        resetSession(s);
        break;
      case 'e':
        cout << "Enter a score event:";
        cin.ignore(1000, '\n'); //a bit tricky, but well, this is C++!
        getline(cin, s);
        m_pt->InputMessage(s.c_str());
        break;
      case 'r':
        RewindScore();
        break;
      case 't':
        if (m_pt)
          m_pt->TogglePause();
        break;
      case 's':
        stopPerformance();
        break;
      case 'p':
        resetSession("");
        break;
      case 'q':
        if (m_pt) {
          m_pt->Stop();
          m_pt->Join();
        }
        loop = false;
        break;
      }
      cout << endl;
    }
  };

private:
  string m_csd;
  CsoundPerformanceThread *m_pt;
};

int main(int argc, char **argv)
{
  string csdName = "";
  if (argc > 1)
    csdName = argv[1];
  CsoundSession *session = new CsoundSession(csdName);
  session->mainLoop();
}

There are also methods in CsoundPerformanceThread for sending score events (ScoreEvent), for moving the time pointer (SetScoreOffsetSeconds), for setting a callback function (SetProcessCallback) to be called at the end of each pass in the process loop, and for flushing the message queue (FlushMessageQueue).

As an exercise, the user should complete this example using the methods above and then try to rewrite the example in Python and/or in Java.

Csound6

With Csound6, the API changed a lot, breaking backward compatibility.

The Python module for the API is called now csnd6 instead of csnd and the corresponding Java package is called now csnd6.jar instead of csnd.jar. To use the CsoundPerformanceThread class from C++, one have to link to libcsnd6 instead of libcsnd.

As usual the best source of information is the csound.h header file. Comparing the Csound6 version of this file with the Csound5 version we see that it has been highly refactored, that many new functions have been added and that some functions have been renamed, or got new signatures, or have been removed.

Let us review this by sections:

Instantiation

csoundInitialize() has a new signature: (int flags) instead of (int *argc, char ***argv, int flags). The first two arguments were never used. The flags argument can be a bitwise or of the two values CSOUNDINIT_NO_SIGNAL_HANDLER and CSOUNDINIT_NO_ATEXIT. With the first value, Csound will react to an operating system interrupt signal in a custom way instead of the classical "Csound tidy up". The second value is for Windows systems only and tells Csound to destroy all instances when exiting. csoundCreate() calls csoundInitialize() with no flags. So if none of the above options are needed, csoundCreate() is enough to create an instance of Csound.

csoundPreCompile() has been removed.

Performance

Seven new functions:

TREE *csoundParseOrc(CSOUND *csound, const char *str)
int csoundCompileTree(CSOUND *csound, TREE *root)
void csoundDeleteTree(CSOUND *csound, TREE *tree)
int csoundCompileOrc(CSOUND *csound, const char *str)
MYFLT csoundEvalCode(CSOUND *csound, const char *str)
int csoundCompileArgs(CSOUND *, int argc, char **argv)
int csoundStart(CSOUND *csound)



 

csoundCompileFromStrings() has been removed.


Score Handling

One new function: int csoundReadScore(CSOUND *csound, char *str)


Attributes

Five new configuration/parameter getting and setting functions:

uint32_t csoundGetNchnlsInput(CSOUND *csound)
int64_t csoundGetCurrentTimeSamples(CSOUND *csound)
int csoundSetOption(CSOUND *csound, char *option)
void csoundSetParams(CSOUND *csound, CSOUND_PARAMS *p)
void csoundGetParams(CSOUND *csound, CSOUND_PARAMS *p)

 

General Input/Ouput

Seven new getting and setting functions for managing audio and/or midi input and output device names:

const char *csoundGetOutputName(CSOUND *)
void csoundSetOutput(CSOUND *csound, char *name, char *type, char *format)
void csoundSetInput(CSOUND *csound, char *name)
void csoundSetMIDIInput(CSOUND *csound, char *name)
void csoundSetMIDIFileInput(CSOUND *csound, char *name)
void csoundSetMIDIOutput(CSOUND *csound, char *name)
void csoundSetMIDIFileOutput(CSOUND *csound, char *name)

 

Here is a C++ example illustrating the new API functions presented in the above sections:

#include <csound/csound.hpp>
#include <csound/csPerfThread.hpp>

#include <iostream>
#include <string>
#include <vector>
using namespace std;

string orc1 =
"instr 1              \n"
"idur = p3            \n"
"iamp = p4            \n"
"ipch = cpspch(p5)    \n"
"kenv linen  iamp, 0.05, idur, 0.1 \n"
"a1   poscil kenv, ipch \n"
"     out    a1         \n"
"endin";

string orc2 =
"instr 1    \n"
"idur = p3  \n"
"iamp = p4  \n"
"ipch = cpspch(p5)  \n"
"a1 foscili iamp, ipch, 1, 1.5, 1.25  \n"
"   out     a1      \n"
"endin\n";

string orc3 =
"instr 1    \n"
"idur = p3  \n"
"iamp = p4  \n"
"ipch = cpspch(p5-1)         \n"
"kenv  linen    iamp, 0.05, idur, 0.1  \n"
"asig  rand     0.45         \n"
"afilt moogvcf2 asig, ipch*4, ipch/(ipch * 1.085)  \n"
"asig  balance  afilt, asig  \n"
"      out      kenv*asig    \n"
"endin\n";

string sco1 =
"i 1 0 1    0.5 8.00\n"
"i 1 + 1    0.5 8.04\n"
"i 1 + 1.5  0.5 8.07\n"
"i 1 + 0.25 0.5 8.09\n"
"i 1 + 0.25 0.5 8.11\n"
"i 1 + 0.5  0.8 9.00\n";

string sco2 =
"i 1 0 1    0.5 9.00\n"
"i 1 + 1    0.5 8.07\n"
"i 1 + 1    0.5 8.04\n"
"i 1 + 1    0.5 8.02\n"
"i 1 + 1    0.5 8.00\n";

string sco3 =
"i 1 0 0.5  0.5 8.00\n"
"i 1 + 0.5  0.5 8.04\n"
"i 1 + 0.5  0.5 8.00\n"
"i 1 + 0.5  0.5 8.04\n"
"i 1 + 0.5  0.5 8.00\n"
"i 1 + 0.5  0.5 8.04\n"
"i 1 + 1.0  0.8 8.00\n";

void noMessageCallback(CSOUND* cs, int attr, const char *format, va_list valist)
{
  // Do nothing so that Csound will not print any message,
  // leaving a clean console for our app
  return;
}

class CsoundSession : public Csound
{
public:
  CsoundSession(vector<string> & orc, vector<string> & sco) : Csound() {
    m_orc = orc;
    m_sco = sco;
    m_pt = NULL;
  };

  void mainLoop() {
    SetMessageCallback(noMessageCallback);
    SetOutput((char *)"dac", NULL, NULL);
    GetParams(&m_csParams);
    m_csParams.sample_rate_override = 48000;
    m_csParams.control_rate_override = 480;
    m_csParams.e0dbfs_override = 1.0;
    // Note that setParams is called before first compilation
    SetParams(&m_csParams);
    if (CompileOrc(orc1.c_str()) == 0) {
      Start(this->GetCsound());
      // Just to be sure...
      cout << GetSr() << ", " << GetKr() << ", ";
      cout << GetNchnls() << ", " << Get0dBFS() << endl;
      m_pt = new CsoundPerformanceThread(this);
      m_pt->Play();
    }
    else {
      return;
    }

    string s;
    TREE *tree;
    bool loop = true;
    while (loop) {
      cout << endl << "1) 2) 3): orchestras, 4) 5) 6): scores; q(uit: ";
      char c = cin.get();
      cin.ignore(1, '\n');
      switch (c) {
      case '1':
        tree = ParseOrc(m_orc[0].c_str());
        CompileTree(tree);
        DeleteTree(tree);
        break;
      case '2':
        CompileOrc(m_orc[1].c_str());
        break;
      case '3':
        EvalCode(m_orc[2].c_str());
        break;
      case '4':
        ReadScore((char *)m_sco[0].c_str());
        break;
      case '5':
        ReadScore((char *)m_sco[1].c_str());
        break;
      case '6':
        ReadScore((char *)m_sco[2].c_str());
        break;
      case 'q':
        if (m_pt) {
          m_pt->Stop();
          m_pt->Join();
        }
        loop = false;
        break;
      }
    }
  };

private:
  CsoundPerformanceThread *m_pt;
  CSOUND_PARAMS m_csParams;
  vector<string> m_orc;
  vector<string> m_sco;
};

int main(int argc, char **argv)
{
  vector<string> orc;
  orc.push_back(orc1);
  orc.push_back(orc2);
  orc.push_back(orc3);
  vector<string> sco;
  sco.push_back(sco1);
  sco.push_back(sco2);
  sco.push_back(sco3);
  CsoundSession *session = new CsoundSession(orc, sco);
  session->mainLoop();
}

  

Realtime Audio I/O

Four new functions for dealing with realtime audio modules:

void csoundSetRTAudioModule(CSOUND *csound, char *module)
int csoundGetModule(CSOUND *csound, int number, char **name, char **type)
int csoundGetAudioDevList(CSOUND *csound, CS_AUDIODEVICE *list, int isOutput)
void csoundSetAudioDeviceListCallback(CSOUND *csound,
       int (*audiodevlist__)(CSOUND *, CS_AUDIODEVICE *list, int isOutput))

 

Realtime Midi I/O

Four new functions for dealing with realtime Midi modules: 

void csoundSetMIDIModule(CSOUND *csound, char *module)
void csoundSetHostImplementedMIDIIO(CSOUND *csound, int state)
int csoundGetMIDIDevList(CSOUND *csound, CS_MIDIDEVICE *list, int isOutput)
void csoundSetMIDIDeviceListCallback(CSOUND *csound,
       int (*mididevlist__)(CSOUND *, CS_MIDIDEVICE *list, int isOutput))


 

Message and Text

One new message function:

void csoundSetDefaultMessageCallback(
     void (*csoundMessageCallback_)(CSOUND *, int attr,
                                                       const char *format,
                                                       va_list valist))


 

void csoundCreateMessageBuffer(CSOUND *csound, int toStdOut) replaces csoundEnableMessageBuffer().


Channels, Control and Events

Historically there were several ways of sending data to and from Csound through software buses:

  • numbered channels with no callback (opcodes chani and chano with API functions csoundChanOKGet(), etc)
  • named channels with no callback (opcodes chnget and chnset with API  function csoundGetChannelPtr())
  • named channels with callback (opcodes chnrecv and chnsend) with API function csoundSetChannelIOCallback())
  • named channels with callback (opcodes invalue and outvalue with API  functions csoundSetOutputValueCallback(), etc)

A bit confusing!

This has been simplified in two categories:

Named Channels with no Callback

This category uses csoundGetChannelPtr() as in Csound5 to get a pointer to the data of the named channel. There are also six new functions to send data to and from a named channel in a thread safe way:

MYFLT csoundGetControlChannel(CSOUND *csound, const char *name, int *err)
void csoundSetControlChannel(CSOUND *csound, const char *name, MYFLT val)
void csoundGetAudioChannel(CSOUND *csound, const char *name, MYFLT *samples)
void csoundSetAudioChannel(CSOUND *csound, const char *name, MYFLT *samples)
void csoundGetStringChannel(CSOUND *csound, const char *name, char *string)
void csoundSetStringChannel(CSOUND *csound, const char *name, char *string)

 

The opcodes concerned are chani, chano, chnget and chnset. When using numbered channels with chani and chano, the API sees those channels as named channels, the name being derived from the channel number (i.e. 1 gives "1", 17 gives "17", etc).

There is also a new helper function returning the data size of a named channel:

int csoundGetChannelDatasize(CSOUND *csound, const char *name)

It is particularly useful when dealing with string channels.

The following functions have been removed: csoundChanIKSet(), csoundChanOKGet(), csoundChanIASet(), csoundChanOAGet(), csoundChanIKSetValue(), csoundChanOKGetValue(), csoundChanIASetSample(), and  csoundChanOAGetSample().

Named Channels with Callback

Each time a named channel with callback is used (opcodes invalue, outvalue, chnrecv, and chnsend), the corresponding callback registered by one of those functions will be called:

void csoundSetInputChannelCallback(CSOUND *csound,
                                                           channelCallback_t inputChannelCalback)
void csoundSetOutputChannelCallback(CSOUND *csound,
                                                              channelCallback_t outputChannelCalback)

 

These functions replace csoundSetInputValueCallback() and csoundSetOutputValueCallback(), which are still in the header file but are now deprecated.

Other Channel Functions

int csoundSetPvsChannel(CSOUND *, const PVSDATEXT *fin, const char *name), and
int csoundGetPvsChannel(CSOUND *csound, PVSDATEXT *fout, const char *name)


replace csoundPvsinSet() and csoundPvsoutGet().

int csoundSetControlChannelHints(CSOUND *, const char *name,
                                                        controlChannelHints_t hints), and
int csoundGetControlChannelHints(CSOUND *, const char *name,
                                                        controlChannelHints_t *hints)

replace csoundSetControlChannelParams() and csoundGetControlChannelParams().

int *csoundGetChannelLock(CSOUND *, const char *name) has a new signature: the third argument has been removed.

int csoundKillInstance(CSOUND *csound, MYFLT instr,
                                    char *instrName, int mode, int allow_release)

kills off one or more running instances of an instrument.

int csoundRegisterKeyboardCallback(CSOUND *,
                                    int (*func)(void *userData, void *p, unsigned int type),
                                    void *userData, unsigned int type), and
void csoundRemoveKeyboardCallback(CSOUND *csound,
                                    int (*func)(void *, void *, unsigned int))
replace csoundSetCallback() and csoundRemoveCallback().

Tables

Two new functions to copy data from a table to a host array, or from a host array to a table in a thread safe way:

void csoundTableCopyOut(CSOUND *csound, int table, MYFLT *dest), and
void csoundTableCopyIn(CSOUND *csound, int table, MYFLT *src)
 

Miscellaneous Functions

One can now create a circular buffer with elements of any type. Thus the existing functions csoundCreateCircularBuffer(), csoundReadCircularBuffer(), and csoundWriteCircularBuffer() have a new signature:

void *csoundCreateCircularBuffer(CSOUND *csound, int numelem, int elemsize)
int csoundReadCircularBuffer(CSOUND *csound, void *circular_buffer,
                                              void *out, int items)
int csoundWriteCircularBuffer(CSOUND *csound, void *p,
                                              const void *inp, int items)
 

There are also two new functions:

int csoundPeekCircularBuffer(CSOUND *csound, void *circular_buffer,
                                              void *out, int items), and
void csoundFlushCircularBuffer(CSOUND *csound, void *p)
 

The function void csoundDestroyCircularBuffer(CSOUND *csound, void *circularbuffer) replaces csoundFreeCircularBuffer().

Finally the new function CSOUND *csoundGetInstance(long obj) is reserved for the Swig generated Python wrapper.

Deprecated and Removed Functions

csoundQueryInterface(), csoundSetChannelIOCallback(), and csoundPerformKsmpsAbsolute() are deprecated.

csoundGetStrVarMaxLen(), csoundGetSampleFormat(), csoundGetSampleSize(), csoundGetOutputFileName(), csoundSetMakeXYinCallback(), csoundSetReadXYinCallback(), csoundSetKillXYinCallback(), and csoundLocalizeString() have been removed.

 

 

References & Links

Michael Gogins 2006, "Csound and CsoundVST API Reference Manual", http://csound.sourceforge.net/refman.pdf

Rory Walsh 2006, "Developing standalone applications using the Csound Host API and wxWidgets", Csound Journal Volume 1 Issue 4 - Summer 2006, http://www.csounds.com/journal/2006summer/wxCsound.html

Rory Walsh 2010, "Developing Audio Software with the Csound Host API",  The Audio Programming Book, DVD Chapter 35, The MIT Press

François Pinot 2011, "Real-time Coding Using the Python API: Score Events", Csound Journal Issue 14 - Winter 2011, http://www.csounds.com/journal/issue14/realtimeCsoundPython.html



your comment:
name :
comment :

If you can't read the word, click here
word :