Monday, May 12, 2014

16. Python Csound Module


In Tutorial 6, the csound Python binding (csnd6) was used. Rather than using it, we will create our own Python module.




In the Python module, to interact with csound, first we import, into the namespace, the system() function from the os module and the sleep() function from the time module. Both these modules are in-built and part of Python standard library. The system() function allows us to send Strings to the command prompt to execute. The sleep() function creates time delays. The List variable L is created. While L is not a descriptive name, it is easy to remember and type. Next the tags are held in different variables. The String variable names are more descriptive. This is because they are used less often, and further, a clearer name adds clarity to the program.




The first two functions in the module, are described. The first is add() which is used to add, any number of terms to the List, L. The function, header(), is used to initialize four header variables. They are the sampling-rate, number of control-samples in one control-period, number of channels (mono or stereo), and amplitude. The amplitude, here, refers to the maximum, that is, all audio values have to be between minus amp and positive amp. Further, in this code, all four of these values have defaults. Thus we do not have to initialize all of them, or even any of them, as long as the function, header(), is called.




The function instrument() is used to insert an instrument block. The instrument block will be sent as a String, which is the second parameter, and which will be placed in the local variable S. Inside the function, a list, tL, is created, with the newline separating the String into different indices. Next, the program checks, if tL is of the correct format. If not, the program raises an error. An error is also called Exception in Python. Finally, each element in tL has two spaces inserted. This is not a necessary step, just a step to make things clearer and stand out in the csd file. The delimiting terms, instr and endin, are added as well. There are many print functions, indicating what is happening. Usually, once you understand the program structure, after reading the output from a few runs, you can remove them. They will be removed in further Tutorials.




Next, we have the function score(). The function score() will define a score line. A score line must have three parameters, corresponding to an instrument, the start time, and the duration. It may have other parameters, which will be understood by the instrument block. These parameters, or fields, are called p-Fields and instrument is p1, start time is p2, duration is p3, and so on, if we have more p-fields. Since the number of p-fields is arbitrary, we may ask Python to pack any other numbers, in a Tuple, with the name p. While, this is not a descriptive name, p is a local variable that will be destroyed, upon function completion. Again, there are extra print statements to indicate what is happening. After some test runs, we can remove them.




Finally, the module will have the writeRun() function. The function writeRun() will first write out the list L to an output file. Before a List can be sent out to some text file, it must have newline characters included. Besides looking better, this is a requirement in csound which requires that opcodes be placed in separate lines. After writing the csd file, we add a delay of 1-second, and then call the command prompt. If the command prompt window is not already open, a new window is opened and it closes after the writing of the sound file. That is why the Anaconda command prompt should be used to launch ipython so all command-prompt printouts are captured and remain open.


# moduleCsound.py

from os import system
from time import sleep

L = []

startSyn,stopSyn='<CsoundSynthesizer>','</CsoundSynthesizer>'
startOpt,stopOpt='<CsOptions>','</CsOptions>'
startIns,stopIns='<CsInstruments>','</CsInstruments>'
startSco,stopSco='<CsScore>','</CsScore>'

def add(*b):
    L.extend(b)
    return

def header(sr=44100,ksmps=10,nch=1,amp=1):
    L.append('sr = %d' % sr)
    L.append('ksmps = %d' % ksmps)
    L.append('nchnls = %d' % nch)
    L.append('0dbfs = %d' % amp)
    return

def instrument(instr,S):
    print "S = ",S
    tL = S.split('\n')
    print "tL after the split = ",tL
    if tL[0]!='' or tL[-1]!='' : raise Exception("Instr Str")
    tL = [' '*2 + tl for tl in tL]
    print "tL after the list comp = ",tL
    tL[0] = 'instr %d' % instr
    tL[-1] = 'endin'
    print "tL after adding instrument delimiters = ", tL
    L.extend(tL)
    return

def score(instr,start,dur,*p):
    print "instrument = %d" % instr
    print "start = %.2f" % start
    print "dur = %.2f" % dur
    print "Extra p-fields = ", p
    String = 'i %s %s %s' % (instr,start,dur)
    LString1=[' ' + str(pf) for pf in p]
    String1 = "".join(LString1)
    print "String to add = ", String+String1
    L.append(String+String1)
    return
    
def writeRun(fname):
    OutL=[Lu + '\n' for Lu in L]
    out = open('%s.csd' % fname,'w')
    out.writelines(OutL)
    out.close()
    sleep(1)
    cmd = 'csound -o %s.wav %s.csd' % (fname,fname)
    print cmd
    system(cmd)
    return



Now, we will go over a test run in IPython. After changing into the directory with the module, we import all, that is, all the module functions are known to the current namespace, and the L List is created, and referenced with an alias of same name, and copies of all String tags are made. Strings are Immutable and copies will be made, not aliases. The Strings are the tags to start csound, and start and stop an empty command-line option statment. We will use only one command option, namely the o option to output an audio file, and it will be used via a system call.



This is the video of Tutorial 16:


6 comments:

About Me

I have used Python for the last 10+ years. I have a PhD in Electrical Engineering. I have taught Assembly Language programming of Intel-compatible chips as well as PC hardware interfacing. In my research, I have used Python to automate my calculations in physics and chemistry. I also use C++ and Java, often with Python.