@ffxAs I understand it, Renoise gives priority to sound, rather than graphics. This means that the song should sound synchronized at all times, and it is the Renoise GUI that is updated in case it runs slower. That is why, in low-powered computers, the GUI may blink.
Then, the beginning of each pattern in the sequence depends on the sound of the song, not the graphics. The idea is to synchronize the metronome as best as possible to the sound of the song, and the best starting point is the beginning of each pattern in the sequence. So, if there is an error, it does not accumulate, you only reproduce a single error, and that perhaps translates into a metronome sufficiently precise to the human eye.
In theory, if the song’s sound has latency, the metronome’s timer will act according to the sound of the song, including said latency.
Now I am in the experimentation phase, trying to understand where all the problems related to time are for a more precise adjustment (and in this adjustment I consider a small delay error at all times, which is inevitable). That’s why even the metronome tool can include a manual setting called latency (metronome latency), although it should not be necessary.
Now I am considering at all times limiting the timer by pattern, so the problem is not to adjust the timer to the whole song, but to depend only on each pattern in the sequence.
I have tested my initial code thoroughly, and it works reasonably well. In addition, the code does not consume hardly any resources, so it depends directly on two things:
- A delay in the execution of a function (a timer), which can be + -5ms (this is a range of 10ms of lag, -5 … 0 … +5 ms).Renoise.ScriptingTool.API.lua:
--[[
Register a timer function or table with a function and context (a method)
that periodically gets called by the app_idle_observable for your tool.
Modal dialogs will avoid that timers are called. To create a one-shot timer,
simply call remove_timer at the end of your timer function. Timer_interval_in_ms
must be > 0. The exact interval your function is called will vary
a bit, depending on workload; e.g. when enough CPU time is available the
rounding error will be around +/- 5 ms.
]]
-- Returns true when the given function or method was registered as a timer.
renoise.tool():has_timer(function or {object, function} or {function, object})
-> [boolean]
-- Add a new timer as described above.
renoise.tool():add_timer(function or {object, function} or {function, object},
timer_interval_in_ms)
-- Remove a previously registered timer.
renoise.tool():remove_timer(timer_func)
2.Delay in observables,to read the BPM and LPB values, the number of lines per pattern, and the beginning of each pattern in the sequence, which can be 10 ms This is precisely what I have not yet tried. Therefore, my initial code does not use observables yet…
-- Invoked periodically in the background, more often when the work load
-- is low, less often when Renoise's work load is high.
-- The exact interval is undefined and can not be relied on, but will be
-- around 10 times per sec.
-- You can do stuff in the background without blocking the application here.
-- Be gentle and don't do CPU heavy stuff please!
renoise.tool().app_idle_observable
-> [renoise.Document.Observable object]
The doubt that I have now is:There is some delay in usingrenoise.song().selected_sequence_index_observable:add:notifier(function)???
-- The currently edited sequence position.
renoise.song().selected_sequence_index, _observable
-> [number]
The code must check the sequence index number at all times. The same for this:
-- BPM, LPB, and TPL.
renoise.song().transport.bpm, _observable
-> [number, 32-999]
renoise.song().transport.lpb, _observable
-> [number, 1-256]
The documentation for the Observables, Renoise,Document.API.lua:
-------- Observables
Documents and Views in the Renoise API are modelled after the observer pattern
(have a look at <http://en.wikipedia.org/wiki/Observer_pattern> if this is new
to you). This means, in order to track changes, a document is basically just a
set of raw data (booleans, numbers, lists, nested nodes) which anything can
attach notifier function (listeners) to. For example, a view in the Renoise
API is an Observer, which listens to observable values in Documents.
Attaching and removing notifiers can be done with the functions 'add_notifier',
'remove_notifier' from the Observable base class. These support multiple kinds
of callbacks, plain functions and methods (functions with a context). Please
see renoise.Document.Observable for more details. Here is a simple example:
function bpm_changed()
print(("something changed the BPM to %s"):format(
renoise.song().transport.bpm))
end
renoise.song().transport.bpm_observable:add_notifier(bpm_changed)
-- later on, maybe:
renoise.song().transport.bpm_observable:remove_notifier(bpm_changed)
When adding notifiers to lists (like the track list in a song) an additional
context parameter is passed to your notifier function. This way you know what
happened to the list:
function tracks_changed(notification)
if (notification.type == "insert") then
print(("new track was inserted at index: %d"):format(notification.index))
elseif (notification.type == "remove") then
print(("track got removed from index: %d"):format(notification.index))
elseif (notification.type == "swap") then
print(("track at index: %d and %d swapped their positions"):format(
notification.index1, notification.index2))
end
end
renoise.song().tracks_observable:add_notifier(tracks_changed)
If you only want to use the existing "_observables" in the Renoise API,
then this is all you need to know. If you want to create your own documents,
read on.
So a call of this type has some delay too? For example:renoise.song().transport.bpm_observable:add_notifier(bpm_changed)Is there a delay in updating the value after being read, or is it instantaneous?This would be required once when starting the new pattern in the sequence…