thursday night general assembly by Backtrace
This has really satisfied the desire to join some kind of big exciting venture at the beginning (I thought it was going to be an early stage startup) but it's taken away from music stuff.
I did get a DosBox computer up and running last week so there will be more vintage 8-bit sounds in the near future, like these:
Friday, October 14, 2011
Thursday, September 29, 2011
Writing Software to Play Music to Write Software To
Writing Software to Play Music to Write Software To by Backtrace
These are the shorter tracks from a composition I wrote and recorded in October 2003 - the inner movements of a 3-hour set. (Full piece is on last.fm here) The outer movements were each around an hour. They're all excerpts of a continuous "musical clock" composition which would generate tones based on the computer's timestamp - hence the performance name "Mod 12". The generative algorithms are loosely based on 12's in date / time (12 months, 2*12 hours in a day, 5 * 12 minutes in an hour, etc) and the tone rows of 12-tone music (the self-similar structures that Charles Wourinen describes in "Simple Composition". I tweaked the chromaticism and use the values 0 through 11 for overtones instead of pitches in the chromatic scale. (Just grab the time in seconds, modulo 12, and throw it against the frequency modulator bits in the soundcard). I think the original inspiration for this system came from an argument I had with my freshman-year college roommate about the supposed impossibility of combining "minimalism" and "serialism", which led to me sketching some things out by hand (in Cakewalk) and then later getting into generative sound programming based on matrices of pitch ratios.
The culmination of this was 5-year (off and on) project in programming for the OPL chip on old Sound Blaster cards. I had kept a Windows 98 machine around with the correct soundcard so that I could run them - something to do when I got bored with writing sequenced stuff in Buzz*. In a way all of the Supercollider stuff I do now comes from this since it's all generative, and using basic waveform synths. There's a video element to these, but I've never been able to figure out a way to put the 2 of them together for recording (save plugging an old Toshiba laptop with S-Video into a VCR and making VHS tapes of the stuff, 10 years ago.) I did a handful of public performances around Chicago and Milwaukee back then too.
Early incarnations of this program (circa 1997) actually used the internal speaker (the beeper) right off the motherboard - some mp3's are on last.fm here.
I got into this kind of programming right around the time that the SB cards with the onboard synth chips were going out in favor of cards that just did straight digital PCM sound encoding, and the built-in midi synths all went for fakey sample-table based instruments (that whole corny mid-90's "Virtual Reality" aesthetic, which I'm sure will become retro-trendy in about 3 years). I used to look at thrift stores (or curbside) for machines with the right number of audio jacks on the sound cards and buy them just to pull the cards.
VMWare emulators don't support the FM Synth chips - I'm going to look into other software emulators like DosBox and see if they work - if so I'll post some videos of the whole thing. The source code has since been lost, but I have some compiled binaries up here along with the SB drivers, if anyone wants to take a swipe at getting these up and running. Here's the guide to programming for the AdLib/OPL2 chip - this is probably the exact same document I was using 12 years ago.
* Writing generative music in general was something I turned towards more and more as I hit my early 20's and started working for a living. As a teenager I spent most of my extra brain cycles on writing fiction, but I turned to music, and especially high-level hands-off generative music, as a way to keep doing something creative and engaging in times when the stresses and disruptions of work kept me from dwelling on the minutia of people whose lives I had invented. Prose has always been a hard thing for me to start and stop, but tweaking a few lines of code, holding the architecture** in my mind, while letting the computer fill in the details of a kind of sound world has always been easy.
** And maybe this has something to do with how I've always had really vivid dreams of ornate architectural spaces.
These are the shorter tracks from a composition I wrote and recorded in October 2003 - the inner movements of a 3-hour set. (Full piece is on last.fm here) The outer movements were each around an hour. They're all excerpts of a continuous "musical clock" composition which would generate tones based on the computer's timestamp - hence the performance name "Mod 12". The generative algorithms are loosely based on 12's in date / time (12 months, 2*12 hours in a day, 5 * 12 minutes in an hour, etc) and the tone rows of 12-tone music (the self-similar structures that Charles Wourinen describes in "Simple Composition". I tweaked the chromaticism and use the values 0 through 11 for overtones instead of pitches in the chromatic scale. (Just grab the time in seconds, modulo 12, and throw it against the frequency modulator bits in the soundcard). I think the original inspiration for this system came from an argument I had with my freshman-year college roommate about the supposed impossibility of combining "minimalism" and "serialism", which led to me sketching some things out by hand (in Cakewalk) and then later getting into generative sound programming based on matrices of pitch ratios.
The culmination of this was 5-year (off and on) project in programming for the OPL chip on old Sound Blaster cards. I had kept a Windows 98 machine around with the correct soundcard so that I could run them - something to do when I got bored with writing sequenced stuff in Buzz*. In a way all of the Supercollider stuff I do now comes from this since it's all generative, and using basic waveform synths. There's a video element to these, but I've never been able to figure out a way to put the 2 of them together for recording (save plugging an old Toshiba laptop with S-Video into a VCR and making VHS tapes of the stuff, 10 years ago.) I did a handful of public performances around Chicago and Milwaukee back then too.
Early incarnations of this program (circa 1997) actually used the internal speaker (the beeper) right off the motherboard - some mp3's are on last.fm here.
I got into this kind of programming right around the time that the SB cards with the onboard synth chips were going out in favor of cards that just did straight digital PCM sound encoding, and the built-in midi synths all went for fakey sample-table based instruments (that whole corny mid-90's "Virtual Reality" aesthetic, which I'm sure will become retro-trendy in about 3 years). I used to look at thrift stores (or curbside) for machines with the right number of audio jacks on the sound cards and buy them just to pull the cards.
VMWare emulators don't support the FM Synth chips - I'm going to look into other software emulators like DosBox and see if they work - if so I'll post some videos of the whole thing. The source code has since been lost, but I have some compiled binaries up here along with the SB drivers, if anyone wants to take a swipe at getting these up and running. Here's the guide to programming for the AdLib/OPL2 chip - this is probably the exact same document I was using 12 years ago.
* Writing generative music in general was something I turned towards more and more as I hit my early 20's and started working for a living. As a teenager I spent most of my extra brain cycles on writing fiction, but I turned to music, and especially high-level hands-off generative music, as a way to keep doing something creative and engaging in times when the stresses and disruptions of work kept me from dwelling on the minutia of people whose lives I had invented. Prose has always been a hard thing for me to start and stop, but tweaking a few lines of code, holding the architecture** in my mind, while letting the computer fill in the details of a kind of sound world has always been easy.
** And maybe this has something to do with how I've always had really vivid dreams of ornate architectural spaces.
Wednesday, September 28, 2011
"Rugs Not Drugs"
Spent most of the day going in circles with some scope / environment stuff in Supercollider and haven't really had the time to make sense of the docs on how to do inheritance with a Proto. The Apollo 11 sonification idea has come back to the foreground - was listening to some old drone pieces like Whiteout Drunk c omposed back when I didn't know how to do rhythm in Supercollider - that kind of 1-dimensional restriction was good for me, especially for the narrow emotional range I was willing to engage with in my music back then.
The drone server has been finicky - keeps crashing because, being hosted on a virtual server, the cpu allocation isn't constant and at some point the scsynth process sucks up 100% of the available cpu and jackd crashes. I'd like to write a piece with is the sonification of its own system resource usage, but that would require having a separate sclang process to monitor peakCPU (probably - but again, figuring out how to do it in a SynthDef is the kind of restriction I like. As far as I know thought, SynthDefs are constant in the # of cycles they use once instantiated).
I'd like to turn the Rugs not Drugs pieces in to something longer (originally I was imagining them as 90 minutes and 4hrs) but my interest in that world seems to wax and wane on a monthly cycle. I had orignally come up with those heavily phase-modulated synths back in June, while working on drones that could fit into a Twitter post.
The drone created by this synth is a matrix of static pitches-amplitudes and phase modulation amounts change, via the r modulator, which is really the heart of the piece. In 20110904 this synth is just instantiated w/ different starting values and left to run from anywhere from 5 seconds to 6 minutes.
20110904 by Backtrace I was inspired by the swaying motif's in Feldman's late orchestra stuff like "Coptic Light" - and by the way that the phrases in his music just hang in the air, each as its own little blob of sound, without really doing anything or going anywhere. This was what I was hearing in my head as I spent an hour cleaning cat hair out of my hallway runner with a lint brush.
The entire code for 20110905 is here:
The drone server has been finicky - keeps crashing because, being hosted on a virtual server, the cpu allocation isn't constant and at some point the scsynth process sucks up 100% of the available cpu and jackd crashes. I'd like to write a piece with is the sonification of its own system resource usage, but that would require having a separate sclang process to monitor peakCPU (probably - but again, figuring out how to do it in a SynthDef is the kind of restriction I like. As far as I know thought, SynthDefs are constant in the # of cycles they use once instantiated).
I'd like to turn the Rugs not Drugs pieces in to something longer (originally I was imagining them as 90 minutes and 4hrs) but my interest in that world seems to wax and wane on a monthly cycle. I had orignally come up with those heavily phase-modulated synths back in June, while working on drones that could fit into a Twitter post.
~synthFactory.("big-drone2",{|freq=120, n=#[1,2,3,4,5,6], p=#[0.5,0.66,1,2,3,4],x=3,of=4, ra=3,tt=1| var a=p *.x n, f=a*freq, b=SinOsc, c=FSinOsc, r=b.ar(1/f,c.ar(tt,of/a,of/a,0).tanh,c.ar(1/f.reverse,0,x,x)), t = c.ar(tt,pi/2,1/2,1/2); b.ar(PinkNoise.ar(f.log2,f) ,Blip.ar(PinkNoise.ar(r.sum**(f.log2 / 8192.log2),f),x,pi*b.ar(1/f,r,r)),((b.ar((r+r.sum).abs.sqrt/ra,r,2,2)**3)/f)*b.ar(a/(p**t) ,b.ar(a+(r.sum)**t,r,pi*b.ar(1/a,r.sum,b.ar(1/a,r,a))),r/x,0))},{[-1,1]/(1..6)},x);
The drone created by this synth is a matrix of static pitches-amplitudes and phase modulation amounts change, via the r modulator, which is really the heart of the piece. In 20110904 this synth is just instantiated w/ different starting values and left to run from anywhere from 5 seconds to 6 minutes.
20110904 by Backtrace I was inspired by the swaying motif's in Feldman's late orchestra stuff like "Coptic Light" - and by the way that the phrases in his music just hang in the air, each as its own little blob of sound, without really doing anything or going anywhere. This was what I was hearing in my head as I spent an hour cleaning cat hair out of my hallway runner with a lint brush.
The entire code for 20110905 is here:
( ~allServers = [Server.local,Server.internal]; ~allServers.collect({|serv| ~synthFactory.("ikat_yellow",{|a=#[1,1,1,1],t=#[128,129],xMod=1,xAdd=1,xRate=1,base=2,aExp=1| var x,p,h, e=1.exp, b=SinOsc; a=a*.x a*2; x=(b.ar((a**aExp) * xRate,0,xAdd,xMod)**e).tan; h =a *.x t; p =BrownNoise.ar(x.abs.log / base.log,t *.x a); b.ar(p,b.ar(p,0,((pi**(e/x)).sin)**x)*pi,6/h); },{[-1,1]/(1..4)},serv); ~synthFactory.("ikat_yellow_lower",{|a=#[1,1,1,1],t=#[128,129],xMod=1,xAdd=1,xRate=1,base=2,aExp=1| var x,p,h, e=1.exp, b=SinOsc; a=a*.x a*2; x=(b.ar((a**aExp) * xRate,0,xAdd,xMod)**e).tan; h =a *.x t; p =BrownNoise.ar(x.abs.log / base.log,h); b.ar(p,b.ar(p,0,((pi**(e/x)).sin)**x)*pi,6/h); },{[-1,1]/(1..4)},serv); ) ( Routine { ~f.set(\gate,0); ~g.set(\gate,0); ~f=Synth("ikat_yellow"); ~f.setn(\a,[1,3/8,441/512,8/7]); ~f.setn(\t,[128,1.5**12]); ~f.set(\xRate,1/(3**8)); ~f.set(\aExp,0); ~f.set(\xAdd,1.exp); ~f.set(\xMod,1.exp); ~f.set(\base,9); ~f.set(\amp,1); ~f.set(\gate,1); ~g=Synth("ikat_yellow_lower"); ~g.setn(\a,[21/16,4/3,3/7,1]); ~g.setn(\t,[128 * (441/512) * (8/7),128]); ~g.set(\xRate,1/(3**8)); ~g.set(\aExp,2); ~g.set(\xAdd,1); ~g.set(\xMod,14/49); ~g.set(\base,9); ~g.set(\amp,1.25); ~g.set(\gate,1); Routine { 5.do({|x| var z = [8,7,9,7,8].wrapAt(x), w = [6,4,5,4,6].wrapAt(x); x.postln; [~f,~g].collect({|d|d.set(\xRate,(1/3**z))}); (3**w).wait; }) }.play; ((3**[6,4,5,4,6]).sum).wait; ~f.set(\gate,0); ~g.set(\gate,0); }.play; )
This piece instantiates 2 drones and just lets them run, tweaking one modulator parameter to divide the piece into blocks of [ 729, 81, 243, 81, 729 ] seconds. The x modulator is the key here - the grumbling, ripping, and pinging sounds created as it modulates the amount of noise in each pitch layer of the drone. The septimal scale is always mostly yellow to me (like the rug which inspired the piece). I'm also really starting to like the 1:8/7 harmony - the inspiration for both of these pieces was just a drone on a stack of pitches at 8/7 ratios to each other.
20110905 by Backtrace
Monday, September 26, 2011
Otomata + Monome * Supercollider
Last week I got a Monome and I've been playing with some Otomata stuff using a Supercollider implementation by Corey Kereliuk.
The first composition I put together was a simple, cheerful minimalist piece and one of the first things I've done using equal temperament (midi notes) in about 5 years.
20110918 Otomata by Backtrace
I started tweaking the code so that I could add more instruments (the above example has a percussive instrument and a sustained pulse-wave instrument) and having the cellular automata trigger a callback function when they hit a wall, instead of just triggering a synth. I could then load that callback function with whatever synths I wanted. I also started polling the instantiated Otomata object itself for global data (like the x,y positions of all the automata at a given moment) so I could use that for musical data. You can hear chord changes in this piece - I had the program I was using count the # of ticks that the sequencer routine was running and store these in a global variable, which I then used to cycle through a set of different scales.
After 4 days straight of playing with this stuff, I think you can sense the burnout setting in a little with this piece (at least that's how I felt about it - not that feel burned out creatively on Otomata, but that this is where my brain goes around 3 AM after playing with sounds all day):
20110922 Otomata by Backtrace
20110922 Otomata by Backtrace
There are multiple instruments being triggered, some effect parameters (filter sweeps) being tweaked by the global state of the Otomata board. After recording this piece I decided it was time to clean up the code for it and try to get some useful, standalone application out of it.
I modifed the original Otomata.sc class to a Proto (so I could tweak it at runtime, without having to recompile SC). I'm working on a Proto version of the Automaton class as well.
Musical information (scale, synths, starting pitch) was decoupled from the sequencer logic - scale and synth are now controlled via the synthFunc callback function and can be switched dynamically as the Otomata is running. See how synthFunc variable is added to the Automaton class in Automaton.sc, and examples of its use in otomata.scd.
Methods to add and remove automata from a running otomata - ~removeOldest, ~removeNewest, and ~removeNth.
Global metadata about the otomata to give additional musical parameters across all of the automata - see the ~dSum, ~xySum, ~xSum, ~ySum. ~age attribute can be accessed to change values over time. I'd like to add similar attributes to each automaton, like age, # of collisions, # of wall hits, "dizziness" (# of right angle turns over time).
In the example code in otomata.scd I show how to use supercollider's function composition operator <> to attach multiple sound callbacks to 1 automaton. if you have 2 functions f(x) and g(x) then h = f<>g creates the function h = f(g(x)) - so whatever g returns is passed on to x. if you're chaining synth callbacks, the function should return the same "note" value that it takes.
Methods to add and remove automata from a running otomata - ~removeOldest, ~removeNewest, and ~removeNth.
Global metadata about the otomata to give additional musical parameters across all of the automata - see the ~dSum, ~xySum, ~xSum, ~ySum. ~age attribute can be accessed to change values over time. I'd like to add similar attributes to each automaton, like age, # of collisions, # of wall hits, "dizziness" (# of right angle turns over time).
In the example code in otomata.scd I show how to use supercollider's function composition operator <> to attach multiple sound callbacks to 1 automaton. if you have 2 functions f(x) and g(x) then h = f<>g creates the function h = f(g(x)) - so whatever g returns is passed on to x. if you're chaining synth callbacks, the function should return the same "note" value that it takes.
Thanks to Corey for posting his code, and to Batuhan Bozkurt for designing the Otomata.
The code posted here is really meant as an example of how to use callback functions in SC and a couple of other techniques - but feel free to use it if you'd like. In the near future I'll post something that's a little more conceptually coherent.
Wednesday, September 14, 2011
HOWTO: Stream mp3's with icecast on Ubuntu / Rackspace Cloud
In this post I'll explain how to set up a streaming audio server in the cloud. We'll be using Rackspace Cloud (Slicehost) running Ubuntu Lucid in the examples. I'll explain the steps in brief first, and then in detail, indicating places where you may need to back up and do extra work, depending on how your system is configured. This howto is meant for people who have experience configuring Linux servers, but are a little lost in the particulars of getting an Icecast server up and running (I couldn't find a good step-by-step guide when I did this). You should be familiar with ssh, installing packages with apt-get, and using make to compile from source.
Streaming mp3's, the basic concept: you put mp3's on your cloud server, you set up a streaming audio server, and people listen using a web client that points to your cloud server. This requires 2 applications - Icecast2 and Ices. This was the first point of confusion for me. Icecast2 is a web server that clients (like iTunes, or a browser) connect to in order to get the streaming audio signal. However, the conversion of binary files to streamable audio data is accomplished by a different app, Ices. Ices and Icecast2 don't have to run on the same server - you can configure them so that the mp3's come from one machine (like one in your home or office) and then get streamed up to your Icecast server, which in turn streams out to multiple listeners on the internet. For this example, both Ices and Icecast2 will be running on the same virtual machine.
Setting up Icecast2 is straightforward enough - I used the instructions in this tutorial on howtoforge.com. Ices2 requires a little bit more work though. The problem with the howtoforge.com example is that by default Ices only works for files encoded in the Ogg Vorbis format (.ogg). In order to get Ices to play mp3's, we need to grab the right libraries and build it from source.
If you have an ubuntuforums.org account you can check the thread here for instructions. If you don't have an ubuntuforums account, I'll explain here (since getting an account on that forum just to read an archived thread took like 20 validation steps).
You'll need to install the following packages:
build-essential
libshout3-dev
liblame-dev
libmp3lame-dev
libmp3lame-dev isn't part of the standard Ubuntu distro so you'll need to edit your /etc/apt/sources.list file to include packages from the Ubuntu Multiverse.
I also already had libxml and jackd installed on this system - so install these packages as well. (TODO: figure out if these packages or any of their dependencies (more likely) are actually needed for Icecast / Ices)
To install Ices from source:
Download the ices0 source package from icecast.org and extract it to your home directory on your cloud server. Then cd to the source directory and run the following commands:
./configure
make
sudo make install
Now you'll need to set up an Ices configuration file to tell Ices where to find your mp3's. This thread gives a good example of the format (the last code sample posted). You can also check the ices.conf.dist file in /usr/local/etc/ The file should be called ices.conf and live in /etc/ices/ I use a playlist file called "playlist.txt" in the same directory as the ices.conf. The playlist.txt file is just a plaintext file with the full path of each mp3 I want to play on its own line (be sure to trim any whitespace from the end of the lines, I had problems with ices not finding files due to trailing whitespace.).
You may need to tweak your icecast2 file in /etc/init.d - make sure it has the line
ICES="/usr/local/bin/ices -c /etc/ices/ices.conf"
To load the right ices.conf file when the daemon starts.
You can now start icecast by running /etc/init.d/icecast2 start - if you want uptime insurance, you can set up a monit process to restart icecast if it crashes. Just add the following code to your monitrc file (assumes you have icecast running on port 8000):
Streaming mp3's, the basic concept: you put mp3's on your cloud server, you set up a streaming audio server, and people listen using a web client that points to your cloud server. This requires 2 applications - Icecast2 and Ices. This was the first point of confusion for me. Icecast2 is a web server that clients (like iTunes, or a browser) connect to in order to get the streaming audio signal. However, the conversion of binary files to streamable audio data is accomplished by a different app, Ices. Ices and Icecast2 don't have to run on the same server - you can configure them so that the mp3's come from one machine (like one in your home or office) and then get streamed up to your Icecast server, which in turn streams out to multiple listeners on the internet. For this example, both Ices and Icecast2 will be running on the same virtual machine.
Setting up Icecast2 is straightforward enough - I used the instructions in this tutorial on howtoforge.com. Ices2 requires a little bit more work though. The problem with the howtoforge.com example is that by default Ices only works for files encoded in the Ogg Vorbis format (.ogg). In order to get Ices to play mp3's, we need to grab the right libraries and build it from source.
If you have an ubuntuforums.org account you can check the thread here for instructions. If you don't have an ubuntuforums account, I'll explain here (since getting an account on that forum just to read an archived thread took like 20 validation steps).
You'll need to install the following packages:
build-essential
libshout3-dev
liblame-dev
libmp3lame-dev
libmp3lame-dev isn't part of the standard Ubuntu distro so you'll need to edit your /etc/apt/sources.list file to include packages from the Ubuntu Multiverse.
I also already had libxml and jackd installed on this system - so install these packages as well. (TODO: figure out if these packages or any of their dependencies (more likely) are actually needed for Icecast / Ices)
To install Ices from source:
Download the ices0 source package from icecast.org and extract it to your home directory on your cloud server. Then cd to the source directory and run the following commands:
./configure
make
sudo make install
Now you'll need to set up an Ices configuration file to tell Ices where to find your mp3's. This thread gives a good example of the format (the last code sample posted). You can also check the ices.conf.dist file in /usr/local/etc/ The file should be called ices.conf and live in /etc/ices/ I use a playlist file called "playlist.txt" in the same directory as the ices.conf. The playlist.txt file is just a plaintext file with the full path of each mp3 I want to play on its own line (be sure to trim any whitespace from the end of the lines, I had problems with ices not finding files due to trailing whitespace.).
You may need to tweak your icecast2 file in /etc/init.d - make sure it has the line
ICES="/usr/local/bin/ices -c /etc/ices/ices.conf"
To load the right ices.conf file when the daemon starts.
You can now start icecast by running /etc/init.d/icecast2 start - if you want uptime insurance, you can set up a monit process to restart icecast if it crashes. Just add the following code to your monitrc file (assumes you have icecast running on port 8000):
check process icecast2 with pidfile /var/run/icecast
start program = "/usr/bin/sudo /etc/init.d/icecast2 start"
stop program = "/usr/bin/sudo /etc/init.d/icecast2 stop"
if failed port 8000 protocol HTTP
request /
with timeout 60 seconds
then start
if failed port 8000 protocol HTTP
request /
with timeout 60 seconds
then alert
Sunday, May 29, 2011
Pendulum Studies
The period of one complete cycle of the dance is 60 seconds. The length of the longest pendulum has been adjusted so that it executes 51 oscillations in this 60 second period. The length of each successive shorter pendulum is carefully adjusted so that it executes one additional oscillation in this period. Thus, the 15th pendulum (shortest) undergoes 65 oscillations.
Inspired by this youtube video, some Supercollider instruments based on periodic fluctuations in amplitude over a series of overtones. The first one mimics the motion in the video - 15 sine waves which oscillate between 51 and 65 hz cycles per minute.
(
//t is the timing of the pendulum swinging
var t = (51..65)/60;
//p is the pitch, 15 overtones of 60 hz
var p = 60 *(1..15);
//modulate the amplitude of each pitch p by t
{Mix.ar(Pan2.ar(FSinOsc.ar(p,0,LFPulse.kr(t,0,0.5,1,0) * 0.04),SinOsc.kr(t,0,1,-0.5)))}.play;
)
Pendulum-15-60 by Backtrace
(
var t = (201..328)/256;
var p = 64 *(1..128);
{Mix.ar(Pan2.ar(FSinOsc.ar(p,0, (1/((p+64)*p.log2)) * LFPulse.kr(t,0,0.5,1,0) *48),SinOsc.kr(t,0,1,-0.5)))}.play;
)
You can always add more oscillators to this series:
Pendulum-128-256 by Backtrace
Saturday, March 26, 2011
A Tweetable Drone
A tweetable Drone
Make a drone in Supercollider than can be posted on Twitter with few enough characters to include the #supercollider hashtag.
This is how many years it takes for the cycle to repeat
/*The lowest note is
63 HZ and the highest is
63 * (3/2**5) * 9
4305.65625 Hz
The fundamental frequency of 64 and 63. 1/64 is the difference between an overtone major third and a major third in pythagorean tuning (5/4, or 80/64 vs 9/8 * 9/8, or 81/64). 1:64 is also the ratio of a pitch and another pitch 6 octaves above it, so in a sense you can think of the rhythm as the pitch, 6 octaves down.
This code defines 2 sine waves at 64 and 63 hz. I'm using the "Blip" Ugen because it has fewer characters than SinOsc and because we can use it to add more overtones to our sound without adding more characters to the code:
Fundamental tone by Backtrace
Add overtones up to the 8th overtone:
Fundamental with overtones by Backtrace
Now, just the sine waves, but harmonized. The expression (3/2**(0..5)) produces an array with these values
Which are the first 6 steps around the circle of 5ths, or if this were written in C major, you would start at C and go up a perfect 5th to G, then D, A, E and B natural, all pitches within the key of C major.
Harmonized in 5ths by Backtrace
Now the same chord, with the overtones. This is the complete set of pitches in the piece, but due to phase cancellation all of these pitches are never heard at once.
All pitches by Backtrace
This is the shape of the moduation on the pitches - using Blip.kr. You can hear how the pitch takes different sized dips, large and small. In the final piece, the modulation is much slower and more subtle.
Modulator shape by Backtrace
Here, multichannel expansion produces 4 pairs of [63,64] modulating in patterns that are increasingly out of step with each other. The mutiple modulators are created using the expression (99..96) which produces an array with values [99,98,97,96] and then this signal is multipled by the frequencies [64,64] via the *.x operator.
Multiple modulators by Backtrace
Now let's hear the same, with overtones.
Overtones with large modulators by Backtrace
Now, let's slow the modulaton down 10,000 times. and make the actual pitch bending much much smaller, from a range of 1/(2**13) to 1/(2**18). These changes in pitch are far too small to hear - the largest is 1/8192 multiplied by the highest pitch 4305.65625 which is (4305.65625/8192) which is about 0.52 of 1Hz. What you hear instead is the sound of pitches up and down the overtone series fading in and out via phase cancellation.
Overtones with small modulators by Backtrace
Finally, we add our harmony, in stacked perfect 5ths.
The last step is to revisit our Pan2 UGen and use multichannel expansion, ranges and operator adverbs to split the Blip UGens into 16 channels panned from full left to full right
[-1,1]/.x(1..9)
produces:
At 127 characters, we still have room to add the #supercollider hash tag
Tweetable drone by Backtrace
Make a drone in Supercollider than can be posted on Twitter with few enough characters to include the #supercollider hashtag.
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x(3/2**(0..5))*.x
Blip.kr(0.0001/(99..96),3,1/2**(13..18),1),9,0.03),[-1,1]/.x(1..9)))}.play
This is how many years it takes for the cycle to repeat
(1/(0.0001 /(99..96)).product) / (3600 *24 *365.25)
/*The lowest note is
63 HZ and the highest is
63 * (3/2**5) * 9
4305.65625 Hz
The fundamental frequency of 64 and 63. 1/64 is the difference between an overtone major third and a major third in pythagorean tuning (5/4, or 80/64 vs 9/8 * 9/8, or 81/64). 1:64 is also the ratio of a pitch and another pitch 6 octaves above it, so in a sense you can think of the rhythm as the pitch, 6 octaves down.
This code defines 2 sine waves at 64 and 63 hz. I'm using the "Blip" Ugen because it has fewer characters than SinOsc and because we can use it to add more overtones to our sound without adding more characters to the code:
{Mix.ar(Pan2.ar(Blip.ar([64,63],1,0.3)))}.play;
Fundamental tone by Backtrace
Add overtones up to the 8th overtone:
{Mix.ar(Pan2.ar(Blip.ar([64,63],9,0.3)))}.play
Fundamental with overtones by Backtrace
Now, just the sine waves, but harmonized. The expression (3/2**(0..5)) produces an array with these values
[ 1, 1.5, 2.25, 3.375, 5.0625, 7.59375 ]
Which are the first 6 steps around the circle of 5ths, or if this were written in C major, you would start at C and go up a perfect 5th to G, then D, A, E and B natural, all pitches within the key of C major.
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x(3/2**(0..5)),1,0.1)))}.play;
Harmonized in 5ths by Backtrace
Now the same chord, with the overtones. This is the complete set of pitches in the piece, but due to phase cancellation all of these pitches are never heard at once.
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x(3/2**(0..5)),9,0.1)))}.play;
All pitches by Backtrace
This is the shape of the moduation on the pitches - using Blip.kr. You can hear how the pitch takes different sized dips, large and small. In the final piece, the modulation is much slower and more subtle.
{Mix.ar(Pan2.ar(Blip.ar([64] *.x Blip.kr(1/4,3,1,2),1,0.03)))}.play;
Modulator shape by Backtrace
Here, multichannel expansion produces 4 pairs of [63,64] modulating in patterns that are increasingly out of step with each other. The mutiple modulators are created using the expression (99..96) which produces an array with values [99,98,97,96] and then this signal is multipled by the frequencies [64,64] via the *.x operator.
{Mix.ar(Pan2.ar(Blip.ar([64,63] *.x Blip.kr(1/(99..96),3,1/2,1),1,0.03)))}.play;
Multiple modulators by Backtrace
Now let's hear the same, with overtones.
{Mix.ar(Pan2.ar(Blip.ar([64,63] *.x Blip.kr(1/(99..96),3,1/2,1),9,0.03)))}.play;
Overtones with large modulators by Backtrace
Now, let's slow the modulaton down 10,000 times. and make the actual pitch bending much much smaller, from a range of 1/(2**13) to 1/(2**18). These changes in pitch are far too small to hear - the largest is 1/8192 multiplied by the highest pitch 4305.65625 which is (4305.65625/8192) which is about 0.52 of 1Hz. What you hear instead is the sound of pitches up and down the overtone series fading in and out via phase cancellation.
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x Blip.kr(0.0001/(99..96),3,1/2**(13..18),1),9,0.05)))}.play;
Overtones with small modulators by Backtrace
Finally, we add our harmony, in stacked perfect 5ths.
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x(3/2**(0..5))*.x Blip.kr(0.0001/(99..96),3,1/2**(13..18),1),9,0.05)))}.play;
The last step is to revisit our Pan2 UGen and use multichannel expansion, ranges and operator adverbs to split the Blip UGens into 16 channels panned from full left to full right
[-1,1]/.x(1..9)
produces:
[ -1, -0.5, -0.33333333333333, -0.25, -0.2, -0.16666666666667, -0.14285714285714, -0.125, -0.11111111111111,
1, 0.5, 0.33333333333333, 0.25, 0.2, 0.16666666666667, 0.14285714285714, 0.125, 0.11111111111111 ]
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x(3/2**(0..5))*.x
Blip.kr(0.0001/(99..96),3,1/2**(13..18),1),9,0.03),[-1,1]/.x(1..9)))}.play
At 127 characters, we still have room to add the #supercollider hash tag
{Mix.ar(Pan2.ar(Blip.ar([64,63]*.x(3/2**(0..5))*.x
Blip.kr(0.0001/(99..96),3,1/2**(13..18),1),9,0.03),[-1,1]/.x(1..9)))}.play #supercollider
Tweetable drone by Backtrace
Monday, February 21, 2011
A Short Study In Recursive Functions
(
//define our basic sine synth, with pink noise amplitude correction: amplitude = (1/freqency)
z = Server.internal;
SynthDef("just-sine",{|freq=100,atk=0.01, rel=10, sus=(-4), amp=0.005,pan =0,gate=0, mr=1, ma=1, mm=1|
Out.ar(0,Mix.ar(Pan2.ar(FSinOsc.ar( [1,1] *.x freq,0,amp/(freq+192)),pan)) * EnvGen.ar(Env.perc(atk,rel, 1, sus),gate, doneAction:2) );}).load(z);
)
(
//recursive function to bring frequency f below 2**14 or quit after 20 tries
~pitchCorrect = {|f,s=1|
(f < (2**14)).or(s>20).if ({f;},{ ~pitchCorrect.(f / (2**((f.log2.floor)/2).ceil),s+1);});
};
//express i as an array of binary digits (redo of the .asBinaryDigits method for integers > 8 bits
~count = {|i|
({|x|((1)*(i/(2**x)%2).floor)}!i.log2.ceil).asInt[0..(i.log2/2).floor.asInt];
};
~r = Routine {
~bounce = {|p,n,s,max|
var c = ~count.(p), pc= (2**(c.sum)).asInt, ps = (c.size+1).log2.ceil;
p.postln;
pc.factors.collect({|pp,j|
var m = Synth("just-sine"), pf=p.factors.permute(p).wrapAt(j).log2.ceil;
m.set(\freq,~pitchCorrect.(pf * 40 *(n**((1+c.sum).log2.ceil%2))*(((ps+1)/ps)**(pc.factors.size+n)))+j);
m.set(\pan, 1/p.factors.size * (-1**j));
m.set(\rel,(p.factors.size+n+s).sqrt * (1/~count.(p).size.log10),\atk,(n+s)/2048,\sus,-2 * p.factors.size.log2,\amp,32 * (1/(n+1)),\gate,1);
});
(1/(n+s)).wait;
(max<0).if({^max});
(n>s).if({~bounce.(p+1,1,~count.(p+1).sum,max-1) },{~bounce.(p,n+1,s,max)});
};
//start at 1024, go for 512 steps
~bounce.(1024,1,2,512);
};
~r.play;
)
z.scope
201102211 by Backtrace
Saturday, January 22, 2011
Problem 5 - Least Common Multiple
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
Of the early problems, I think this one was the easiest to solve. An integer is just a collection of prime factors multiplied together so to see what I was working with, I looked at the factorization of the integers 1 through 10:
I feel like Supercollider should really return [1] for 1.factors instead of [0] but that's the default behavior - technically 1 has no prime factors but multiplying by the factors of 1 should also not return 0. Otherwise, this code is a great example of the conciseness of sclang and - the first expression generates an array of the integers 1 through 10 - then collect iterates a function over that array and returns a new array. the underscore _ character is the "anonymous argument" for simple functions - we take each integer and return its factors using the factors method.
Anyway, now let's break down 2050 into its prime components and see how it compares:
So the distinct factors of 1..10 appear in 2050 at least once (which they would have to) and some of them appear more than once. I took a guess that this had to do with the maximum number of distinct factors in each of the numbers 1..10 and tried this:
This expression generates the factors 1 through 10, rejects any elements containing 0 from the list (to make up for the idiosyncracy of 1.factors = [0]) and then tests to see which numbers only have a single distinct prime factor. I do this using the .reject method. Since .factors returns a sorted array of factors, we can assume that if the first and last elements of a sorted array are the same value, then all elements in the array are the same value - we reject any arrays that fail this test. Finally, use .flatten to take the array of arrays and turn them into a single array of one dimension, and use the .product method (a kind of map/reduce shorthand) to multiply the elements of the array together.
Which returns 60480 - a little higher than what we were looking for. If we divide this result by our target value 2520, we get 24, and 24.factors is [2,2,2,3] - so these are the surplus factors that our current algorithm finds. I realized that those extra factors could be eliminated by only returning the first factor of any number that only had one value for its factors, and came up with this function:
~f(10) will return 2520 and ~f(20) returns the correct answer for this problem.
I think this is my best example so far of solving one of these problems by generating a large collection and then filtering it down to find the answer.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
Of the early problems, I think this one was the easiest to solve. An integer is just a collection of prime factors multiplied together so to see what I was working with, I looked at the factorization of the integers 1 through 10:
({|i|i+1}!10).collect(_.factors);
[ [ 0 ], [ 2 ], [ 3 ], [ 2, 2 ], [ 5 ], [ 2, 3 ], [ 7 ], [ 2, 2, 2 ], [ 3, 3 ], [ 2, 5 ] ]
I feel like Supercollider should really return [1] for 1.factors instead of [0] but that's the default behavior - technically 1 has no prime factors but multiplying by the factors of 1 should also not return 0. Otherwise, this code is a great example of the conciseness of sclang and - the first expression generates an array of the integers 1 through 10 - then collect iterates a function over that array and returns a new array. the underscore _ character is the "anonymous argument" for simple functions - we take each integer and return its factors using the factors method.
Anyway, now let's break down 2050 into its prime components and see how it compares:
2520.factors;
[ 2, 2, 2, 3, 3, 5, 7 ];
So the distinct factors of 1..10 appear in 2050 at least once (which they would have to) and some of them appear more than once. I took a guess that this had to do with the maximum number of distinct factors in each of the numbers 1..10 and tried this:
({|i|i+1}!10).collect(_.factors).reject(_==[0]).reject({|i|i[0] != i.reverse[0]}).flatten.product;
This expression generates the factors 1 through 10, rejects any elements containing 0 from the list (to make up for the idiosyncracy of 1.factors = [0]) and then tests to see which numbers only have a single distinct prime factor. I do this using the .reject method. Since .factors returns a sorted array of factors, we can assume that if the first and last elements of a sorted array are the same value, then all elements in the array are the same value - we reject any arrays that fail this test. Finally, use .flatten to take the array of arrays and turn them into a single array of one dimension, and use the .product method (a kind of map/reduce shorthand) to multiply the elements of the array together.
Which returns 60480 - a little higher than what we were looking for. If we divide this result by our target value 2520, we get 24, and 24.factors is [2,2,2,3] - so these are the surplus factors that our current algorithm finds. I realized that those extra factors could be eliminated by only returning the first factor of any number that only had one value for its factors, and came up with this function:
~f = {|n|({|i|i+1}!n).collect(_.factors).reject(_==[0]).reject({|i|i[0] != i.reverse[0]}).collect({|i|i[0]}).flatten.product};
~f(10) will return 2520 and ~f(20) returns the correct answer for this problem.
I think this is my best example so far of solving one of these problems by generating a large collection and then filtering it down to find the answer.
Thursday, January 20, 2011
Problem #4 - More Sonifications
I decided for this sonification to treat the digits 0..9 something like a major-9th chord. The frequency ratio 4:5:6:7:8:9 is a major ninth in 7-limit just intonation. Since we can't multiply frequencies by 0, I add 1 to all of the digits, so our pitch ratio is 1:2:3:4:5:6:7:8:9:10 which is essentially a power chord underneath a major 9th, topped off with an extra mediant (and also the first 9 overtones of the lowest pitch). Ok.
In the first sonification I posted, there are only 5 distinct pitches, which play at different octaves - I decided to make things more interesting by transposing these pitches according to some other base pitch - this kind of parallel harmonization, called "chord planing" became kind of a cliche in early 20th century music by composers like Debussy and Ravel (in a funny way this reminds me of the time my undergrad composition teacher Rob Constable, who studied with Ligeti, played us the opening of an intensely polyrhythmic and polytonal piece like Lotano and then said that "Ligeti would sail across the Atlantic and kill me for saying this but you can really hear the Ravel in the there").
So this next version tweaks the previous version by playing a parallel harmony at some overtone based on the sum of the digits in the palindomic number being played - for example, if the notes in the melody are 975579 then the pitches are 42 *.x [9,7,5,5,7,9];
I brough down the base frequency from the key of 16hz to the key of 11000/768 hz, and then made the wait time as the iterator counted down be 64 times that frequency. (So the rhythms are still related at an octave ratio to the pitches to try to squeeze out some interesting phase-cancellation effects in how the pitches overlap).
4.0 by Backtrace
Even with all of the rhythmic complexity, it feels like the notes come in clusters - around every 11th time through the loop, for example. This got me thinking into what it is that actually makes a number palindromic, and if it has anything to do with it being the product of numbers that must have palindromic prime factors.
The next sonification I made was rhythmically simpler, but harmonically the same - as the outer iterator counts down, if any products in the inner loop of 999 numbers contain palindromes, it plays all of them at once, waits for 1 second, and then moves to the next in the list (if there are no palindromes, it gets skipped with no wait). The rhythms of the palindromic melodies are also simpler - just 1 beat per second divided by the number of digits, so a melody is always 1 second long.
4.1 by Backtrace
In the first sonification I posted, there are only 5 distinct pitches, which play at different octaves - I decided to make things more interesting by transposing these pitches according to some other base pitch - this kind of parallel harmonization, called "chord planing" became kind of a cliche in early 20th century music by composers like Debussy and Ravel (in a funny way this reminds me of the time my undergrad composition teacher Rob Constable, who studied with Ligeti, played us the opening of an intensely polyrhythmic and polytonal piece like Lotano and then said that "Ligeti would sail across the Atlantic and kill me for saying this but you can really hear the Ravel in the there").
So this next version tweaks the previous version by playing a parallel harmony at some overtone based on the sum of the digits in the palindomic number being played - for example, if the notes in the melody are 975579 then the pitches are 42 *.x [9,7,5,5,7,9];
I brough down the base frequency from the key of 16hz to the key of 11000/768 hz, and then made the wait time as the iterator counted down be 64 times that frequency. (So the rhythms are still related at an octave ratio to the pitches to try to squeeze out some interesting phase-cancellation effects in how the pitches overlap).
(
//set up a Routine that will iterate 999 * 999 times.
Routine{
999.do({|j|
var jj = 999-j;
999.do({|i|
var n =((999-j) * (999-i)).asString,c=0,s;
//test if the string representation of n is a palindrome
(n.reverse==n).if ({
//it is - let's play some notes
Routine {
/* we'll use the sum of the
digits of n as a musical parameter */
var sumDigits=0;
n.size.do({|d|
sumDigits = sumDigits + n.at(d).digit;
});
//step through each of the digits of n
n.size.do({|k|
//we'll play 2 notes for each digit
var s = [1,0], freq, octave;
//a pair of notes, in a base octave, and harmonized at an overtone
octave = [{((1/((12/11)/1000))/64) * sumDigits},{((1/((12/11)/1000))/128) * (2**sumDigits.log2.floor)}];
//iterate through [0,1] and play the 2 notes
s.collect({|t|
var ss;
ss = Synth("sine");
//the frequency is octave * kth digit of n
freq = (n.at(k).digit +1) * (octave[t]);
/* amplitude follows a pink noise curve, release gets longer as the sequence progresses,
and pan spreads more as the sequence progreses. The amplitude envelope is a sharp percussive curve. */
ss.set(\freq,freq ,\atk,0.001,\rel,((i+3).log + (j+3).log).clip(0.1,7)*2/3,
\pan,(j/1000)* (-1**i) *2/3,\amp,0.3 * (1/(freq+128)),\sus,-32,\gate,1);
});
//change the rhythm of the notes - each palindomic melody is based on a factor of the palindromic number
((1/ (jj.factors.wrapAt(i)).log2.ceil)).wait;
});
}.play;
p.add([999-j,999-i,n.asInt]);
});
((12/11) / 1000).wait;
});
});
}.play;
)
4.0 by Backtrace
Even with all of the rhythmic complexity, it feels like the notes come in clusters - around every 11th time through the loop, for example. This got me thinking into what it is that actually makes a number palindromic, and if it has anything to do with it being the product of numbers that must have palindromic prime factors.
The next sonification I made was rhythmically simpler, but harmonically the same - as the outer iterator counts down, if any products in the inner loop of 999 numbers contain palindromes, it plays all of them at once, waits for 1 second, and then moves to the next in the list (if there are no palindromes, it gets skipped with no wait). The rhythms of the palindromic melodies are also simpler - just 1 beat per second divided by the number of digits, so a melody is always 1 second long.
(
Routine{
999.do({|j|
var jj =999-j, doWait = 0;
(jj).post;
jj.factors.postln;
999.do({|i|
var n =((jj) * (999-i)).asString,c=0,s;
(n.reverse==n).if ({
n.postln;
doWait = 1;
Routine {
var sumDigits=0;
n.size.do({|d|
sumDigits = sumDigits + n.at(d).digit;
});
n.size.do({|k|
var s = [1,0], freq, octave;
octave = [{16 * sumDigits},{8* (2**sumDigits.log2.floor)}];
s.collect({|t|
var ss;
ss = Synth("sine");
freq = (n.at(k).digit +1) * (octave[t]);
ss.set(\freq,freq ,\atk,0.001,\rel,n.size,\pan,(j/1000)* (-1**i) *2/3,\amp,(jj.log10 /10) * (1/(freq+128)),\sus,-32,\gate,1);
});
(1/n.size).wait;
});
}.play;
p.add([999-j,999-i,n.asInt]);
},{
0.wait
});
});
doWait.wait;
});
}.play;
)
4.1 by Backtrace
Wednesday, January 19, 2011
Problem 4 - Palindromic Numbers
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 99.
Find the largest palindrome made from the product of two 3-digit numbers.
My first attempt at this is pretty straightforward brute-force code:
Start with 999 * 999 and iterate downward (999 * 998, 999* 997, 999 * 996 ... 999 * 1, 998 *999, 998 * 998) It's even less efficient than 999-squared since it doesn't remember that multiplication is reversible, so it would do both 999 * 998 and 998 * 999.
The algorithm coverts the digits into a string, then does a comparison w/ the reverse of the string, and if they match, adds them to a List. I only do 10,000 checks since my assumption was that the winning number, being the highest sum of digits, it would have to have a lot of 9's in it, probably beginning and ending w/ a 9.
I save both multiples that product the number, along with the product, then use sortBy to sort the resulting array by the 2nd key.
This is the first sonification I've done for Project Euler so far that I've really liked - here's the code + a recording of the first one. I'll post more details in a later post.
4.2 by Backtrace
Find the largest palindrome made from the product of two 3-digit numbers.
My first attempt at this is pretty straightforward brute-force code:
Start with 999 * 999 and iterate downward (999 * 998, 999* 997, 999 * 996 ... 999 * 1, 998 *999, 998 * 998) It's even less efficient than 999-squared since it doesn't remember that multiplication is reversible, so it would do both 999 * 998 and 998 * 999.
The algorithm coverts the digits into a string, then does a comparison w/ the reverse of the string, and if they match, adds them to a List. I only do 10,000 checks since my assumption was that the winning number, being the highest sum of digits, it would have to have a lot of 9's in it, probably beginning and ending w/ a 9.
(
var p=(List());
100.do({|j|
100.do({|i|
var n =((999-j) * (999-i)).asString,c=0,s;
(n.reverse==n).if ({
p.add([999-j,999-i,n.asInt]);
});
});
});
p.sortBy(2).reverse;
)
I save both multiples that product the number, along with the product, then use sortBy to sort the resulting array by the 2nd key.
This is the first sonification I've done for Project Euler so far that I've really liked - here's the code + a recording of the first one. I'll post more details in a later post.
(
Routine{
500.do({|j|
j.postln;
999.do({|i|
var n =((999-j) * (999-i)).asString,c=0,s;
(n.reverse==n).if ({
Routine {
n.size.do({|k|
var s,freq;
s = Synth("sine");
freq = (n.at(k).digit +1) *(4096 / (2**i.log.ceil));
s.set(\freq,freq ,\atk,0.001,\rel,((i+3).log + (j+3).log),\pan,(j/1000)* (-1**i) *2/3,\amp,0.3 * (1/(freq+128)),\sus,-32,\gate,1);
(1/ 8).wait;
});
}.play;
});
(1/1024).wait;
});
});
}.play;
)
4.2 by Backtrace
Problem 3 -The Largest Prime Factor of a Composite Number
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
This one had me stumped for a while but mostly because of some type-casting issues with Supercolllider. sclang comes with an Integer.factors method that works great for 32-bit integers, up to a point.
So while
correctly returns
If you ask
it curiously returns
which is probably not coincidentally the Mersenne Prime (2**31)-1 and probably has something to do with sclang overflowing the bit register for a 32-bit integer.
So after a few hrs of fighting w/ sclangs weird type-casting issues I came up with the following recursive function:
//function to find distinct factors of num, not complete factorization
//factors is an empty Array that we can add to
It's pretty fast, though not tail-recursive (and the 2 function calls seems like bad form. There's probably also a better way to do recursive functions than with the pseduo-global ~variable stuff). It doesn't do a complete factorization, just finds the unique factors of the number. The trick to gaining speed is that once you find a factor of composite num, you divide by the factor so that the number that you continue to factor is a little smaller, and even if you're just testing every integer sequentially for divisibility, your factor catches up with num pretty quickly. It probably helps that in this case, the test number has no repeating factors and most of the factors are pretty large.
What is the largest prime factor of the number 600851475143 ?
This one had me stumped for a while but mostly because of some type-casting issues with Supercolllider. sclang comes with an Integer.factors method that works great for 32-bit integers, up to a point.
So while
13195.factors
correctly returns
[5, 7, 13, 29]
If you ask
600851475143.factors
it curiously returns
[ 2147483647 ]
which is probably not coincidentally the Mersenne Prime (2**31)-1 and probably has something to do with sclang overflowing the bit register for a 32-bit integer.
So after a few hrs of fighting w/ sclangs weird type-casting issues I came up with the following recursive function:
//function to find distinct factors of num, not complete factorization
//factors is an empty Array that we can add to
~f = {|num, f=2,factors=([])|
/*
break if we've already gotten to the square root of num
the final value of num is the last factor
because it was the last even divisor of num/f
*/
(f>num.sqrt).if({factors.add(num);factors.postln;^factors});
((num%f)==0).and(factors.detect({|c|f%c==0})==nil).if({
~f.(num/f, f+1,factors.add(f));
},{
~f.(num, f+1,factors);
});
};
//number in question has to be cast as a Float for 64-bit precision
~f.(600851475143.0);
It's pretty fast, though not tail-recursive (and the 2 function calls seems like bad form. There's probably also a better way to do recursive functions than with the pseduo-global ~variable stuff). It doesn't do a complete factorization, just finds the unique factors of the number. The trick to gaining speed is that once you find a factor of composite num, you divide by the factor so that the number that you continue to factor is a little smaller, and even if you're just testing every integer sequentially for divisibility, your factor catches up with num pretty quickly. It probably helps that in this case, the test number has no repeating factors and most of the factors are pretty large.
Problem 2 - the even Fibonacci Numbers
Problem 2:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
I solved this using a basic recursive function - it's a fairly brute-force solution.
Here's the code for a sonification that generates a spooky little overture:
It uses the sine tone generator from Problem 1
One thing that the sonification of this solution makes really apparent is how the ratio of adjacent members of the Fibonacci series approaches the same value. The function pre-generates all of the notes before they are played, each one waiting it s turn to play as determined by the value of c.log.wait in the beginning of the Routine. I usually like a lot of symmetry in my music, which means I usually do things in integers or ratios of integers yet in this sequence, the notes all sound at very regular intervals even though their start times are not based on an integer. The pitch is determined by logarithm of the value of the variable e, the running sum of even numbered terms of the sequence - which always increases every 3rd term.
For a relatively small number of pitches this generates a pretty rich sound - there's more for me to explore here.
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
I solved this using a basic recursive function - it's a fairly brute-force solution.
~f = {|a=1,b=2,e=2|
var c = a+b;
(c<4000000).if ({
//add any even term to e
e = e+(c%2==0).if({c},{0});
~f.(b,c,e);
}, {
e;
});
};
~f.();
Here's the code for a sonification that generates a spooky little overture:
It uses the sine tone generator from Problem 1
~f = {|a=1,b=2,e=0|
var c = a+b;
(c<4000000).if ({
//add any even term to e
e = e+ (c%2==0).if({c},{0});
e.log.postln;
(e>0).if ({
Routine {
var s1,s2;
//put the wait at the front - rhythms accumulate as a duration of silence before the note
c.log.wait;
s1 = Synth("sine");
s1.set(\freq,64 * e.log,\atk,0.01,\rel,64 - c.log,\amp,0.1,\gate,1);
}.play;
});
~f.(b,c,e);
}, {
e;
});
};
~f.();
One thing that the sonification of this solution makes really apparent is how the ratio of adjacent members of the Fibonacci series approaches the same value. The function pre-generates all of the notes before they are played, each one waiting it s turn to play as determined by the value of c.log.wait in the beginning of the Routine. I usually like a lot of symmetry in my music, which means I usually do things in integers or ratios of integers yet in this sequence, the notes all sound at very regular intervals even though their start times are not based on an integer. The pitch is determined by logarithm of the value of the variable e, the running sum of even numbered terms of the sequence - which always increases every 3rd term.
For a relatively small number of pitches this generates a pretty rich sound - there's more for me to explore here.
Tuesday, January 18, 2011
Some Sonifications of Problem 1.
First - boot the server and create a really basic sine wave synth:
z = Server.internal;
SynthDef("sine",{ |freq=100, atk=1, rel=1,sus=1, amp=0.005,pan =0,gate=0 |
Out.ar(0,Mix.ar(Pan2.ar(SinOsc.ar( freq * [1,1],0,amp),pan)) * EnvGen.ar(Env.perc(atk,rel,sus),gate, doneAction:2) );
}).load(z);
The first sonification is really basic - just run through the sequence of frequencies returned by the series and if the frequency f is greater than 0, then play the note at that pitch (times 8 so that the lowest notes are audible). The amplitude is scaled back over a pink noise curve (1/f) so that the low freqs are nice and full and the high freqs are not piercing.
You can hear the 3/5 rhythm really clearly in this example:
1.0 by Backtrace
In the second sonification, I leave the notes on a long release so that many of them play simultaneously, and I lock the pitches to a single octave. As the notes build up, you can hear the same 3/5 rhythm, only this time as a differnce tone between frequencies.
1.1 by Backtrace
First - boot the server and create a really basic sine wave synth:
z = Server.internal;
SynthDef("sine",{ |freq=100, atk=1, rel=1,sus=1, amp=0.005,pan =0,gate=0 |
Out.ar(0,Mix.ar(Pan2.ar(SinOsc.ar( freq * [1,1],0,amp),pan)) * EnvGen.ar(Env.perc(atk,rel,sus),gate, doneAction:2) );
}).load(z);
The first sonification is really basic - just run through the sequence of frequencies returned by the series and if the frequency f is greater than 0, then play the note at that pitch (times 8 so that the lowest notes are audible). The amplitude is scaled back over a pink noise curve (1/f) so that the low freqs are nice and full and the high freqs are not piercing.
(
Routine {
//generate our numbers
({|i|i}!1000).collect({|i|((i%3==0).or(i%5==0)).if({i},{0})}).collect({|f,i|
var s;
f.postln;
(f != 0).if({
//allocate a sinewave synth
s = Synth("sine");
//frequency
s.set(\freq,f*8);
//scale back the amplitude
s.set(\amp,0.001,\atk,0.001,\rel, 1/3 ,\sus,-32,\gate,1, \amp, 1/(f+1) * 1/16);
},{});
(1/12).wait;
}).sum;
}.play;
)
You can hear the 3/5 rhythm really clearly in this example:
1.0 by Backtrace
In the second sonification, I leave the notes on a long release so that many of them play simultaneously, and I lock the pitches to a single octave. As the notes build up, you can hear the same 3/5 rhythm, only this time as a differnce tone between frequencies.
(
Routine {
({|i|i}!1000).collect({|i|((i%3==0).or(i%5==0)).if({i},{0})}).collect({|f,i|
var s, freq;
freq = f*(512/ (2**f.log2.ceil));
f.postln;
(f != 0).if({
s = Synth("sine");
s.set(\freq,freq );
s.set(\amp,0.001,\atk,0.001,\rel,(1000-i)/8,\sus,-32,\gate,1, \amp,(1/ freq )*1/12);
},{});
(1/12).wait;
}).sum;
}.play;
)
1.1 by Backtrace
Problem 1 - Multiples of 3 and 5
Problem 1:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
This is essentially FizzBuzz revisited - in Supercollider you can find the solution with one line of code:
({|i|i}!1000).collect({|i|((i%3==0).or(i%5==0)).if({i},{0})}).sum;
({|i|i}!1000)
Generates the integers 0 to 999
.collect({|i|((i%3==0).or(i%5==0)).if({i},{0})})
.collect is an iterator method that maps an anonymous function over a Collection and returns a new collection - in this case we test to see if i modulo 3 or 5 is 0 - if it is, we return the value of i. Otherwise, we return o (which won't be accounted for when we sum to get the final answer).
In Supercollider, any expression is a Boolean so you can write a conditional as (expression).if({true},{false}) - where if is a method that takes 2 functions as arguments, for the true and false conditions.
.sum
Finally, we call the .sum method on the collection to return the sum of the collection's elements.
In the next post I'll post some sonifications of this algorithm.
Hi.
This will be a blog about Supercollider and music and using Supercollider to work my way through the problems on Project Euler.
Subscribe to:
Posts (Atom)