Subscribe to the Atom feed.
Posted February 16, 2010
Happy Tuesday.
clock = Clock(120)
scale = { 0, 3, 7, 10 }
base = 40
function beep()
local o = PulseOsc(440)
c(o, speaker)
while true do
o.freq = mtof(base + scale[random(1, #scale)])
o.width = rand()
yield(0.5, clock)
end
end
fork(beep)
fork(beep)
fork(beep)
fork(beep)
fork(beep)
fork(beep)
function beep2()
local o = PulseOsc(440)
local g = Gain()
c(o, g)
g.gain = 0
local m = SinOsc(0.03)
m.phase = 0.7
c(m, blackhole)
while true do
fork(function()
c(g, dac)
o.freq = mtof(base + 24 + scale[#scale])
o.width = rand()
yield(0.25, clock)
d(g, dac)
end)
yield(0.5, clock)
g.gain = m.last * 0.5 + 0.5
end
end
fork(beep2)
function tick(gg)
local n = Noise(0.1)
local g = Gain(gg)
c(n, g, dac)
yield(10 * ms)
d(g, dac)
end
fork(function()
while true do
fork(tick, 1)
yield(1, clock)
fork(tick, .5)
yield(1, clock)
fork(tick, .5)
yield(1, clock)
fork(tick, .5)
yield(1, clock)
end
end)
while yield(16, clock) do
base = random(base - 2, base + 3)
clock.bpm = clock.bpm * (1 + rand() * 0.02)
end
Posted February 15, 2010
I finally freakin' did it. There exist commands by the names gcc and g++ on both OS X and Linux, but they are by no means the same.
Anyway, if you change the PLATFORM=OSX of the Makefile to say PLATFORM=LINUX, then it ought to build and work, assuming you have all the dependencies. Tested on Ubuntu and Gentoo with the latest from the repository. The tutorial has been updated.
I will probably begin to supply occasional binaries now. ckv's feeling like a real project these days.
Posted February 15, 2010
A beat isn't really a unit of time. Although beat = second / 2 is good enough for lots of electronic music, for any music where the tempo will change over time, yield(beat) is insufficient because threads will get out of sync if beat ever changes unless great care is taken.
This was the motivation for introducing Clock objects to ckv. Clocks allow you to use musically meaningful units when yielding time, and won't let threads get out of sync when you change a beat's duration. Here's an example:
clock = Clock(300)
fork(function()
while sync(1, clock) do
local n = Noise()
c(n, speaker)
yield(100 * ms)
disconnect(n, speaker)
end
end)
clock_mod = SinOsc(0.1)
c(clock_mod, blackhole)
while true do
clock.bpm = clock_mod.last * 90 + 100
print("bpm:", clock.bpm)
yield(100 * ms)
end
now, yield, and sync have been updated to work with clocks by taking one as the last argument.
… which you would know if you had checked out the just-updated Language Reference page (formerly "ugen Reference"), which now documents ugens, events, clocks, forking, the global namespace, and more. Check it out, it's so long.
Posted February 8, 2010
I've never been much of a live-coder, but I've been doing a bit of it in ChucK recently to avoid more staggeringly inefficient ckv syntax like connect(…) (now aliased to c(…)). I've recorded two of my attempts on Vimeo – here and here – but haven't perfected my recording methods or musical sense, so it's probably hard work to get through them both. ☺
Anyway, a trick I learned from one of kijjaz' videos was to prepend scripts with second - (now % second) => now so that whenever you add them to the VM, they'll wait for the next whole second before they begin. This quantization gives you a little extra flexibility in when you press the hotkey for adding a shred.
So now that's built into ckv as sync(second), or sync(minute / 140) or whatever your sync period is.
Posted January 17, 2010
To reduce overhead and boilerplate in the ugens' tick functions, I've just committed code that makes some important changes:
So while I previously recommended writing a ugen like this:
Noise = {
new = function(class, gain)
return UGen.initialize_io({
gain = gain or 1.0,
last_value = 0.0,
tick = function(self)
if not(now() == self.last_tick) then
self.last_value = (math.random() * 2.0 - 1.0) * self.gain;
self.last_tick = now();
end
return self.last_value
end
})
end
}
You can now slim that down to something like this:
Noise = {
new = function(class, gain)
return {
gain = gain or 1.0,
tick = function(self)
self.last = (math.random() * 2.0 - 1.0) * self.gain;
end
}
end
}
The changes to how ugens are ticked are not trivial, so please let me know about any bugs I have introduced. :)
The way I ensure that ugens are ticked only once is by sorting the ugen graph by longest path length from speaker & blackhole, and caching the resulting queue until the next time a connection is made or broken. All in all I see a ~20% speed improvement (using ex01.ckv as a benchmark), which I expect to be greater on more complicated graphs, and less on graphs that change frequently.
Posted January 10, 2010
The source is in a crazy state with a bunch of stuff not checked in, otherwise this ugen would probably be in by now. It soft-clips, then caps the first derivative for some interesting distortion. It's a port of some ChucK I wrote a while back; it's great to be able to write UGens like this. :)
Distort = {
new = function(class, pregain)
return UGen.initialize_io({
last_value = 0.0,
pregain = pregain or 100.0,
max_slew = 0.5,
tick = function(self)
if not(now() == self.last_tick) then
local i = UGen.sum_inputs(self) * self.pregain;
local out = 0.0;
-- make roundy
if i >= 1 then
out = 2.0 / 3.0
elseif i <= -1 then
out = -2.0 / 3.0
else
out = i - (i * i * i) / 3.0
end
-- max slew
if math.abs(out - self.last_value * 2.0 / 3.0) > self.max_slew then
if out < self.last_value * 2.0 / 3.0 then
out = self.last_value * 2.0 / 3.0 - self.max_slew
else
out = self.last_value * 2.0 / 3.0 + self.max_slew
end
end
self.last_value = out * 3.0 / 2.0;
self.last_tick = now();
end
return self.last_value
end
})
end
}
Posted January 6, 2010
In-between airport concourse sprints today, I wrote up a reference page for unit generators.
I also added an example to the tutorial for SndIn.
I'd say more but… exhausted. Good night. ;p
Posted January 5, 2010
I spent a few hours throwing together some introductory material on ckv. The installation section assumes that you're extremely comfortable on the command-line (something I soon hope to remedy), but the rest should be pretty straight-forward without being simplistic. I want anybody to be able to read the tutorial and get going.
I'm going to use the tutorial as a walk-through for every single feature of ckv (a choose-your-own-adventure examples directory) in addition to traditional language and unit generator reference pages.
Posted January 4, 2010
I created a Delay unit generator in Lua to see how much we can expect where large arrays of samples are concerned. At 44100 sample rate, this doesn't start dropping samples for me until the buffer's at least a couple hundred milliseconds long. A second-long delay line clicks every second, but it's definitely not as bad as I was expecting.
Delay = {
new = function(class, delay)
return UGen.initialize_io({
delay = delay or second,
last_value = 0.0,
last_values = {},
tick = function(self)
if not(now() == self.last_tick) then
local vals = self.last_values;
local offset = math.ceil(self.delay); -- no sub-samples for you!
vals[#vals + 1] = UGen.sum_inputs(self);
self.last_value = vals[#vals - offset] or 0.0;
if #vals > offset * 2 then
for i = 1, offset, 1 do
vals[i] = vals[#vals - offset + i]
end
for i = offset + 1, #vals, 1 do
vals[i] = nil
end
end
self.last_tick = now();
end
return self.last_value
end
})
end
}