//function definitions start with no indentation. there are three reserved name, that correspond //to the three paths the program takes by default. they are audio(), scene(), and post(). //press escape to toggle the user interface on and off //press shift+i to enter edit mode, escape to exit //this is the audio thread audio() //these note functions convert from strings of keyboard characters to midi indices, and //then convert from those indices to actual frequency values. notes = notes("q,w,e,r") frequencies = note_hz(notes) //we can read midi devices to see what state the notes are in. if there is no device name //provided, then the script will be checking the computer keyboard. the key mappings are //defined in settings.txt //use command+m to enter --midi-- mode where keyboard presses register as notes. this //note_signals() call returns a buffer of values corresponding to the midi-note value from //the requested device (but normalised to the range [0.0, 1.0] instead of [0, 127]) note_signals = note_signals(notes) //we can take those note value arrays and feed them into an adsr. if the variable //viewer mode is active (command+v) and midi mode is active (command+c), if the //adsr function or the amplidues variable is highlighted you should see peaks appear //whenever the corresponding keys are pressed (q, w, e, r) amplitudes = adsr(40.0ms, 80.0ms, 0.7, 500.0ms, note_signals) //we can then use the adsr outputs as multipliers against a series of oscillators. //using the same debugging tools you should see waveforms appear here output = osc("sin", frequencies, amplitudes) //since there were four notes being fed into the osc() call, there will be four //sound arrays being returned. we can collapse them into a single buffer with sum() output = sum(output) //then we can assign that value to both output channels channels[0] = output channels[1] = output //before we leave the audio thread though, we can write the amplitudes to a global //shared space so that they can be read by the graphics thread export(amplitudes) return channels //this is the graphics thread scene() //so to start with, we'll pull in those amplitude values and use them to position and rotate things amplitudes = import("amplitudes") //there are a bunch of transformation functions in the api, we'll use them to rotate the scene //there are four amplitude arrays, and we can index them with the [] operator. the second [] is used //to index into the actual array data. each array is 1024 elements long, but any number can be //provided and it will wrap to a valid index. //the $dt symbol here is an inbuilt variable representing the time between frames. its usage here //is to provide a constantly increasing multiplier to the rotation functions. xform = matrix() xform = rotate_x(xform, acc(amplitudes[0][0] * 360.0deg * $dt)) xform = rotate_y(xform, acc(amplitudes[1][0] * 360.0deg * $dt)) xform = rotate_z(xform, acc(amplitudes[2][0] * 360.0deg * $dt)) //create a ring of rings here ring_count = amplitudes[3][0]*9.0 //loop indices are inclusive, so this will loop 12 times loop j from 1 to 12 //creating a ring in the x/z plane xform = rotate_y(xform, 30.0deg) //offset all the shapes a bit in time so they arent identical shape_rotation = acc($dt, j * 0.1) * 36.0deg shape_blend = lfo("sin", 1.0s, 1.0, j * 0.1) ring_xform = xform ring_xform = translate_x(ring_xform, 8.0) //loop values can themselves be expressions, in this case we read the ring count variable loop i from 0 to ring_count //this should form a ring in the x/y plane shape_xform = rotate_z(ring_xform, i * 36.0deg) shape_xform = translate_x(shape_xform, -3.0) shape_xform = rotate_y(shape_xform, shape_rotation) //this function call isnt an inbuilt function, scripts can define there own functions draw_test_shape(shape_xform, shape_blend) //this is a user function draw_test_shape(xform, blend) //since all shapes in this program are distance fields, its trivial to blend between them. shape = mix(cube(), sphere(), blend) draw(shape, xform) //this represents a 2d post effects chain post() //you can import stuff from the other threads amp = import("amplitudes") //shader() will attempt to parse the function it refers to into a gpu shader //the $scene_texture variable represents the output of the scene() call above return shader("red_shift", $scene_texture, 0.1 * amp[0][0], 0.1 * amp[3][0]) red_shift(source_texture, offset_x, offset_y) //the $u, $v and $uv params are the current pixel in [0,1] range and are being used to //sample the scene_texture and shift the red channel a bit. pretty simple shifted = sample(source_texture, $u + offset_x, $v + offset_y) normal = sample(source_texture, $uv) return colour(shifted[0], normal[1], normal[2], 1.0)