Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

SONIFYING SIEVES: SYNTHESIS AND SIGNAL PROCESSING

APPLICATIONS OF THE XENAKIS SIEVE WITH PYTHON AND


CSOUND
Christopher Ariza
Massachusetts Institute of Technology
Music and Theater Arts, 4-246
77 Massachusetts Avenue
Cambridge, MA 02139 USA
ABSTRACT
This paper introduces applications of the Xenakis sieve
for sound synthesis and signal processing with Python and
Csound. Using Python to collect, interpolate, and map
sieve-derived values, sieves are employed in Csound for
additive and subtractive synthesis, amplitude and
frequency modulation, and waveform segment synthesis.
Approaches to multi-dimensional interpolation of multiple
sieve segments are demonstrated.
1.

INTRODUCTION

Iannis Xenakis (1922-2001) envisioned broad applications


for sieves: sieve theory is very general and consequently
is applicable to any other sound characteristics that may
be provided with a totally ordered structure [20]. Xenakis
employed sieves as a way of generating ordered sequences
of integers out of periodicities. As summarized in Ariza
[3], Xenakis describes the use of sieves in numerous
acoustic compositions: Nomos Alpha (1965-1966) [18]
and Jonchaies (1977) [17], for example, are described as
having sieve-based pitch structures; Psappha (1975) [8]
and Kombo (1981) [17] are described as having sievebased rhythmic structures.
In his later discussions of sieves, Xenakis noted that
it is quite conceivable to apply this theory to the
synthesis of sounds by computer [19]. While suggested,
Xenakis provides no documented examples of this
approach. Other writers, discussing diverse applications of
sieves, have also focused on pitch and rhythm parameters
[1, 5, 3, 10, 11, 16]. This paper introduces applications of
the Xenakis sieve for sound synthesis and signal
processing with Python and Csound.
The expansion of Python scripting into Csound
provides new opportunities for instrument control and
design [7]. Used within the Csound orchestra, the Pythonbased Xenakis Sieve object [3] permits applying sievederived values to a wide range of sonic parameters. The
use of Python methods and functions to create control-rate

signals [4] is extended herein by using Python for


collecting, mapping, and interpolating sieve-derived
values. Exploring the sonic relevance of particular sieves
is enabled by these techniques, and offers a rich avenue
for further study.
The Sieve class, distributed within athenaCL [2], is
employed in this study. As an open-source, cross-platform
Python system with no required software dependencies,
athenaCL integrates well within Csound and offers diverse
tools for computer-aided algorithmic composition. This
paper demonstrates using components of athenaCL tools
within Python scripts; the use of athenaCLs interactive
command-line interface is not discussed.
This paper briefly reviews the use of Python in Csound
as well as the basic operation of the Xenakis Sieve object
and logic string. After introducing Python interpolation
objects, numerous practical applications of sieves for
synthesis and signal processing are provided. All
examples are presented in the Csound Unified File
Format, or CSD. Complete CSD files and rendered audio
examples are available for download at the following
URL: http://www.flexatone.net/docs/sspaxspc.zip. Users
must install Csound version 5 or later, Python, and
athenaCL version 1.4.9 or later to render these examples.
The athenaCL directory must be within Pythons search
directory or, alternatively, installed within the Python sitepackages directory. Consult Python and athenaCL
documentation for more information on installing Python
modules.
2.

USING PYTHON WITHIN CSOUND

The Csound Python opcodes permit Python objects to be


instantiated and manipulated in the Csound orchestra.
Values from these Python objects can be accessed within
instruments both at instrument initialization or as control
rate value generators. Within the Csound orchestra, the
pyinit opcode must be called. Next, the pyruni block is
used to execute Python code, creating objects and
functions for use in Csound instrument definitions.

Within the Csound instrument, the pycall1i opcode


(for values assigned to i-rate initialization variables) or
pycall1 opcode (for values assigned to k-rate control
rate variables) call the previously-instantiated Python
functions or object methods with one or more arguments.
Depending on the number of values returned by the
instantiated Python function or object method, different
pycall opcodes are used. The Csound pycall opcodes
require the Python function or object method to return a
floating-point value.
3.

USING XENAKIS SIEVES AS PYTHON


OBJECTS

The notation and definition of Sieve objects presented in


Ariza [3] will be used in this paper. A brief introduction of
Sieve object functionality is provided using Python's
interactive mode. The Python primary prompt is >>>.
After installing athenaCL, the sieve.py module can be
imported. After importing, a basic Sieve object is created
from a logic string and a sieve segment is extracted. The
sieve segment is extracted by calling the Sieve object with
three arguments: a transposition n, a z, and a format string.
These concepts will be explained below.
>>>
>>>
>>>
[0,

from athenaCL.libATH import sieve


a = sieve.Sieve('2@0')
a(0, range(0,10), 'int')
2, 4, 6, 8]

Figure 1. Importing sieve.py


In Figure 1 the Sieve object a is instantiated using a
logic string. The logic string is a symbolic representation
of a sieve and consists of one or more residual classes
combined by logic operators. A residual class defines an
infinite number sequence as a modulus (M) and a shift (I),
notated as M@I. Each value in the sequence modulus M is
equal to I: thus 2@0 specifies all integers x where x % 2
== 0, or, between 0 and 9, the values 0, 2, 4, 6, 8.
Similarly, 3@2 specifies all integers x where x % 3 == 2,
or, between 0 and 9, the values 2, 5, 8. Specified sieve
values can be thought of as active points on a grid of
integers.
Residual classes can be combined with logic operators
to produce more complex sequences. The permitted logic
operators and their symbolic representation is as follows:
union (|), intersection (&), symmetric difference (^), and
complementation (-). As defined in Ariza [3],
combinations of residual classes and logic operators can
result in simple and complex sieves. Maximally simple
sieves use residual classes combined only by union; for
clarity, this paper makes use only of maximally simple
sieves. The example below demonstrates a simple sieve,
combining two residual classes by union.

>>> a = sieve.Sieve('3@0|3@1')
>>> a(0, range(0,10), 'int')
[0, 1, 3, 4, 6, 7, 9]

Figure 2. The Union of Two Residual Classes


Sieves define infinite number sequences. A sieve
segment is a finite contiguous section of a sieve sequence.
A sieve can be thought of as a filter applied to a finite
range of contiguous integers, labeled z. The generation of
z values is facilitated in Python with the range()
function. The range function returns a list of contiguous
integers from the minimum to one less than the maximum
specified value. Changing the z provided to a sieve results
in different sizes and ranges of sieve segments. The sieve
created in Figure 2, for example, can be used in Figure 3
to generate a sieve segment between -100 and -80 by
calling the instantiated Sieve object with a different z.
>>> a = sieve.Sieve('3@0|3@1')
>>> a(0, range(-100,-80), 'int')
[-99, -98, -96, -95, -93, -92, -90, -89, -87, -86, -84,
-83, -81]

Figure 3. Creating Sieve Segments


Sieve segments can be represented in a variety of
formats. Ariza [3] defines four: integer, width, binary, and
unit. This paper makes use of integer and unit sieve
segments. Integer segments are demonstrated above: these
are the integer values extracted from the supplied z. Unit
segments map z to the unit interval and return segment
points as proportionally-spaced, floating-point values
between 0 and 1. Figure 4 demonstrates both integer
(using the int argument) and unit (using the unit
argument) sieve segments.
>>> a = sieve.Sieve('3@0|5@4')
>>> a(0, range(0,16), 'int')
[0, 3, 4, 6, 9, 12, 14, 15]
>>> a(0, range(0,16), 'unit')
[0.0, 0.20000000000000001, 0.26666666666666666,
0.40000000000000002,
0.59999999999999998, 0.80000000000000004,
0.93333333333333335, 1.0]

Figure 4. Unit and Integer Sieve Segments


Given only a sieve logic string and a z, the number of
values obtained in the resulting sieve segment may not be
immediately apparent. For this reason, rather than calling
the Sieve object with a z value, the collect() method
may be employed. This method, given a minimum z value
and a desired segment length, will continue to expand z
upward as necessary to collect as many values as
specified. The method requires four arguments: a
transposition n, a minimum z, a length, and a format. In
Figure 5 the first 10 values from a sieve segment starting
at 30 are collected.
>>> a = sieve.Sieve('3@0|5@4')
>>> a.collect(0, 30, 10, 'int')
[30, 33, 34, 36, 39, 42, 44, 45, 48, 49]

Figure 5. Using the Sieve Collect Method

The flexible representation of sieves as logic strings


provides a clear representation of sieve structures. Logic
strings permit creating both simple and complex sieves.
Xenakis, using a similar notation, describes the goal of
creating symmetries which are as complex as one
might want [19].
Alternatively, Xenakis describes the need to retrieve
from a given series of events or objects in space or time
the symmetries that constitute the series [19]. As shown
in Figure 6, the Python Sieve object permits the formation
of Sieve objects by providing a list of integers without a
logic string. When providing a list of integers, a z is
automatically determined: this determination may have an
affect on what logic string is derived from the integer
sequence [3].
>>> a = sieve.Sieve([3,9,10,15,19,20])
>>> print a
6@3|9@1|10@0

Figure 6. Forming a Sieve without a Logic String


For applications in synthesis and signal processing
parameters, the unit sieve segment format is particularly
useful. Once scaled within the unit interval, a sieve
segment can subsequently be scaled within any value
range. Scaling values between sieve points is suggested by
Xenakis: of the three hyperbolae, or transformations, of
sieves suggested, one is the modification of the unity, or
the space between sieve integers [19].
4.

INTERPOLATION OF SIEVE SEGMENTS

Working with multiple sieves is facilitated by creating


arrays of equal-sized sieve segments and, rather than using
one at a time, interpolating between them. This approach
permits, while cycling between sieve segment points,
smoothly fading between different sieves.
Roads [13] discusses numerous varieties of
interpolation; here, linear and half-cosine interpolation
will be used. When connecting waveform segments as
breakpoints, as demonstrated below, half-cosine
interpolation is preferred: half-cosine interpolation
ensures a smooth curve between the breakpoints [13].
The athenaCL system offers numerous break-point
segment generator objects. These objects offer the
additional functionality of holding last-defined values
when requesting a point beyond the defined break-points.
The break-point function generators, defined in the bpf.py
module, are based in part on the Object-oriented Music
Definition Environment (OMDE/pmask) by Maurizio
Umberto Puxemdu.
In Figure 7 a LinearSegment object is created from
two points. Points are specified as a list of lists, where
each component list defines x and y value pairs. Once
instantiated, for any given x the interpolated y value is
returned.

>>> from athenaCL.libATH.omde import bpf


>>> a = bpf.LinearSegment([(0,50),(1,60)])
>>> a(0)
50.0
>>> a(.75)
57.5
>>> a(.95)
59.5

Figure 7. Using LinearSegment interpolation


The HalfCosineSegment object, demonstrated in
Figure 8, provides a smoother transition to the specified
points.
>>> from athenaCL.libATH.omde import bpf
>>> a = bpf.HalfCosineSegment([(0,50),(1,60)])
>>> a(0)
50.0
>>> a(.75)
58.535533905932738
>>> a(.95)
59.938441702975688

Figure 8. Using HalfCosineSegment interpolation


There are numerous ways sieves can be interpolated. A
one-dimensional approach interpolates between the points
of a single sieve segment. As values from the actual sieve
segment are transitory, this approach does not maintain
the spacing and structure of the sieve segment, and is not
demonstrated in this paper. A second approach takes
multiple sieve segments of equal size and interpolates
between adjacent points from different segments. A third
approach takes two-dimensional matrices created by
sieves and interpolates between adjacent matrices.
5. APPLICATIONS OF SIEVES FOR ADDITIVE
SYNTHESIS, SUBTRACTIVE SYNTHESIS, AND
FREQUENCY MODULATION
Xenakis states that sieves can be applied to any set of
characteristics of sound or of well-ordered sonorous
structures especially to any group furnished with an
additive operation and whose elements are multiples of a
unity [19]. Additive synthesis, while an elementary
technique, offers a practical application. This approach
can be extended to coordinate arrays of band-pass filters,
with applications in subtractive synthesis and vocoding.
Modulating between different sieves configured as
frequency bands can offer an unusual form of frequency
modulation.
The CSD specified in Figure 10 is a simple
demonstration of additive synthesis using sieve-derived
integers as frequency scalars and sieve-derived unitinterval values for amplitude scalars. A base frequency,
converted from a MIDI note number, is provided from the
score; this frequency is than scaled by the sieve-derived
integers. A Python function, sieveValues(), is defined
to return values from two sieve segments. Given an index
position (converted to an integer), this function returns an

integer sieve segment point (converted to a float) and a


randomly selected floating-point sieve segment point. A
variety of other probabilistic or stochastic methods could
be used to select sieve values. A graphic representation of
these two sieve segments is provided in Figure 9.

Figure 9. Ordered Integer Sieve Segment and


Unordered Unit Interval Sieve Segment
<CsoundSynthesizer>
<CsInstruments>
pyinit
pyruni {{
import random; from athenaCL.libATH import sieve
a = sieve.Sieve('5@2|9@0|13@1')
aSegInt = a.collect(0, 1, 8, 'int')
aSegUnit = a.collect(0, 49, 8, 'unit')
def sieveValues(i):
return (float(aSegInt[int(i)]),
float(random.choice(aSegUnit)))
}}
sr
= 44100
ksmps = 10
nchnls = 1
instr 100
iDur = p3
iAmp = ampdbfs(p4)
iFqBase = cpsmidinn(p5)
iAmpScale = 0.6
iFq1,iAmp1
iFq2,iAmp2
iFq3,iAmp3
iFq4,iAmp4
iFq5,iAmp5
iFq6,iAmp6
iFq7,iAmp7
iFq8,iAmp8

pycall2i
pycall2i
pycall2i
pycall2i
pycall2i
pycall2i
pycall2i
pycall2i

"sieveValues",
"sieveValues",
"sieveValues",
"sieveValues",
"sieveValues",
"sieveValues",
"sieveValues",
"sieveValues",

0
1
2
3
4
5
6
7

aSig1
aSig2
aSig3
aSig4
aSig5
aSig6
aSig7
aSig8

oscili
oscili
oscili
oscili
oscili
oscili
oscili
oscili

aMix

= (aSig1 + aSig2 + aSig3 + aSig4 \


+ aSig5 + aSig6 + aSig7 + aSig8) * iAmpScale
adsr .2*iDur, .2*iDur, .7, .2*iDur
out
aMix * kEnvl

kEnvl

iAmp*iAmp1,
iAmp*iAmp2,
iAmp*iAmp3,
iAmp*iAmp4,
iAmp*iAmp5,
iAmp*iAmp6,
iAmp*iAmp7,
iAmp*iAmp8,

envelopes for each sine component, or by varying initial


phase.
Rather than employing integer scalars of a base
frequency, frequency values can be specified as unitinterval scalars of a frequency range; this range can then
be added to a base frequency. This approach is
demonstrated in Figure 12. Here, rather than sine tones,
numerous parallel band-pass filters are configured to filter
white noise; alternatively, these sieve-derived center
frequencies could be used to filter sampled audio, for
vocoding, or for other applications.
In Figure 12, two sieve segments of equal size are
used. The Python SieveInterpolate class is defined to
interpolate between two or more equal-sized sieve
segments. A half-cosine interpolation segment is created
to move between parallel sieve points within sieve
segments. When calling an instance of SieveInterpolate
within Csound, the user specifies the desired sieve
segment point as well as a control-rate interpolation value
representing the range between adjacent sieve points. This
type if interpolation creates a two-dimensional matrix of
values, with discrete values between points of a sieve
segment and continuous values between points of adjacent
sieves. A graphic representation of this mapping is
provided in Figure 11.

iFqBase*iFq1,
iFqBase*iFq2,
iFqBase*iFq3,
iFqBase*iFq4,
iFqBase*iFq5,
iFqBase*iFq6,
iFqBase*iFq7,
iFqBase*iFq8,

1,
1,
1,
1,
1,
1,
1,
1,

0
0
0
0
0
0
0
0

endin
</CsInstruments>
<CsScore>
f 1
0 16384 10 1
i 100 0 4
-12 40
i 100 6 4
-12 44
</CsScore>
</CsoundSynthesizer>

Figure 10. Additive Synthesis with Sieves


This example can be extended with the addition of
noise to the frequency scalars, by providing dynamic

Figure 11. Mapping Multiple Interpolated Sieve


Segments to Frequency
<CsoundSynthesizer>
<CsInstruments>
pyinit
pyruni {{
import random, math; from athenaCL.libATH import sieve
from athenaCL.libATH.omde import bpf
class SieveInterpolate:
def __init__(self, segments):
points = [[] for i in range(len(segments[0]))]
for i in range(len(segments[0])):
for q in range(len(segments)):
points[i].append((q, segments[q][i]))
self.gen = []
for i in range(len(segments[0])):
row = bpf.HalfCosineSegment(points[i])
self.gen.append(row)
def __call__(self, i, phase):
return self.gen[int(math.floor(i))](phase)
a = sieve.Sieve('2@1|7@6')
aSeg = a.collect(0, 1, 8, 'unit')
b = sieve.Sieve('5@1|9@8|13@11')
bSeg = b.collect(0, 1, 8, 'unit')
c = sieve.Sieve('4@1|7@8|21@3')
cSeg = b.collect(0, 117, 8, 'unit')
interpolator = SieveInterpolate([aSeg, cSeg, bSeg])

}}
sr
= 44100
ksmps = 100
nchnls = 1
instr 100
iDur = p3
iAmp = ampdbfs(p4)
iAmpScale = 0.5
iBw = 3
iFqBase = cpsmidinn(p5)
iFqRange = p6
kMorphFlat line
0, iDur, 2
kMorphMod
oscili
1, 40, 1, 0
kMorphMod = (kMorphMod + 1) * .5
kFq1 pycall1
"interpolator", 0,
kFq2 pycall1
"interpolator", 1,
kFq3 pycall1
"interpolator", 2,
kFq4 pycall1
"interpolator", 3,
kFq5 pycall1
"interpolator", 4,
kFq6 pycall1
"interpolator", 5,
kFq7 pycall1
"interpolator", 6,
kFq8 pycall1
"interpolator", 7,
aSrc
aSig1
aSig2
aSig3
aSig4
aSig5
aSig6
aSig7
aSig8
aMix
kEnvl

kMorphFlat
kMorphMod
kMorphFlat
kMorphFlat
kMorphFlat
kMorphMod
kMorphFlat
kMorphFlat

random
-0dbfs, 0dbfs
butterbp aSrc, iFqBase+(iFqRange*kFq1), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq2), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq3), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq4), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq5), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq6), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq7), iBw
butterbp aSrc, iFqBase+(iFqRange*kFq8), iBw
= (aSig1 + aSig2 + aSig3 + aSig4 \
+ aSig5 + aSig6 + aSig7 + aSig8) * iAmpScale
adsr
.1*iDur, .2*iDur, .8, .1*iDur
out aMix * kEnvl

endin
</CsInstruments>
<CsScore>
f 1
0 16384 10 1
i 100 0
6 -12 48
i 100 0.1 8 -12 44
i 100 0.2 6 -12 46
i 100 0.3 6 -12 43
</CsScore>
</CsoundSynthesizer>

4000
4000
8000
6000

Figure 12. Dynamic Subtractive Synthesis with


Sieves
This approach can be extended in a variety of ways.
For example, rapid modulation between sieve segments
can be used to create frequency modulation; as the
frequency distance between each sieve segment point may
be different, novel timbres emerge.
6. APPLICATIONS OF SIEVES FOR DYNAMIC
AMPLITUDE ENVELOPES AND AMPLITUDE
MODULATION
While related to creating rhythms with sieves, sieves can
be used to create dynamic amplitude envelopes. Similar to
an analog step sequencer, the ordered or unordered
floating point values of a sieve segment can be used as a
control signal and looped. Using the SieveInterpolate class
introduced in Figure 12, this looping pattern can be
gradually morphed into patterns derived from other sieve
segments.

In Figure 14, four unit interval sieve segments are


created and shuffled. Rather than random shuffling, sieve
segment points might be arranged to meet specific needs.
Next, these segments are provided to the SieveInterpolate
class. The Csound instrument definition creates an
amplitude envelope from these interpolated values, where
two scaled phasors are used to select sieve points within
segments and to interpolate between adjacent sieve
segments. The resulting amplitude envelope, scaled by
unity and converted into an audio-rate signal, is low-pass
filtered and then applied to white noise. A graphic
representation of this mapping is provided in Figure 13

Figure 13. Mapping Multiple Interpolated Sieve


Segments to Envelope Amplitudes
<CsoundSynthesizer>
<CsInstruments>
pyinit
pyruni {{
import random, math; from athenaCL.libATH import sieve
from athenaCL.libATH.omde import bpf
class SieveInterpolate:
def __init__(self, segments):
points = [[] for i in range(len(segments[0]))]
for i in range(len(segments[0])):
for q in range(len(segments)):
points[i].append((q, segments[q][i]))
self.gen = []
for i in range(len(segments[0])):
row = bpf.HalfCosineSegment(points[i])
self.gen.append(row)
def __call__(self, i, phase):
return self.gen[int(math.floor(i))](phase)
a = sieve.Sieve('2@1|7@6')
a1Seg = a.collect(0, 1, 8, 'unit')
a2Seg = a.collect(0, 29, 8, 'unit')
b = sieve.Sieve('5@1|9@8|13@11')
b1Seg = b.collect(0, 1, 8, 'unit')
b2Seg = b.collect(0, 87, 8, 'unit')
segments = [a1Seg, b1Seg, a2Seg, b2Seg]
for seg in segments:
random.shuffle(seg)
interpolator = SieveInterpolate(segments)
}}
sr
= 44100
ksmps = 100
nchnls = 1
instr 100
iDur = p3
iAmp = ampdbfs(p4)
iAmpScale = 0.1
iSegCount = 3.9999
kMorph
kMorph
kSelect
kSelect

oscili
1, 0.125, 1, 0
= ((kMorph + 1) * .5) * iSegCount
phasor
1.2
= kSelect * 7.9999

kSrc
aSrc
aSrc
aNoise
aMix
kEnvl

pycall1
"interpolator", kSelect, kMorph
= kSrc * 1
lowpass2 aSrc, 120, .85
random
-0dbfs, 0dbfs
= aNoise * aSrc * iAmpScale
adsr .1*iDur, .2*iDur, .8, .1*iDur
out
aMix * kEnvl

endin
</CsInstruments>
<CsScore>
f 1
0 16384 10 1
i100 0
16 -12 .3
</CsScore>
</CsoundSynthesizer>

Figure 14. Dynamic Envelopes with Sieves


The approach demonstrated in Figure 14 can be
applied for amplitude and ring modulation. The
interpolated sieve-derived signal can be driven at a much
faster rate (120 Hz, for example), the modulating signal
can be scaled between -1 and 1 (for ring modulation), and
modulation can be applied to a sine tone or other sound
source. As the sieve segments are interpolated, transitions
between unique timbres are clearly audible.
7.

APPLICATIONS OF SIEVES FOR WAVEFORM


SEGMENT SYNTHESIS

While Xenakis provides no demonstration, he suggests


applying sieves to the generation of waveforms,
imagining the amplitude and/or the time of the sound
signal ruled by sieves [19]. A similar suggestion is made
in the nearly identical chapter of Formalized Music [20].
Xenakis notes that the fine symmetries thus created
should open a new field for exploration [19]. What
follows is a preliminary examination of this technique.
While Xenakis does not suggest a specific approach to
generating waveforms, his technique of Dynamic
Stochastic Synthesis [20], encoded in GENDYN [9],
provides a potentially related approach. Curtis Roads,
assuming an approach similar to GENDYN, suggests
creating breakpoints from sieve-derived time and
amplitude coordinates; these breakpoints are then
interpolated: in sieve theory the breakpoints would be
calculated according to a partitioning algorithm based on
sieved amplitude and time dimensions [14]. This
approach to waveform segment synthesis [13], sound
composition with individual sample points [14], abstract
algorithm synthesis [15], or non-standard synthesis [12], is
related to Herbert Brns SAWDUST system [13] and
Gottfried Michael Koenigs Sound Synthesis Program
(SSP) [6, 13].
Waveform segment synthesis of sieves benefits from
an alternative approach to interpolation. Two Python
objects are used. The first, SieveCoordinate, employs two
equal-sized sieve segments to create the x and y
coordinates of a unit-interval wavetable. The x-coordinate
segment is used for time spacings; these values must be
recalculated as accumulated time steps. A zero start value

is added, and the segment is re-scaled within the unit


interval. The y-coordinate segment is used for amplitude
spacings; the last segment point is added to the beginning,
creating a loop. Next, x and y points are collected as pairs
and passed to a HalfCosineSegment interpolator object.
When calling this object, an unit interval phase value, as a
supplied x value, returns the interpolated unit interval y
value.
The second Python object, SievePolyCoordinate, takes
a list of any number of SieveCoordinate objects as
interpolating unit-interval wavetables, and permits
interpolating between adjacent tables at any phase value.
When called, a unit interval phase is passed to the object;
the object then calls each component wavetable object
with the specified phase, collects all returned values, and
then uses these values to create a list of points for crosswavetable interpolation. Using the LinearSegment
interpolation object, values can be selected from a single
wavetable or smoothly interpolated between adjacent
wavetables. When combined, SieveCoordinate and
SievePolyCoordinate create a three dimensional matrix,
with continuous, interpolated values between all
dimensions. A graphic representation of this approach is
provided in Figure 15.

Figure 15. Interpolating between Multiple SieveConstructed Wavetables


In Figure 16, six sieve segments are used to construct
four unit interval wavetables with the SieveCoordinate
class. These wavetables are then provided to
SievePolyCoordinate. The Csound instrument definition
employs a variety of phasors to control table reading
speed and interpolation between adjacent tables. The
output of the SievePolyCoordinate object is converted to
an audio-rate signal, scaled between -1 and 1, and then

low-pass filtered at 16000 Hz. This signal is then scaled


and enveloped.
<CsoundSynthesizer>
<CsInstruments>
pyinit
pyruni {{
import random, math
from athenaCL.libATH import sieve, unit
from athenaCL.libATH.omde import bpf
class SieveCoordinate:
def __init__(self, xSeg, ySeg):
yArray = [ySeg[-1]] + ySeg
xArray = unit.unitNormAccumulate(xSeg)
points = []
for i in range(len(xArray)):
points.append((xArray[i], yArray[i]))
self.gen = bpf.HalfCosineSegment(points)
def __call__(self, phase):
return self.gen(phase)
class SievePolyCoordinate:
def __init__(self, generators):
self.gen = list(generators)
def __call__(self, n, phase):
val = []
for x in range(len(self.gen)):
val.append((x, self.gen[x](phase)))
out = bpf.LinearSegment(val)
return out(n)
a = sieve.Sieve('2@1|7@6')
a1Seg = a.collect(0, 1, 16, 'unit')
a2Seg = a.collect(0, 50, 16, 'unit')
b = sieve.Sieve('5@1|9@8|13@11')
b1Seg = b.collect(0, 1, 16, 'unit')
b2Seg = b.collect(0, 120, 16, 'unit')
for seg in [a1Seg, a2Seg, b2Seg, b1Seg]:
random.shuffle(seg)
c = sieve.Sieve('2@1|5@3')
c1Seg = c.collect(0, 1, 4, 'unit')
c2Seg = c.collect(0, 34, 4, 'unit')
generator = SievePolyCoordinate([
SieveCoordinate(a1Seg, b1Seg),
SieveCoordinate(a2Seg, b2Seg),
SieveCoordinate(c1Seg, c2Seg),
SieveCoordinate(a1Seg, b1Seg)])
}}
sr
= 44100
ksmps = 10
nchnls = 1
instr 100
iDur = p3
iAmp = ampdbfs(p4)
iAmpScale = 0.6
iFqBase = cpsmidinn(p5)
iSegCount = 3.999
kMorph
kSelect
kSelect
kSrc
aSrc
aSrc
aMix
kEnvl

phasor
iFqBase
oscili
1, .125, 1, 0
= ((kSelect + 1) * .5) * iSegCount
pycall1
"generator", kSelect, kMorph
= (kSrc * 2) - 1
lowpass2
aSrc, 16000, .85
= iAmp * aSrc
adsr .1*iDur, .2*iDur, .8, .1*iDur
out
aMix * kEnvl

endin
</CsInstruments>
<CsScore>
f 1
0 16384
10 1
i 100 0
15 -12 48
i 100 5
10 -12 39
i 100 10
5 -12 58
</CsScore>
</CsoundSynthesizer>

Figure 16. Waveform Segment Synthesis with


Sieves

This approach to waveform segment synthesis


produces novel and dynamic timbres. While effective, the
approach demonstrated in Figure 16 is limited in speed by
Python. Performed at a control rate of 1/10th the sampling
rate, resolution is lost in creating an audio signal.
Increasing the control rate, while increasing processing
time, improves the quality of waveform rendering.
8.

FUTURE WORK

This paper introduces numerous approaches to sonifying


and mapping sieve data for synthesis and signal
processing applications. These examples are not
exhaustive:
sieve-derived
values,
as
collected,
interpolated, and mapped here, can be applied to a wide
range of synthesis and signal processing tasks.
Future investigations of waveform segment synthesis
might systematically explore relationships between
specific sieve structures and resultant sonic structures. The
expansive new parameter spaces opened through the use
of sieve logic strings could be made more manageable
with an automated form of exploration: a Pythoncontrolled system, for example, could produce vast
amounts of audio and analysis information, facilitating
quickly surveying the sonic results of hundreds of sieves.
9.

REFERENCES

[1] Amiot, E. and G. Assayag, C. Malherbe, A. Riotte.


1986.
Duration
Structure
Generation
and
Recognition in Musical Writing. In Proceedings of
the International Computer Music Conference. San
Francisco:
International
Computer
Music
Association. 75-81.
[2] Ariza, C. 2005a. An Open Design for ComputerAided Algorithmic Music Composition: athenaCL.
Ph.D. Dissertation, New York University.
[3] Ariza, C. 2005b. The Xenakis Sieve as Object: A
New Model and a Complete Implementation.
Computer Music Journal 29(2): 40-60.
[4] Ariza, C. 2008. Python at the Control Rate:
athenaCL Generators as Csound Signals. Csound
Journal 9.
[5] Bel, B. 1990. Time and musical structures.
Interface 19(2-3): 107-135.
[6] Berg, P. and R. Rowe, D. Theriault. 1980. SSP and
Sound Description. Computer Music Journal 4(1):
25-35.
[7] Cabrera, A. 2007. Using Python Inside Csound.
Csound Journal 1(6).
[8] Emmerson, S. 1976. Xenakis Talks to Simon
Emmerson. Music and Musicians 24: 24-26.

[9] Hoffman, P. 2000. A New GENDYN Program.


Computer Music Journal 24(2): 31-38.
[10] Malherbe, C. and G. Assayag, M. Castellengo. 1985.
Functional Integration of Complex Instrumental
Sounds in Musical Writing. In Proceedings of the
International Computer Music Conference. San
Francisco:
International
Computer
Music
Association. 185-192.
[11] Mesnage, M.
d'utilisation.

1998.

Criblographe

Manuel

[12] Roads, C. 1978. An Interview with Gottfried


Michael Koenig. Computer Music Journal 2(3): 1115, 29.
[13] Roads, C. 1996. The Computer Music Tutorial.
Cambridge: MIT Press.
[14] Roads, C. 2002. Microsound. Cambridge: MIT Press.
[15] Smith, J. O. 1991. Viewpoints on the History of
Digital Synthesis. In Proceedings of the
International Computer Music Conference. San
Francisco:
International
Computer
Music
Association. 1-10.
[16] Tipei, S. 1987. Maiden Voyages: A Score Produced
with MP1. Computer Music Journal 11(2): 49-64.
[17] Varga, B. A. 1996. Conversations with Iannis
Xenakis. London: Faber and Faber Limited.
[18] Xenakis, I. 1966. The Origins of Stochastic Music.
Tempo 78: 9-12.
[19] Xenakis, I. 1990. Sieves. Perspectives of New
Music 28(1): 58-78.
[20] Xenakis, I. 1992. Formalized Music: Thought and
Mathematics in Music. Indiana: Indiana University
Press.

You might also like