naivesound/glitch

feature request: custom phase-based oscillators

tsulej opened this issue · 4 comments

  • implement square, triange and saw functions, similar to s() function (where you can insert 't' related values instead of frequency)
  • implement pwm(t1,t2) based on 't' ( if(t1&0xff < t2&0xff) return 0 else 255; or something like this)

I haven't implemented square, triangle and sawtooth because they can be easily implemented manually:

  • Sawtooth: t*f/31.25, (just t plays triangular wave with frequency of 31.25Hz)
  • Square: t*(f/31.25)%256)>>7)<<7
  • Triangular: ((trg=((t*f/31.25)%256)-127))<0&&trg||-trg, this one is a bit more complex because it requires getting the absolute value of a number and making a conditional expression.
  • PWM: also, using conditional - ((t*f/31.25)&255)>(255*width)&&255||0, here width is the width the pulse.

s() is the only oscillator that can't be written as a simple math expression, so it's in the standard library.

Obviously, these are not easy to type (or to remember). However, I think the key feature of Glitch (comparing to more powerful tools like ChucK, SuperCollider, etc) is that you can learn all ~20 functions and their parameters, also you can understand how every function works.

I don't think making these oscillator functions part of the standard library works well for Glitch, manually implementing oscillators will end up with clicks when frequency changes, and there will be confusion between phase-based and frequency-based oscillators. What I think of is the ability to define custom user functions/macros, so that you could write once macro(pwm, (phase, width), (phase&255)>(255*width)&&255||0) and later use pwm(a, b). Then you oscillators can be defined as macros/functions and can be reused in code many times.

Thank you for explanation. Macros would be nice.

@tsulej Glitch is based on my expression evaluation engine, and I've just added macro support there - zserge/expr@215bc8b

Now in Glitch you can define macros like this:

$(add, $1 + $2),
1+add(5, 6), // returns 12

In fact the following macro is literally expanded to:

1+($1=5, $2=6, $1+$2)

Positional arguments are not cleared because we don't know the "arity" of the macro beforehand, and we don't want to waste cycles or erasing non-existent variables. So if you do add(5,6) and then add(3) you will have $2 = 6 and thus the result will be 9.

Macros may have an empty body, so $(zero), zero() returns 0.

Macros may have a multi-expression body, e.g. $(hypot, a=$1*$1, b=$2*$2, (a+b)**0.5). Here a and b are global variables, because all the variables in Glitch are global.

Macros may be overloaded, e.g. $(foo, 1), x=foo(), $(foo, 2), y=foo() will end up with x=1 and y=2 but I don't see a practical use case for that at the moment.

Macros can not be recursive. I don't know if it's a feature or not, but the way how expression engine works is that it resolved macro body before it defines a macro symbol. Also we don't have a "stack" for local variables, so recursion would be really problematic. Not to mention that it can potentially hang up your whole song.

If you build Glitch from the head of the master branch you should be able to use macros already.

Nice, thx, I'll try it soon