Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

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 signals [4] is extended herein by using Python for collecting, mapping, and interpolating sieve-derived This paper introduces applications of the Xenakis sieve values. Exploring the sonic relevance of particular sieves for sound synthesis and signal processing with Python and is enabled by these techniques, and offers a rich avenue Csound. Using Python to collect, interpolate, and map for further study. sieve-derived values, sieves are employed in Csound for additive and subtractive synthesis, amplitude and The Sieve class, distributed within athenaCL [2], is frequency modulation, and waveform segment synthesis. employed in this study. As an open-source, cross-platform Approaches to multi-dimensional interpolation of multiple Python system with no required software dependencies, sieve segments are demonstrated. athenaCL integrates well within Csound and offers diverse tools for computer-aided algorithmic composition. This paper demonstrates using components of athenaCL tools 1. INTRODUCTION within Python scripts; the use of athenaCL’s interactive command-line interface is not discussed. (1922-2001) envisioned broad applications for sieves: “sieve theory is very general and consequently This paper briefly reviews the use of Python in Csound is applicable to any other sound characteristics that may as well as the basic operation of the Xenakis Sieve object be provided with a totally ordered structure” [20]. Xenakis and logic string. After introducing Python interpolation employed sieves as a way of generating ordered sequences objects, numerous practical applications of sieves for of integers out of periodicities. As summarized in Ariza synthesis and signal processing are provided. All [3], Xenakis describes the use of sieves in numerous examples are presented in the Csound Unified File acoustic compositions: (1965-1966) [18] Format, or CSD. Complete CSD files and rendered audio and Jonchaies (1977) [17], for example, are described as examples are available for download at the following having sieve-based pitch structures; (1975) [8] URL: http://www.flexatone.net/docs/sspaxspc.zip. Users and Komboï (1981) [17] are described as having sieve- must install Csound version 5 or later, Python, and based rhythmic structures. athenaCL version 1.4.9 or later to render these examples. The athenaCL directory must be within Python’s search In his later discussions of sieves, Xenakis noted that “… it is quite conceivable to apply this theory to the directory or, alternatively, installed within the Python site- packages directory. Consult Python and athenaCL synthesis of sounds by computer” [19]. While suggested, documentation for more information on installing Python Xenakis provides no documented examples of this modules. 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 2. USING PYTHON WITHIN CSOUND the Xenakis sieve for sound synthesis and signal processing with Python and Csound. The Csound Python opcodes permit Python objects to be instantiated and manipulated in the Csound orchestra. The expansion of Python scripting into Csound Values from these Python objects can be accessed within provides new opportunities for instrument control and instruments both at instrument initialization or as control design [7]. Used within the Csound orchestra, the Python- rate value generators. Within the Csound orchestra, the based Xenakis Sieve object [3] permits applying sieve- opcode must be called. Next, the block is derived values to a wide range of sonic parameters. The pyinit pyruni used to execute Python code, creating objects and use of Python methods and functions to create control-rate functions for use in Csound instrument definitions.

367 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

Within the Csound instrument, the pycall1i opcode >>> a = sieve.Sieve('3@0|3@1') >>> a(0, range(0,10), 'int') (for values assigned to i-rate initialization variables) or [0, 1, 3, 4, 6, 7, 9] pycall1 opcode (for values assigned to k-rate control Figure 2. The Union of Two Residual Classes rate variables) call the previously-instantiated Python functions or object methods with one or more arguments. Sieves define infinite number sequences. A sieve Depending on the number of values returned by the segment is a finite contiguous section of a sieve sequence. instantiated Python function or object method, different A sieve can be thought of as a filter applied to a finite pycall opcodes are used. The Csound pycall opcodes range of contiguous integers, labeled z. The generation of require the Python function or object method to return a z values is facilitated in Python with the range() floating-point value. 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 3. USING XENAKIS SIEVES AS PYTHON in different sizes and ranges of sieve segments. The sieve OBJECTS created in Figure 2, for example, can be used in Figure 3 The notation and definition of Sieve objects presented in to generate a sieve segment between -100 and -80 by Ariza [3] will be used in this paper. A brief introduction of calling the instantiated Sieve object with a different z. Sieve object functionality is provided using Python's interactive mode. The Python primary prompt is . >>> a = sieve.Sieve('3@0|3@1') >>> >>> a(0, range(-100,-80), 'int') After installing athenaCL, the sieve.py module can be [-99, -98, -96, -95, -93, -92, -90, -89, -87, -86, -84, -83, -81] imported. After importing, a basic Sieve object is created from a logic string and a sieve segment is extracted. The Figure 3. Creating Sieve Segments sieve segment is extracted by calling the Sieve object with Sieve segments can be represented in a variety of three arguments: a transposition n, a z, and a format string. formats. Ariza [3] defines four: integer, width, binary, and These concepts will be explained below. unit. This paper makes use of integer and unit sieve >>> from athenaCL.libATH import sieve segments. Integer segments are demonstrated above: these >>> a = sieve.Sieve('2@0') are the integer values extracted from the supplied z. Unit >>> a(0, range(0,10), 'int') [0, 2, 4, 6, 8] segments map z to the unit interval and return segment points as proportionally-spaced, floating-point values Figure 1. Importing sieve.py between 0 and 1. Figure 4 demonstrates both integer In Figure 1 the Sieve object “a” is instantiated using a (using the “int” argument) and unit (using the “unit” logic string. The logic string is a symbolic representation argument) sieve segments. of a sieve and consists of one or more residual classes >>> a = sieve.Sieve('3@0|5@4') combined by logic operators. A residual class defines an >>> a(0, range(0,16), 'int') infinite number sequence as a modulus (M) and a shift (I), [0, 3, 4, 6, 9, 12, 14, 15] >>> a(0, range(0,16), 'unit') notated as M@I. Each value in the sequence modulus M is [0.0, 0.20000000000000001, 0.26666666666666666, equal to I: thus 2@0 specifies all integers x where x % 2 0.40000000000000002, == 0, or, between 0 and 9, the values 0, 2, 4, 6, 8. 0.59999999999999998, 0.80000000000000004, 0.93333333333333335, 1.0] Similarly, 3@2 specifies all integers x where x % 3 == 2, or, between 0 and 9, the values 2, 5, 8. Specified sieve Figure 4. Unit and Integer Sieve Segments values can be thought of as active points on a grid of Given only a sieve logic string and a z, the number of integers. values obtained in the resulting sieve segment may not be Residual classes can be combined with logic operators immediately apparent. For this reason, rather than calling to produce more complex sequences. The permitted logic the Sieve object with a z value, the collect() method operators and their symbolic representation is as follows: may be employed. This method, given a minimum z value union (|), intersection (&), symmetric difference (^), and and a desired segment length, will continue to expand z complementation (-). As defined in Ariza [3], upward as necessary to collect as many values as combinations of residual classes and logic operators can specified. The method requires four arguments: a result in simple and complex sieves. Maximally simple transposition n, a minimum z, a length, and a format. In sieves use residual classes combined only by union; for Figure 5 the first 10 values from a sieve segment starting clarity, this paper makes use only of maximally simple at 30 are collected. sieves. The example below demonstrates a simple sieve, combining two residual classes by union. >>> 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

368 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

The flexible representation of sieves as logic strings provides a clear representation of sieve structures. Logic >>> from athenaCL.libATH.omde import bpf >>> a = bpf.LinearSegment([(0,50),(1,60)]) strings permit creating both simple and complex sieves. >>> a(0) Xenakis, using a similar notation, describes the goal of 50.0 >>> a(.75) creating “… symmetries which are as complex as one 57.5 might want” [19]. >>> a(.95) 59.5 Alternatively, Xenakis describes the need “to retrieve from a given series of events or objects in space or time Figure 7. Using LinearSegment interpolation the symmetries that constitute the series” [19]. As shown The HalfCosineSegment object, demonstrated in in Figure 6, the Python Sieve object permits the formation Figure 8, provides a smoother transition to the specified of Sieve objects by providing a list of integers without a points. logic string. When providing a list of integers, a z is automatically determined: this determination may have an >>> from athenaCL.libATH.omde import bpf affect on what logic string is derived from the integer >>> a = bpf.HalfCosineSegment([(0,50),(1,60)]) >>> a(0) sequence [3]. 50.0 >>> a(.75) >>> a = sieve.Sieve([3,9,10,15,19,20]) 58.535533905932738 >>> print a >>> a(.95) 6@3|9@1|10@0 59.938441702975688 Figure 6. Forming a Sieve without a Logic String Figure 8. Using HalfCosineSegment interpolation For applications in synthesis and signal processing There are numerous ways sieves can be interpolated. A parameters, the unit sieve segment format is particularly one-dimensional approach interpolates between the points useful. Once scaled within the unit interval, a sieve of a single sieve segment. As values from the actual sieve segment can subsequently be scaled within any value segment are transitory, this approach does not maintain range. Scaling values between sieve points is suggested by the spacing and structure of the sieve segment, and is not Xenakis: of the three hyperbolae, or transformations, of demonstrated in this paper. A second approach takes sieves suggested, one is the modification of the “unity,” or multiple sieve segments of equal size and interpolates the space between sieve integers [19]. between adjacent points from different segments. A third approach takes two-dimensional matrices created by sieves and interpolates between adjacent matrices. 4. INTERPOLATION OF SIEVE SEGMENTS

Working with multiple sieves is facilitated by creating 5. APPLICATIONS OF SIEVES FOR ADDITIVE arrays of equal-sized sieve segments and, rather than using SYNTHESIS, SUBTRACTIVE SYNTHESIS, AND one at a time, interpolating between them. This approach FREQUENCY MODULATION permits, while cycling between sieve segment points, smoothly fading between different sieves. Xenakis states that sieves can be applied to “any set of Roads [13] discusses numerous varieties of characteristics of sound or of well-ordered sonorous interpolation; here, linear and half-cosine interpolation structures … especially to any group furnished with an will be used. When connecting waveform segments as additive operation and whose elements are multiples of a breakpoints, as demonstrated below, half-cosine unity” [19]. Additive synthesis, while an elementary interpolation is preferred: “half-cosine interpolation technique, offers a practical application. This approach ensures a smooth curve between the breakpoints” [13]. can be extended to coordinate arrays of band-pass filters, with applications in subtractive synthesis and vocoding. The athenaCL system offers numerous break-point Modulating between different sieves configured as segment generator objects. These objects offer the frequency bands can offer an unusual form of frequency additional functionality of holding last-defined values modulation. when requesting a point beyond the defined break-points. The break-point function generators, defined in the bpf.py The CSD specified in Figure 10 is a simple module, are based in part on the Object-oriented Music demonstration of additive synthesis using sieve-derived Definition Environment (OMDE/pmask) by Maurizio integers as frequency scalars and sieve-derived unit- Umberto Puxemdu. interval values for amplitude scalars. A base frequency, converted from a MIDI note number, is provided from the In Figure 7 a LinearSegment object is created from score; this frequency is than scaled by the sieve-derived two points. Points are specified as a list of lists, where each component list defines x and y value pairs. Once integers. A Python function, sieveValues(), is defined instantiated, for any given x the interpolated y value is to return values from two sieve segments. Given an index returned. position (converted to an integer), this function returns an

369 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

integer sieve segment point (converted to a float) and a envelopes for each sine component, or by varying initial randomly selected floating-point sieve segment point. A phase. variety of other probabilistic or methods could Rather than employing integer scalars of a base be used to select sieve values. A graphic representation of frequency, frequency values can be specified as unit- these two sieve segments is provided in Figure 9. interval 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 Figure 9. Ordered Integer Sieve Segment and used. The Python SieveInterpolate class is defined to Unordered Unit Interval Sieve Segment interpolate between two or more equal-sized sieve segments. A half-cosine interpolation segment is created to move between parallel sieve points within sieve pyinit segments. When calling an instance of SieveInterpolate pyruni {{ import random; from athenaCL.libATH import sieve within Csound, the user specifies the desired sieve a = sieve.Sieve('5@2|9@0|13@1') segment point as well as a control-rate interpolation value aSegInt = a.collect(0, 1, 8, 'int') aSegUnit = a.collect(0, 49, 8, 'unit') representing the range between adjacent sieve points. This def sieveValues(i): type if interpolation creates a two-dimensional matrix of return (float(aSegInt[int(i)]), float(random.choice(aSegUnit))) values, with discrete values between points of a sieve }} segment and continuous values between points of adjacent sr = 44100 ksmps = 10 sieves. A graphic representation of this mapping is nchnls = 1 provided in Figure 11. instr 100 iDur = p3 iAmp = ampdbfs(p4) iFqBase = cpsmidinn(p5) iAmpScale = 0.6

iFq1,iAmp1 pycall2i "sieveValues", 0 iFq2,iAmp2 pycall2i "sieveValues", 1 iFq3,iAmp3 pycall2i "sieveValues", 2 iFq4,iAmp4 pycall2i "sieveValues", 3 iFq5,iAmp5 pycall2i "sieveValues", 4 iFq6,iAmp6 pycall2i "sieveValues", 5 iFq7,iAmp7 pycall2i "sieveValues", 6 iFq8,iAmp8 pycall2i "sieveValues", 7 Figure 11. Mapping Multiple Interpolated Sieve aSig1 oscili iAmp*iAmp1, iFqBase*iFq1, 1, 0 aSig2 oscili iAmp*iAmp2, iFqBase*iFq2, 1, 0 Segments to Frequency aSig3 oscili iAmp*iAmp3, iFqBase*iFq3, 1, 0 aSig4 oscili iAmp*iAmp4, iFqBase*iFq4, 1, 0 aSig5 oscili iAmp*iAmp5, iFqBase*iFq5, 1, 0 aSig6 oscili iAmp*iAmp6, iFqBase*iFq6, 1, 0 pyinit aSig7 oscili iAmp*iAmp7, iFqBase*iFq7, 1, 0 pyruni {{ aSig8 oscili iAmp*iAmp8, iFqBase*iFq8, 1, 0 import random, math; from athenaCL.libATH import sieve from athenaCL.libATH.omde import bpf class SieveInterpolate: aMix = (aSig1 + aSig2 + aSig3 + aSig4 \ def __init__(self, segments): + aSig5 + aSig6 + aSig7 + aSig8) * iAmpScale points = [[] for i in range(len(segments[0]))] kEnvl adsr .2*iDur, .2*iDur, .7, .2*iDur for i in range(len(segments[0])): out aMix * kEnvl for q in range(len(segments)): endin points[i].append((q, segments[q][i])) self.gen = [] for i in range(len(segments[0])): f 1 0 16384 10 1 row = bpf.HalfCosineSegment(points[i]) i 100 0 4 -12 40 self.gen.append(row) i 100 6 4 -12 44 def __call__(self, i, phase): return self.gen[int(math.floor(i))](phase)

Figure 10. Additive Synthesis with Sieves a = sieve.Sieve('2@1|7@6') aSeg = a.collect(0, 1, 8, 'unit') This example can be extended with the addition of b = sieve.Sieve('5@1|9@8|13@11') bSeg = b.collect(0, 1, 8, 'unit') noise to the frequency scalars, by providing dynamic c = sieve.Sieve('4@1|7@8|21@3') cSeg = b.collect(0, 117, 8, 'unit') interpolator = SieveInterpolate([aSeg, cSeg, bSeg])

370 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

}} In Figure 14, four unit interval sieve segments are sr = 44100 ksmps = 100 created and shuffled. Rather than random shuffling, sieve nchnls = 1 segment points might be arranged to meet specific needs. instr 100 iDur = p3 Next, these segments are provided to the SieveInterpolate iAmp = ampdbfs(p4) class. The Csound instrument definition creates an iAmpScale = 0.5 iBw = 3 amplitude envelope from these interpolated values, where iFqBase = cpsmidinn(p5) two scaled phasors are used to select sieve points within iFqRange = p6 segments and to interpolate between adjacent sieve segments. The resulting amplitude envelope, scaled by kMorphFlat line 0, iDur, 2 kMorphMod oscili 1, 40, 1, 0 unity and converted into an audio-rate signal, is low-pass kMorphMod = (kMorphMod + 1) * .5 filtered and then applied to white noise. A graphic kFq1 pycall1 "interpolator", 0, kMorphFlat kFq2 pycall1 "interpolator", 1, kMorphMod representation of this mapping is provided in Figure 13 kFq3 pycall1 "interpolator", 2, kMorphFlat kFq4 pycall1 "interpolator", 3, kMorphFlat kFq5 pycall1 "interpolator", 4, kMorphFlat kFq6 pycall1 "interpolator", 5, kMorphMod kFq7 pycall1 "interpolator", 6, kMorphFlat kFq8 pycall1 "interpolator", 7, kMorphFlat

aSrc random -0dbfs, 0dbfs aSig1 butterbp aSrc, iFqBase+(iFqRange*kFq1), iBw aSig2 butterbp aSrc, iFqBase+(iFqRange*kFq2), iBw aSig3 butterbp aSrc, iFqBase+(iFqRange*kFq3), iBw aSig4 butterbp aSrc, iFqBase+(iFqRange*kFq4), iBw aSig5 butterbp aSrc, iFqBase+(iFqRange*kFq5), iBw aSig6 butterbp aSrc, iFqBase+(iFqRange*kFq6), iBw aSig7 butterbp aSrc, iFqBase+(iFqRange*kFq7), iBw Figure 13. Mapping Multiple Interpolated Sieve aSig8 butterbp aSrc, iFqBase+(iFqRange*kFq8), iBw Segments to Envelope Amplitudes

aMix = (aSig1 + aSig2 + aSig3 + aSig4 \ + aSig5 + aSig6 + aSig7 + aSig8) * iAmpScale kEnvl adsr .1*iDur, .2*iDur, .8, .1*iDur pyinit out aMix * kEnvl pyruni {{ endin import random, math; from athenaCL.libATH import sieve from athenaCL.libATH.omde import bpf class SieveInterpolate: f 1 0 16384 10 1 def __init__(self, segments): i 100 0 6 -12 48 4000 points = [[] for i in range(len(segments[0]))] i 100 0.1 8 -12 44 4000 for i in range(len(segments[0])): i 100 0.2 6 -12 46 8000 for q in range(len(segments)): i 100 0.3 6 -12 43 6000 points[i].append((q, segments[q][i])) self.gen = [] for i in range(len(segments[0])): row = bpf.HalfCosineSegment(points[i]) Figure 12. Dynamic Subtractive Synthesis with self.gen.append(row) def __call__(self, i, phase): Sieves return self.gen[int(math.floor(i))](phase) This approach can be extended in a variety of ways. a = sieve.Sieve('2@1|7@6') For example, rapid modulation between sieve segments a1Seg = a.collect(0, 1, 8, 'unit') can be used to create frequency modulation; as the a2Seg = a.collect(0, 29, 8, 'unit') b = sieve.Sieve('5@1|9@8|13@11') frequency distance between each sieve segment point may b1Seg = b.collect(0, 1, 8, 'unit') be different, novel timbres emerge. b2Seg = b.collect(0, 87, 8, 'unit') segments = [a1Seg, b1Seg, a2Seg, b2Seg] for seg in segments: random.shuffle(seg) 6. APPLICATIONS OF SIEVES FOR DYNAMIC interpolator = SieveInterpolate(segments) AMPLITUDE ENVELOPES AND AMPLITUDE }} sr = 44100 MODULATION ksmps = 100 nchnls = 1 While related to creating rhythms with sieves, sieves can instr 100 be used to create dynamic amplitude envelopes. Similar to iDur = p3 iAmp = ampdbfs(p4) an analog step sequencer, the ordered or unordered iAmpScale = 0.1 floating point values of a sieve segment can be used as a iSegCount = 3.9999 control signal and looped. Using the SieveInterpolate class introduced in Figure 12, this looping pattern can be kMorph oscili 1, 0.125, 1, 0 kMorph = ((kMorph + 1) * .5) * iSegCount gradually morphed into patterns derived from other sieve kSelect phasor 1.2 segments. kSelect = kSelect * 7.9999

371 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

is added, and the segment is re-scaled within the unit kSrc pycall1 "interpolator", kSelect, kMorph interval. The y-coordinate segment is used for amplitude aSrc = kSrc * 1 spacings; the last segment point is added to the beginning, aSrc lowpass2 aSrc, 120, .85 aNoise random -0dbfs, 0dbfs creating a loop. Next, x and y points are collected as pairs aMix = aNoise * aSrc * iAmpScale and passed to a HalfCosineSegment interpolator object. kEnvl adsr .1*iDur, .2*iDur, .8, .1*iDur out aMix * kEnvl When calling this object, an unit interval phase value, as a endin supplied x value, returns the interpolated unit interval y value. f 1 0 16384 10 1 The second Python object, SievePolyCoordinate, takes i100 0 16 -12 .3 a list of any number of SieveCoordinate objects as interpolating unit-interval wavetables, and permits Figure 14. Dynamic Envelopes with Sieves interpolating between adjacent tables at any phase value. When called, a unit interval phase is passed to the object; The approach demonstrated in Figure 14 can be the object then calls each component wavetable object applied for amplitude and ring modulation. The with the specified phase, collects all returned values, and interpolated sieve-derived signal can be driven at a much then uses these values to create a list of points for cross- faster rate (120 Hz, for example), the modulating signal wavetable interpolation. Using the LinearSegment can be scaled between -1 and 1 (for ring modulation), and interpolation object, values can be selected from a single modulation can be applied to a sine tone or other sound wavetable or smoothly interpolated between adjacent source. As the sieve segments are interpolated, transitions wavetables. When combined, SieveCoordinate and between unique timbres are clearly audible. SievePolyCoordinate create a three dimensional matrix, with continuous, interpolated values between all 7. APPLICATIONS OF SIEVES FOR WAVEFORM dimensions. A graphic representation of this approach is SEGMENT SYNTHESIS provided in Figure 15. 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 [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 Figure 15. Interpolating between Multiple Sieve- algorithm synthesis [15], or non-standard synthesis [12], is Constructed Wavetables related to Herbert Brün’s SAWDUST system [13] and Gottfried Michael Koenig’s Sound Synthesis Program In Figure 16, six sieve segments are used to construct (SSP) [6, 13]. four unit interval wavetables with the SieveCoordinate Waveform segment synthesis of sieves benefits from class. These wavetables are then provided to an alternative approach to interpolation. Two Python SievePolyCoordinate. The Csound instrument definition objects are used. The first, SieveCoordinate, employs two employs a variety of phasors to control table reading equal-sized sieve segments to create the x and y speed and interpolation between adjacent tables. The coordinates of a unit-interval wavetable. The x-coordinate output of the SievePolyCoordinate object is converted to segment is used for time spacings; these values must be an audio-rate signal, scaled between -1 and 1, and then recalculated as accumulated time steps. A zero start value

372 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

low-pass filtered at 16000 Hz. This signal is then scaled This approach to waveform segment synthesis and enveloped. 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 pyinit rate, resolution is lost in creating an audio signal. pyruni {{ Increasing the control rate, while increasing processing import random, math from athenaCL.libATH import sieve, unit time, improves the quality of waveform rendering. from athenaCL.libATH.omde import bpf class SieveCoordinate: def __init__(self, xSeg, ySeg): 8. FUTURE WORK yArray = [ySeg[-1]] + ySeg xArray = unit.unitNormAccumulate(xSeg) points = [] This paper introduces numerous approaches to sonifying for i in range(len(xArray)): and mapping sieve data for synthesis and signal points.append((xArray[i], yArray[i])) self.gen = bpf.HalfCosineSegment(points) processing applications. These examples are not def __call__(self, phase): exhaustive: sieve-derived values, as collected, return self.gen(phase) interpolated, and mapped here, can be applied to a wide class SievePolyCoordinate: def __init__(self, generators): range of synthesis and signal processing tasks. self.gen = list(generators) def __call__(self, n, phase): Future investigations of waveform segment synthesis val = [] might systematically explore relationships between for x in range(len(self.gen)): specific sieve structures and resultant sonic structures. The val.append((x, self.gen[x](phase))) out = bpf.LinearSegment(val) expansive new parameter spaces opened through the use return out(n) of sieve logic strings could be made more manageable a = sieve.Sieve('2@1|7@6') a1Seg = a.collect(0, 1, 16, 'unit') with an automated form of exploration: a Python- a2Seg = a.collect(0, 50, 16, 'unit') controlled system, for example, could produce vast b = sieve.Sieve('5@1|9@8|13@11') b1Seg = b.collect(0, 1, 16, 'unit') amounts of audio and analysis information, facilitating b2Seg = b.collect(0, 120, 16, 'unit') quickly surveying the sonic results of hundreds of sieves. for seg in [a1Seg, a2Seg, b2Seg, b1Seg]: random.shuffle(seg) c = sieve.Sieve('2@1|5@3') c1Seg = c.collect(0, 1, 4, 'unit') 9. REFERENCES c2Seg = c.collect(0, 34, 4, 'unit') [1] Amiot, E. and G. Assayag, C. Malherbe, A. Riotte. generator = SievePolyCoordinate([ 1986. “Duration Structure Generation and SieveCoordinate(a1Seg, b1Seg), SieveCoordinate(a2Seg, b2Seg), Recognition in Musical Writing.” In Proceedings of SieveCoordinate(c1Seg, c2Seg), the International Computer Music Conference. San SieveCoordinate(a1Seg, b1Seg)]) }} Francisco: International Computer Music sr = 44100 Association. 75-81. ksmps = 10 nchnls = 1 [2] Ariza, C. 2005a. An Open Design for Computer- instr 100 iDur = p3 Aided Algorithmic Music Composition: athenaCL. iAmp = ampdbfs(p4) Ph.D. Dissertation, New York University. iAmpScale = 0.6 iFqBase = cpsmidinn(p5) [3] Ariza, C. 2005b. “The Xenakis Sieve as Object: A iSegCount = 3.999 New Model and a Complete Implementation.” kMorph phasor iFqBase Computer Music Journal 29(2): 40-60. kSelect oscili 1, .125, 1, 0 kSelect = ((kSelect + 1) * .5) * iSegCount [4] Ariza, C. 2008. “Python at the Control Rate: kSrc pycall1 "generator", kSelect, kMorph aSrc = (kSrc * 2) - 1 athenaCL Generators as Csound Signals.” Csound aSrc lowpass2 aSrc, 16000, .85 Journal 9. aMix = iAmp * aSrc kEnvl adsr .1*iDur, .2*iDur, .8, .1*iDur [5] Bel, B. 1990. “Time and musical structures.” out aMix * kEnvl endin Interface 19(2-3): 107-135. [6] Berg, P. and R. Rowe, D. Theriault. 1980. “SSP and f 1 0 16384 10 1 Sound Description.” Computer Music Journal 4(1): i 100 0 15 -12 48 i 100 5 10 -12 39 25-35. i 100 10 5 -12 58 [7] Cabrera, A. 2007. “Using Python Inside Csound.” Csound Journal 1(6). Figure 16. Waveform Segment Synthesis with [8] Emmerson, S. 1976. “Xenakis Talks to Simon Sieves Emmerson.” Music and Musicians 24: 24-26.

373 Proceedings of the International Computer Music Conference (ICMC 2009), Montreal, Canada August 16-21, 2009

[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. 1998. “Criblographe Manuel d'utilisation.” [12] Roads, C. 1978. “An Interview with Gottfried Michael Koenig.” Computer Music Journal 2(3): 11- 15, 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.

374