Using Pmask

Hans Mikelson

hans@csounds.com

rock1.py drums2.orc rock1.sco

Introduction

This article is intended to be a practical guide for getting Pmask running. Pmask, written by Maurizio Umberto Puxeddu, is a tool for algorithmic composition using Csound and can be obtained from:

http://web.tiscalinet.it/mupuxeddu/csound/

It is based on Cmask by Andre Bartetzki. Cmask is described at the following site:

http://www.kgw.tu-berlin.de/~abart/CMaskMan/CMask-Manual.htm

Cmask was written in C and Pmask is written in Python. Some advantages of the Python language are: it is freely available on the internet, it supports cross platform usage, it has strong support of objects and it has many advanced language features. This makes it easy for users to add their own functionality to the system. This article already assumes you have Python 2.0 installed and are somewhat familiar with the Python language. This article is somewhat specific to Windows 98 since that is the type of system I am currently using. I believe the Linux version of Pmask is a little easier to get started using.

Installation

I am still learning Python and some of the quirks of the language are confusing to me. I installed version 2.0 of Python and version 0.10 of Pmask. To install Pmask I just unzipped it into the Python directory. This created the directory pmask-0.10. Inside of this directory is a file called setup.py. I clicked on it which caused a command prompt window to open display an error message and close again before I could read it. I opened an MS-Dos window, started doskey, changed to the pmask-0.10 directory and typed python setup.py. This displayed the message:

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commmands
   or: setup.py cmd --help

error: no commands supplied

Maurizio Umberto Puxeddu wrote me the following note:

The setup.py script is a bare-bone standard distutils script. It can do several things, including installing pmask on your system, using the comand "install":
setup.py install

Since this is not what Windows' users expect (the just want setup.* to install the thing automatically) and since I wrote so little docs, I modified the script so that the default command is "install". You can now doubleclick on the setup.py icon and get pmask installed and available from any Python session (in any directory and in IDLE too) with just "import pmask".

This fix is available in

http://web.tiscalinet.it/mupuxeddu/csound/pmask-0.11.zip

Not to be deterred I decided to try one of the examples. I changed to the example\drum\ directory and clicked on drum.py. Another error message flashed by. I went back to the command prompt, changed to the example\drum directory and typed python drum.py. This generated an error message stating that the pmask module could not be found. I decided to move the file drum.py into the directory pmask-0.10. Typing in python drum.py from the command prompt generated a screen full of numbers. Unfortunately I do not know of anyway to capture the numbers as the scroll up the command window. Maurizio has been working on a satisfactory cross platform solution to this. In the mean time it seems easiest to me to just open a file and replace all of the print statements with write statements to write the output to the file as follows:

scof = open('test.sco','w')
scof.write(str(fs)+'\n')
scof.write(str(i1)+'\n')
scof.write(str(i2)+'\n')
scof.write(str(ss)+'\n')
scof.close()

Usage

Now that it is running time for some fun. First of all I was testing some of the chaotic generators. After some experimenting I came up with the following pmask code which I thought sounded something like popcorn:

from pmask import Range, UniformRandom
from pmask import PowerSegment, LinearSegment
from pmask import Mask, Quantizer
from pmask import FStatement, ScoreSection
from pmask import Lorenz, CircleSqr, Bifurcation, Hopalong, Mandelbrot, Julia, Henon


fs   = FStatement(1, 0, 65536, 10, 1)
sta  = 0.0
cdur = 15.0

#                                          sta  val    end  val
dens  = Mask(UniformRandom(), PowerSegment([(sta, 0.03), (cdur, 0.02)], 3.0), # lower
             PowerSegment([(sta, 0.10), (cdur, 0.20)], 3.0)) # upper

dur   = Mask(Julia(),       PowerSegment([(sta, 0.1), (cdur, 0.2)], 2.0),
             PowerSegment([(sta, 0.4), (cdur, 0.5)], 1.0))

amp   = Mask(Bifurcation(), PowerSegment([(sta, 2000), (cdur, 4000)], 0),
             PowerSegment([(sta, 10000), (cdur, 6000)], 0))

pitch = Mask(CircleSqr(),   PowerSegment([(sta, 5.0), (cdur, 6.0)], 0),
             PowerSegment([(sta, 7.0), (cdur, 9.0)], 0))

pan   = Mask(Hopalong(),    PowerSegment([(sta, 0.0), (cdur, 0.5)], 0),
             PowerSegment([(sta, 0.5), (cdur, 1.0)], 0))

hitq  = Mask(Henon(),       PowerSegment([(sta, 0.0), (cdur, 0.5)], 0),
             PowerSegment([(sta, 0.5), (cdur, 0.9)], 0))

#                 sta  dur  instr dens  gdur amp  pitch  pan  
ss = ScoreSection(sta, cdur, 34,  dens, dur,  amp, pitch, pan,
		  hitq, 2, .9, 1, 1, 1.05, .02)

i1 = 'i1 ' + str(sta) + ' ' + str(cdur+1) + ' 1'
i2 = 'i1 ' + str(sta) + ' ' + str(cdur+1) + ' 2'

Let's look at some of the statements here starting with the ScoreSection object. Generates a sequence of score events. Each element in the score section is related to a p-field. The first element tells when the score section starts that is when the first event in the score section begins. The second element tells the total duration of the score section. The third parameter determines the instrument number for the score section. the fourth parameter controls the density of the score events. This actually determines how far apart subsequent events are spaced. The next parameter determines the duration of the score event. The remaining score elements are p-fields for the instrument event. Each of these parameters may be either a constant or generated by the pmask generators.

Comparing Pmask and CMask

There are many generators that are described in the Cmask manual. Let's consider how some of the examples in the Cmask manual would be generated using Pmask.

CMask:	(0 0 4 10 7 5 9 8 ipl 0.5)
Pmask:	PowerSegment([(0.0,0.0),(4.0,10.0),(7.0,5.0),(9.0,8.0)],0.5)

CMask:  (0 0 4 10 7 5 9 8 ipl -2)
Pmask:	PowerSegment([(0.0,0.0),(4.0,10.0),(7.0,5.0),(9.0,8.0)],-2.0)

At first it may seem that pmask is not as compact as Cmask as a language and in some ways this is true. However consider that PowerSegment is a class so instances can be created:

ps1 = PowerSegment([(0.0,0.0),(4.0,10.0),(7.0,5.0),(9.0,8.0)],0.5)
ps2 = PowerSegment([(0.0,0.0),(4.0,10.0),(7.0,5.0),(9.0,8.0)],2.0)

Now ps1 and ps2 can be used as the power segments instead of writing out the entire list. Not only that but you also have access to all the capabilities of the Python language. For example you could set up a list of tuples (each pair of elements in the list is called a tuple.):

ls = []
for i in range(10):
  ls.append((i,UniformRandom()))
ps = PowerSegment(ls,.05)

This would first create a list of random segments which could then be used to define the PowerSegment. Now let's try a mask.

CMask: p4 rnd uni mask (2 100 6 70 11 240) (2 240 6 310 11 240)
Pmask: dens = Mask(UniformRandom(), PowerSegment([(2,100),(6,70), (11,240)], 3.0),
                                    PowerSegment([(2,240),(6,310),(11,240)], 3.0))

Rhythm

Finally I will present a rhythm section similar to what is found at the end of the example from my article on Csound Drums in this issue. The variable csta controls the start time for the rhythm and cdur controls the total duration of the rhythm. The next line creates the noise instrument which runs for the duration of the rhythm.

cdur	= 32
csta	= 0.0
ngen = """
i1 0 %f .5 1
i1 0 %f .1 2
""" % (cdur, cdur)
sf.write(ngen)

For the kick drum section kicki is the instrument number, kickr controls the rhythm as a list of length of time between i-events. The variable kicka controls the amplitude and kickhf controls the high frequency value for the drum. Finally a score section object is created and output.

# Kick drum part
kicki	= 11
kickr   = List([1, .75, .25, 1, 1, 1, 1, .25, .75, 1],'cycle')
kicka   = List([30000, 25000, 33000, 20000],'cycle')
kickhf  = List([200, 170, 150, 230, 260],'cycle')
# Amp    HiF  LoF Pan Dec  Tens Hit PBendQ OD   OC  OF Sus  QFmF LPAmpF
kickss = ScoreSection(csta, cdur, kicki, kickr, .25, kicka, kickhf, 90,
		      .5, .03, .2,  .5, 4,     .02, .1, 1, .01, 20,  8)
sf.write(str(kickss)+'\n')

Most of the high hat part is similar to the kick drum part. The instrument number is 24, hihatd controls the note duration, not to be confused with the duration between notes which is held constant at .25 seconds. I use lists to modify the pan and mix p-fields.

# HiHat part
hihati	= 24
hihatd  = List([.25, .23, .2, .26, .28, .3],'cycle')
hhpan   = List([.25, .73, .2, .46, .28, .7],'cycle')
hhmix   = List([.15, .23, .2, .26, .31, .12, .3, .3],'cycle')
# Amp    Pitch Pan    Fc    Q     OTAmp OTFqc OTQ  Mix
hihatss = ScoreSection(csta, cdur, hihati, .25, hihatd, 30000, 8.00, hhpan,
 		       5333, 40.0, 0.5,  1.5,  0.2, hhmix)
sf.write(str(hihatss)+'\n')

Finally here is the snare part. I use lists again to control the values of the snare spring decay and snare tone.

# Snare drum part
snarei	= 34
snares  = csta+.5
snarer = List([1, 1, 1, 1,  1, 1, .25, .75, 1],'cycle')
snsdec = List([.2, .3, .5, 1,  1.3, 1],'cycle')
snton  = List([.5, 1, .3, 1.3, 2],'cycle')
# Amp    Fqc   Pan Q   SprDec SprTone SprMix SprQ PBend PBTime
snaress = ScoreSection(snares, cdur, snarei, snarer, .25, 30000,
		       7.00, .3, .7, snsdec,snton,      1,     1,   1.5,  .1)
sf.write(str(snaress)+'\n')

Links

Pmask can be obtained from the following site: http://pythonsound.sourceforge.net/

After trying to get a decent rhythm going for a while I decided to try and learn about drumming. The following site has some useful beginning tutorials for drumming: http://216.103.111.115/webthumper/drums/home.html