Islands of Consciousness is damn cool. Random composition of random images feeding a random audio composition thingie, creating something pretty strange.
Sunday, December 31, 2006
I'm currently working on stroking, and trying to come up with a decent algorithm for that stuff. The big problem here isn't really how to code stuff, but rather what things should look like.
Anyway, in order to visualize some problems for myself, I draw a stroke around a stroke. Then I got the idea that if I modify it a little bit, line segments will become visible, and I kinda liked what the result looked like, as it nicely illustrates the bezier subdivision done.
So in the picture below, there is a cubic bezier, which is subdivided into line segments. Then each of these line segments is made 15 pixels wide, and filled with white. Finally those are stroked with a black outline, which makes each individual segment clearly visible.
Wednesday, December 27, 2006
So far the hardest problem to solve turned out to be how to parametrise arcs. There are three obvious parameters, namely a control triangle of the rational quadratic bezier that is the arc. But how to get a weight for the middle point?
After playing with different parametrisation, I currently have one which gives the middle point the weight of cos(angle/2) where angle is an extra parameter to the function. It happens that when the legs of the control triangle are of equal length, and the angle between them is supplied as angle, one gets an arc of a circle. When the legs aren't of equal length, or the parameter isn't the angle between the legs, one gets an ellipse instead. For zero angle one gets normal non-rational bezier, and for angle pi one gets a straight line. Larger angles give the other side of the ellipse or circle.
Bonus benefit is that when the curve is subdivided in the middle, one doesn't need to track weights for the control points. Just using half the angle will give the proper midpoint weight.
For the typical uses of arcs, there's an extra function which simply makes the guess that the angle between the legs will probably be fine. For stuff like corners of polyline strokes or rectangles this seems to be a reasonable value.
And naturally we have a sample picture:
In the picture you see 3 elliptic arcs, one line and one cubic bezier.
Sunday, December 24, 2006
Saturday, December 23, 2006
Haven't got much of anything interesting added to the synthesizer thingie during the last few days. Instead, I've actually played around a bit in two dimensions. For a long time I've wanted a simple vector graphics library. A few days ago I started playing with the math. The result is rather boring system based on a couple of simple principles:
- There is canvas, masks, and paint; paint is applied to canvas through a mask.
- Geometric primitives (= line paths) work with masks.
- Colors, gradients, bitmaps, whatever, work with paint.
- To draw a white triangle, cut a triangle shaped hole in a mask, apply white paint.
Now, at this point there isn't even a datatype for a path, so the individual lines in the mask must be drawn separately, but fixing that isn't a huge amount of work (actually probably less than writing this blog post). And I'd expect stuff like beziergons to work before christmas.
Now, what is nice about the design, is that as you see it antializes rather beautifully. It actually draws what you'd get by doing 16x16 naive supersampling and averaging down. Ofcourse all the supersampling is internal to the line drawing function. I'll probably make it 256x256 when I have the extra minute to tweak the algorithm a bit more.
The whole thing is a very basic scanline rasterizer, so the trick really is in the scanlines. Now a scanline rasterizer works by incrementing a counter every time it crosses a line going up, and decrements a counter every time it crosses a line going down. Then you typically fill when you've got either non-zero or odd number in your counter, depending a bit on what you want.
The antialising then is actually quite easy, just a question of putting fractional values in the scanline buffer, and drawing an anti-aliased line. The only important thing then is that the line drawing function makes sure that when one starts from left side of the line, and sums all the pixel values until one has crossed the whole line, the result is exactly one. In a sense, what is being drawn is the finite difference of the edge of an antialised half-plane. And I actually got the idea originally from anti-aliasing audio with BLEPs. Don't ask.
And the best thing? Since integration (and differentiation) are linear operations, one can draw each line independently, and just integrate the scanlines at the end. There aren't really any special cases whatsoever.
Ps. I'm not sure if the above makes any sense. I might consider a better explanation some other day, but I now have to add those beziergons. :)
Monday, December 18, 2006
Ok, here's a really quick sample clip of stuff my synthesizer project currently does.
One sound, live playing, a touch of delay but no other effects.
Oh and the player (which hopefully works) is XSPF Web Music Player button version.
Sunday, December 17, 2006
Saturday, December 16, 2006
Ok, so I posted a thread about this in KVR, and obvious I'm not the only one to have thought about it (which was to be expected ofcourse), but I'm still posting this here for future reference, since I don't have anything else to post about today.
In synthesizers, being able to convert from pitch to frequency is necessary. In equal tempered scale this involves the relation f=b2p/12, where f is the frequency, b is the tuning base, and p is the pitch in semitones relative to the base tuning. This is trivially solved in C++ by f*pow(2.0,p/12). Trouble is, pow() takes an awful lot of time, so it's not really realistic if you want to do it on per-sample, per-voice basis.
One would expect to find some nice code that solves this problem reasonably accurate for the purposes of synthesizers, yet still reasonably fast. I've tried doing this a few times, but always failed. By reasonably accurate I mean something that gives resolution of about 1/100 semitones over the range of hearing (around 10 octaves). Most solutions I've seen are either
- wildly inaccurate or only accurate near 0
- only marginally faster (often slower than) than what gcc gives for pow
Now, looking at the facts, the accuracy requirements are measured in fractions of semitones. This suggests rounding before exponentiation should not cause problems, if the exponential itself can be calculated accurately. So how much accuracy does one need?
First of all, one only needs to consider one side, since 2-x = 1/2x. Let's say we say we want to go up and down 10 octaves, we need at least 4 bits for the integer part. As for the fractional part, 1 octave = 12 semitones, and 1 semitones = 100 cents. Hence there are 1200 cents to an octave. For 12 bits one gets 4096 tunings between octave. That's a total of 16 bits, and more than enough.
Full lookup table for 16 bits would ofcourse take 65536 entries, which for 32-bit floats takes a 256kB. Not huge these days, but exponentials have the nice property that a(x+y)=axay. And this ofcourse means one can do a separate table for the upper 8 bits and the lower 8 bits. That's 256 entries per table, both tables together taking 2kB of memory.
So the algorithm for pow2(x) with two tables of 8 bits each then goes:
- if x<0 recurse for 1/2-x
- multiply x by 212
- round/floor/whatever x into an integer (in C++ cast works fine)
- do table lookups with 8 lowest, and 8 next bits, and multiply them together
- return result
I just love some of these Firefox preference bugs. There's a setting in firefox called accessibility.typeaheadfind.timeoutenable. When set to false, it's purpose is to disable hiding the typeahead find bar. This supposedly stopped working properly after version 0.10 or something like that.
Now, there's another setting called accessibility.typeahead.timeout. You'd assume that if you set this to some large value, you'd essentially have your bar stay visible? Nope, doesn't seem to work. What seems to work though, is setting the a.t.timeout to value 0.
Now, the funny thing is I can easily find several sources that tell me that a.t.timeoutenable is broken, but don't see the workaround mentioned anywhere. I remember wondering (and solving) this for multiple times now, so I'll put it here for future reference.
I'm still using 1.5 series Firefox at home, but if I'm not mistaken the situation is identical in 2.0 series. At least I remember fighting with it after oh-so-wonderful centrally-administered computer-management thingie decided to give me 2.0 on another computer.
edit: I guess it's worth adding that yes, there's an open bug since years now, still in state New, so probably no use trying to report this.
Friday, December 15, 2006
Ok, I can't resist temptation for another meta-post.
I've wasted the last five or so hours with playing around with this blog thingie, trying to get it to look more like mine, and I have to say that while there's still stuff I want to do later, this thingie is actually quite easy to customize.
Still searching for some nice solution to drop audioclips into my blog posts. I'm gonna need that kind of thing in the near future.
So anybody reading this is welcome to my new blog. The subject of this blog will be synthesized and processed sound. The name is partly a joke, but highlights the fact that I don't like overly clean sounds, even if I'm definitely not a noise lover either.
As for the blog I'll be at least exploring some of the sounds and processing tricks I encounter when I play with things. Some of the content will probably be in form of tutorials. Some will be naive exploration of stuff everybody else already knew. Hopefully some ideas will also be novel. At times I'm sure to comment on things having to nothing to do with music. And some posts will almost surely involve some code, for I am a programmer after all. Finally, I'll probably link to some of my music from time to time.
There's some other stuff too, but it's really a bit too early to go into details. Time will show what will come out of this.
As some people reading this might know, this ain't my first blog. The previous blog was called "Beyond the Parenthesis." The last post dates back around a year now. The reasons for it's decay are many, but probably the biggest issue was that the time taken to maintain a custom built publishing kludge was simply too much. My hobbies where also starting to drift away from it's focus a bit too much.
As for comments, feel free to give lots of them, but please keep discussions civil and at least somewhat in topic. :)