With this you could for example do:
– setting up
if xinc == 1 then volume_osc = osc.create(sine, 16, 0, 255) end
– using
xline.note_columns[1].volume_value = volume_osc:value()
Update: I think it’s almost done here. Might need some bug fixing and tidying things up. + Documentation (it’s quite flexible)
Click to view contents
local xinc -- for testing only
local osc = {
func = {
sine = {
user_abr = { "sine", "sin" },
calc = function(phase) return (math.sin(math.rad(360 * phase)) / 2 + 0.5) end
},
square = {
user_abr = { "square", "pulse", "sqr", "pul" },
calc = function(phase) if (phase < 0.5) then return 1 else return 0 end end
},
random = {
user_abr = { "random", "rnd", "noise" },
calc = function(phase) return math.random() end
},
triangle = {
user_abr = { "tri", "triangle" },
calc = function(phase) return math.abs(((phase+0.75) % 1) - 0.5) * 2 end
},
saw = {
user_abr = { "saw", "ramp", "sawtooth" },
calc = function(phase) return phase end
},
min = {
user_abr = { "min" },
calc = function() return 0 end
},
max = {
user_abr = { "max" },
calc = function() return 1 end
}
-- make a step sequencer here? or at least a toggler? maybe implement euclidean pulses?
},
}
osc.get_index = function(user_type)
for i_osc, osc in pairs(osc.func) do
for i_abr, abr in ipairs(osc.user_abr) do
if (abr == user_type) then return i_osc end
end
end
return "min"
end
function osc:validate()
return (self.type and self.freq and tonumber(self.freq) and self.min_value and tonumber(self.min_value)
and self.max_value and tonumber(self.max_value))
end
function osc:runtime()
return xinc - self.start_xinc
end
function osc:reset(phase)
self.start_xinc, self.phase = xinc, phase or self.phase
end
function osc:value(scale_min, scale_max, resolution)
if self then
local phase = self:runtime() % self.freq / self.freq
local min, max, res = scale_min or self.min_value, scale_max or self.max_value, resolution or self.resolution
local value = osc.func[osc.get_index(self.type)].calc(phase) * (max-min) + min -- bad practice referencing osc template instead of using metatables? (lack of selfness?)
return math.max(min, math.min(max, math.floor(value/res+0.500001) * res)) -- not sure why. bug in the universe/lua?
end
end
function osc.create(type, freq, min_value, max_value, phase, resolution)
local object
if (type and freq) then
object = {
type = osc.get_index(type),
freq = freq,
min_value = min_value or 0,
max_value = max_value or 255,
resolution = resolution or 1,
phase = phase or 0,
start_xinc = xinc or 1
}
for k, val in pairs(osc) do
if not (k == "func") or (k == "get_index") then -- again, instead of using metatables
object[k] = val
end
end
end
if object:validate() then return object else return nil end
end
-- testing below
for i = 1, 64 do xinc = i
if xinc == 1 then my_osc = osc.create("tri", 32) end
print(my_osc:value())
end
EDIT:
Not sure danoise will find it useful enough, but I for sure will use it anyway
Another (totally different) idea I got is a very small and simple helper function that could be available in xStream that I would find quite useful and that would possibly simplify some aspects of xStream coding even more. The following:
if on_update(xline.note_columns[1].note_value) then
xline.note_columns[1].effect_value = 10
end
Super simple syntax imo.
on_update() will return the value only when changed, otherwise it will return nil. The idea is also that the on_update() function would not only accept variable data, but even a whole object (excl methods).
It should be pretty easy to code. I imagine that the first time on_update is being ran, it will register the reference in an internal table (variables would be normal values and object/tables would be hash values). Registering/checking objects and tables would require some recursive scanning, sure. On each subsequent update it will compare the current value to what is cached. Not sure how to get the “uniqueness” of the parameter passed into the function, but it should be possible? Or maybe there is a better way already available for similar tasks?
EDIT 2: An alternative would be to hook up a document to xline properties, and make use of document observables? Observing xline is quite enough, I think, maybe allowing for something like this if possible:
if xline.note_columns[1].note_value:is_updated() == true . Or is something similar already possible?
IDEA 3:
Another simplifying idea, or at least one that adds flexibility, would be to provide not only xline but also something like xline(offset).
xline(16).note_columns[1].effect_value = 10
This is sometimes more convenient than 1) caching values for later use or 2) calculating the correct value with a possibly more intricate function (offsetting), 3) dealing with xpos.
A very simple practical example would be to generate a tracked delay in a second column. In this case xline(steps) would imo make more sense, or at least be easier, than storing data in a table for % search or fetching data from previous lines.
Just an idea for streamlining/simplifying model coding and improving readability, making new users feel even more welcome