Commit eae8cb52 authored by Hanspeter Portner's avatar Hanspeter Portner

api: improve parameter, add state:sync.

parent 54c4e848
Pipeline #7382435 passed with stages
in 4 minutes and 15 seconds
......@@ -5,6 +5,5 @@
=== High priority
* doc: Parameter
* api: add forge:vectorize
* api: simplify forge:vector
* api: simplify forge:set
* api: improve Parameter
......@@ -17,6 +17,51 @@
#include <api_parameter.h>
__realtime static int
_lparameter__index(lua_State *L)
{
moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
// 1: self
// 2: key
if(lua_isinteger(L, 2) && (lua_tointeger(L, 2) == moony->uris.rdf_value))
{
if(lua_geti(L, 1, moony->uris.patch.get) == LUA_TFUNCTION)
{
lua_pushvalue(L, 1); // self
lua_call(L, 1, 1);
return 1;
}
}
lua_pushnil(L);
return 1;
}
__realtime static int
_lparameter__newindex(lua_State *L)
{
moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
// 1: self
// 2: key
// 3: value
if(lua_isinteger(L, 2) && (lua_tointeger(L, 2) == moony->uris.rdf_value))
{
if(lua_geti(L, 1, moony->uris.patch.set) == LUA_TFUNCTION)
{
lua_pushvalue(L, 1); // self
lua_pushvalue(L, 3); // value
lua_call(L, 2, 0);
}
}
return 0;
}
__realtime static int
_lparameter__call(lua_State *L)
{
......@@ -26,16 +71,13 @@ _lparameter__call(lua_State *L)
// 1: self
// 2: value or nil
// get current value
// push old value on stack
lua_geti(L, 1, moony->uris.rdf_value);
//TODO call patch:Get?
if(!lua_isnil(L, 2))
if(!lua_isnil(L, 2)) // has value to set
{
// set new value
lua_pushvalue(L, 2);
lua_seti(L, 1, moony->uris.rdf_value);
//TODO call patch:Set?
lua_seti(L, 1, moony->uris.rdf_value); // param[RDF.value] = value
}
return 1;
......@@ -64,6 +106,8 @@ _lparameter(lua_State *L)
}
const luaL_Reg lparameter_mt [] = {
{"__index", _lparameter__index},
{"__newindex", _lparameter__newindex},
{"__call", _lparameter__call},
{NULL, NULL}
};
......@@ -242,7 +242,7 @@ _lstateresponder_register_access(lua_State *L, moony_t *moony, int64_t frames,
}
__realtime static inline void
_lstateresponder_register(lua_State *L, moony_t *moony, int64_t frames,
_lstateresponder_reg(lua_State *L, moony_t *moony, int64_t frames,
lforge_t *lforge, const LV2_Atom_URID *subject, int32_t sequence_num)
{
LV2_Atom_Forge_Frame obj_frame;
......@@ -388,7 +388,7 @@ _lstateresponder__call(lua_State *L)
if(!property)
{
// register state
_lstateresponder_register(L, moony, frames, lforge, subject, sequence_num);
_lstateresponder_reg(L, moony, frames, lforge, subject, sequence_num);
lua_pushboolean(L, 1); // handled
return 1;
......@@ -453,25 +453,13 @@ _lstateresponder__call(lua_State *L)
|| !lv2_atom_forge_urid(lforge->forge, property->body) )
luaL_error(L, forge_buffer_overflow);
if(lua_geti(L, -1, moony->uris.patch.get) != LUA_TNIL)
{
lua_pushvalue(L, -2); // self[property]
lua_pushvalue(L, 2); // frames
lua_pushvalue(L, 3); // forge
lua_call(L, 3, 1);
}
else
{
lua_pop(L, 1); // nil
lua_geti(L, -1, moony->uris.rdf_value);
}
if(!lua_isnil(L, -1))
if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL)
{
if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.value)
|| !_lforge_basic(L, -1, lforge->forge, range) )
luaL_error(L, forge_buffer_overflow);
}
lua_pop(L, 1); // value
}
lv2_atom_forge_pop(lforge->forge, &obj_frame);
......@@ -509,20 +497,8 @@ _lstateresponder__call(lua_State *L)
if( (lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
&& (lua_geti(L, -1, property->body) != LUA_TNIL) ) // self[property]
{
if(lua_geti(L, -1, moony->uris.patch.set) != LUA_TNIL)
{
lua_pushvalue(L, -2); // self[property]
lua_pushvalue(L, 2); // frames
lua_pushvalue(L, 3); // forge
_latom_value(L, value);
lua_call(L, 4, 0);
}
else
{
lua_pop(L, 1); // nil
_latom_value(L, value);
lua_seti(L, -2, moony->uris.rdf_value); // self[property].value = value
}
_latom_value(L, value);
lua_seti(L, -2, moony->uris.rdf_value); // self[property][RDF.value] = value
if(sequence_num)
_lstateresponder_ack(L, lforge, moony, frames, sequence_num);
......@@ -546,7 +522,7 @@ _lstateresponder__call(lua_State *L)
}
__realtime static int
_lstateresponder_reg(lua_State *L)
_lstateresponder_register(lua_State *L)
{
moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
......@@ -572,11 +548,104 @@ _lstateresponder_reg(lua_State *L)
};
// register state
_lstateresponder_register(L, moony, frames, lforge, &subject, 0); //TODO use patch:sequenceNumber
_lstateresponder_reg(L, moony, frames, lforge, &subject, 0); //TODO use patch:sequenceNumber
return 1; // forge
}
__realtime static int
_lstateresponder_sync(lua_State *L)
{
moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
lua_settop(L, 3); // discard superfluous arguments
// 1: self
// 2: frames
// 3: forge
//FIXME support syncing specific parameter only
// replace self with its uservalue
lua_getuservalue(L, 1);
lua_replace(L, 1);
int64_t frames = luaL_checkinteger(L, 2);
lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
LV2_Atom_Forge_Frame obj_frame;
if( !lv2_atom_forge_frame_time(lforge->forge, frames)
|| !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.put) )
luaL_error(L, forge_buffer_overflow);
{
LV2_Atom_Forge_Frame body_frame;
if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject)
|| !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.self)
|| !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
|| !lv2_atom_forge_int(lforge->forge, 0) //TODO
|| !lv2_atom_forge_key(lforge->forge, moony->uris.patch.body)
|| !lv2_atom_forge_object(lforge->forge, &body_frame, 0, 0) )
luaL_error(L, forge_buffer_overflow);
{
if(lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
{
// iterate over writable properties
lua_pushnil(L);
while(lua_next(L, -2))
{
const LV2_URID key = lua_tointeger(L, -2); // key
LV2_URID range = 0; // fallback
if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER) // prop[RDFS.range]
range = lua_tointeger(L, -1);
lua_pop(L, 1); // range
if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL)
{
if( !lv2_atom_forge_key(lforge->forge, key)
|| !_lforge_basic(L, -1, lforge->forge, range) )
luaL_error(L, forge_buffer_overflow);
}
lua_pop(L, 1); // nil || rdf_value
lua_pop(L, 1); // removes 'value', keeps 'key' for next iteration
}
}
lua_pop(L, 1); // nil || writable
if(lua_geti(L, 1, moony->uris.patch.readable) != LUA_TNIL)
{
// iterate over writable properties
lua_pushnil(L);
while(lua_next(L, -2))
{
const LV2_URID key = lua_tointeger(L, -2); // key
LV2_URID range = 0; // fallback
if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER) // prop[RDFS.range]
range = lua_tointeger(L, -1);
lua_pop(L, 1); // range
if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL)
{
if( !lv2_atom_forge_key(lforge->forge, key)
|| !_lforge_basic(L, -1, lforge->forge, range) )
luaL_error(L, forge_buffer_overflow);
}
lua_pop(L, 1); // nil || rdf_value
lua_pop(L, 1); // removes 'value', keeps 'key' for next iteration
}
}
lua_pop(L, 1); // nil || readable
}
lv2_atom_forge_pop(lforge->forge, &body_frame);
}
lv2_atom_forge_pop(lforge->forge, &obj_frame);
return 0;
}
__realtime static int
_lstateresponder_stash(lua_State *L)
{
......@@ -617,12 +686,11 @@ _lstateresponder_stash(lua_State *L)
if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL) // prop[RDF.value]
{
//TODO call prop[Patch.Get] ?
if( !lv2_atom_forge_key(lforge->forge, key)
|| !_lforge_basic(L, -1, lforge->forge, range) )
luaL_error(L, forge_buffer_overflow);
}
lua_pop(L, 1); // nil || prop[RDF.value]
lua_pop(L, 1); // value
// removes 'value'; keeps 'key' for next iteration
lua_pop(L, 1);
......@@ -665,9 +733,7 @@ _lstateresponder_apply(lua_State *L)
{
_latom_value(L, &prop->value);
lua_seti(L, -2, moony->uris.rdf_value); // set prop[RDF.value]
//TODO call prop[Patch.Set] ?
}
lua_pop(L, 1); // nil || prop
}
}
......@@ -701,7 +767,8 @@ _lstateresponder(lua_State *L)
const luaL_Reg lstateresponder_mt [] = {
{"__call", _lstateresponder__call},
{"register", _lstateresponder_reg},
{"register", _lstateresponder_register},
{"sync", _lstateresponder_sync},
{"stash", _lstateresponder_stash},
{"apply", _lstateresponder_apply},
{NULL, NULL}
......
......@@ -3430,6 +3430,16 @@ end</code></pre>
<dd>self forge object</dd>
</dl>
<dl>
<dt class="func">stateR:sync(frames, forge)</dt>
<dt>frames (integer)</dt>
<dd>frame time of current event</dd>
<dt>forge (userdata)</dt>
<dd>forge object to sync state to</dd>
<dt class="ret">(userdata)</dt>
<dd>self forge object</dd>
</dl>
<dl>
<dt class="func">stateR:stash(forge)</dt>
<dt>forge (userdata)</dt>
......@@ -3451,7 +3461,7 @@ end</code></pre>
local urn = HashMap('urn:moony:xzy#')
-- define read-only parameter
local period = {
local period = Parameter{
[RDFS.label] = 'Period',
[RDFS.comment] = 'set period',
[RDFS.range] = Atom.Float,
......@@ -3462,7 +3472,7 @@ local period = {
}
-- define read-write parameter
local frequency = {
local frequency = Parameter{
[RDFS.label] = 'Frequency',
[RDFS.comment] = 'set frequency',
[RDFS.range] = Atom.Int,
......
......@@ -81,10 +81,6 @@ stateR:pInt
a lv2:Parameter ;
rdfs:label "An Int" ;
rdfs:range atom:Int .
stateR:pPath
a lv2:Parameter ;
rdfs:label "A Path" ;
rdfs:range atom:Path .
stateR:pString
a lv2:Parameter ;
rdfs:label "A String" ;
......@@ -328,27 +324,29 @@ moony:bank-state_state-responder
moony:paramHidden false ;
moony:paramCols 3 ;
moony:paramRows 4 ;
moony:code """local urid = HashMap('urn:moony:stateR#')
moony:code """local urn = HashMap('urn:moony:stateR#')
local sync = false
local pLong
local pInt = {
local pInt = Parameter{
[RDFS.label] = 'Integer',
[RDFS.comment] = 'This is an Integer',
[RDFS.range] = Atom.Int,
[Units.unit] = Units.hz,
[Core.minimum] = 0,
[Core.maximum] = 10,
[RDF.value] = 5,
[Patch.Set] = function(self, frames, forge, value)
self[RDF.value] = value
pLong[RDF.value] = value * 2
if forge then
forge:time(frames):set(urid.pLong):long(pLong[RDF.value]):pop()
end
_value = 5,
[Patch.Get] = function(self)
return self._value
end,
[Patch.Set] = function(self, value)
self._value = value
pLong(value * 2)
sync = true
end
}
pLong = {
pLong = Parameter{
[RDFS.label] = 'Long',
[RDFS.comment] = 'This is a Long',
[RDFS.range] = Atom.Long,
......@@ -359,24 +357,25 @@ pLong = {
}
local pDouble
local pFloat = {
local pFloat = Parameter{
[RDFS.label] = 'Float',
[RDFS.comment] = 'This is a Float',
[RDFS.range] = Atom.Float,
[Units.unit] = Units.m,
[Core.minimum] = -1.0,
[Core.maximum] = 1.0,
[RDF.value] = 0.0,
[Patch.Set] = function(self, frames, forge, value)
self[RDF.value] = value
pDouble[RDF.value] = value * 2
if forge then
forge:time(frames):set(urid.pDouble):double(pDouble[RDF.value]):pop()
end
end
_value = 0.0,
[Patch.Get] = function(self)
return self._value
end,
[Patch.Set] = function(self, value)
self._value = value
pDouble(value * 2)
sync = true
end
}
pDouble = {
pDouble = Parameter{
[RDFS.label] = 'Double',
[RDFS.comment] = 'This is a Double',
[RDFS.range] = Atom.Double,
......@@ -386,63 +385,44 @@ pDouble = {
[RDF.value] = pFloat[RDF.value] * 2
}
local pBool = {
local pBool = Parameter{
[RDFS.label] = 'Bool',
[RDFS.comment] = 'This is a Boolean',
[RDFS.range] = Atom.Bool,
[RDF.value] = true
}
local pURI
local pURID = {
local pURID = Parameter{
[RDFS.label] = 'URID',
[RDFS.comment] = 'This is an URID',
[RDFS.range] = Atom.URID,
[RDF.value] = MIDI.MidiEvent,
[Patch.Set] = function(self, frames, forge, value)
self[RDF.value] = value
pURI[RDF.value] = Unmap[value]
if forge then
forge:time(frames):set(urid.pURI):uri(pURI[RDF.value]):pop()
end
end
}
pURI = {
[RDFS.label] = 'URI',
[RDFS.comment] = 'This is an URI',
[RDFS.range] = Atom.URI,
[RDF.value] = Unmap(MIDI.MidiEvent)
[RDF.value] = MIDI.MidiEvent
}
local pString = {
local pString = Parameter{
[RDFS.label] = 'String',
[RDFS.comment] = 'This is a String',
[RDFS.range] = Atom.String,
[RDF.value] = 'hello world'
}
local pChunk = {
local pChunk = Parameter{
[RDFS.label] = 'Chunk',
[RDFS.comment] = 'This is a Chunk',
[RDFS.range] = Atom.Chunk,
[RDF.value] = string.char(0x1, 0x2, 0x3),
[Patch.Set] = function(self, frames, forge, value)
self[RDF.value] = value
_value = string.char(0x1, 0x2, 0x3),
[Patch.Get] = function(self)
return self._value
end,
[Patch.Set] = function(self, value)
self._value = value
for i = 1, #value do
print(i, string.byte(value, i))
end
end
}
local pPath = {
[RDFS.label] = 'Path',
[RDFS.comment] = 'This is a Path',
[RDFS.range] = Atom.Path,
[RDF.value] = '/tmp/file.suffix'
}
local pEnum = {
local pEnum = Parameter{
[RDFS.label] = 'Enum',
[RDFS.comment] = 'This is an Enum',
[RDFS.range] = Atom.Int,
......@@ -456,23 +436,21 @@ local pEnum = {
}
}
local stateR = StateResponder({
local stateR = StateResponder{
[Patch.writable] = {
[urid.pInt] = pInt,
[urid.pFloat] = pFloat,
[urid.pBool] = pBool,
[urid.pURID] = pURID,
[urid.pString] = pString,
[urid.pChunk] = pChunk,
[urid.pPath] = pPath,
[urid.pEnum] = pEnum
[urn.pInt] = pInt,
[urn.pFloat] = pFloat,
[urn.pBool] = pBool,
[urn.pURID] = pURID,
[urn.pString] = pString,
[urn.pChunk] = pChunk,
[urn.pEnum] = pEnum
},
[Patch.readable] = {
[urid.pLong] = pLong,
[urid.pDouble] = pDouble,
[urid.pURI] = pURI
[urn.pLong] = pLong,
[urn.pDouble] = pDouble
}
})
}
function save(forge)
stateR:stash(forge)
......@@ -480,9 +458,6 @@ end
function restore(atom)
stateR:apply(atom)
pLong[RDF.value] = pInt[RDF.value] * 2
pDouble[RDF.value] = pFloat[RDF.value] * 2
pURI[RDF.value] = Unmap[pURID[RDF.value]]
end
function once(n, control, notify)
......@@ -491,7 +466,10 @@ end
function run(n, control, notify)
for frames, atom in control:foreach() do
stateR(frames, notify, atom)
if stateR(frames, notify, atom) and sync then
stateR:sync(frames, notify)
sync = false
end
end
end""" ;
moony:state [
......@@ -500,7 +478,6 @@ end""" ;
stateR:pEnum 0 ;
stateR:pFloat "8e-1"^^xsd:float ;
stateR:pInt 9 ;
stateR:pPath <file:///tmp/file.suffix> ;
stateR:pString "world hello" ;
stateR:pURID <http://lv2plug.in/ns/ext/midi#MidiEvent>
]
......@@ -1119,7 +1096,7 @@ moony:bank-template_part-4
local urn = HashMap('urn:moony:template-4#')
-- define single parameter
local param = {
local param = Parameter({
[RDFS.label] = 'Awesome Parameter',
[RDFS.comment] = 'does this and that...',
[RDFS.range] = Atom.Int,
......@@ -1127,7 +1104,7 @@ local param = {
[Core.maximum] = 10,
[Units.unit] = Units.hz,
[RDF.value] = 5
}
})
-- define a StateResponder object
local stateR = StateResponder({
......
......@@ -1423,23 +1423,24 @@ do
[RDF.value] = 2
}
assert(state_int() == state_int[RDF.value])
assert(state_int() == 2)
assert(state_int(1) == 2)
assert(state_int() == 1)
local state_flt = {
local state_flt = Parameter{
[RDFS.label] = 'Flt',
[RDFS.range] = Atom.Float,
[Core.minimum] = -0.5,
[Core.maximum] = 10.0,
[RDF.value] = 1.0,
[Patch.Get] = function(self, frames, forge)
_value = 1.0,
[Patch.Get] = function(self)
flt_get_responded = true
return self[RDF.value]
return self._value
end,
[Patch.Set] = function(self, frames, forge, value)
[Patch.Set] = function(self, value)
self._value = value
flt_set_responded = true
self[RDF.value] = value
end
}
......@@ -1460,17 +1461,26 @@ do
-- state:stash
forge:frameTime(4)
state:stash(forge)
-- state:sync
state:sync(5, forge)
--FIXME state:register()
end
local function consumer(seq, forge)
assert(#seq == 6)
for frames, atom in seq:foreach() do
if frames < 4 then
assert(state(frames, forge, atom) == true)
end
end
assert(state_int[RDF.value] == 2)
assert(state_flt[RDF.value] == 2.0)
assert(state_int() == state_int[RDF.value])
assert(state_int() == 2)
assert(state_flt() == state_flt[RDF.value])
assert(state_flt() == 2.0)
assert(flt_get_responded)
assert(flt_set_responded)
......@@ -1485,8 +1495,23 @@ do
-- test state:apply
state:apply(atom)
assert(state_int[RDF.value] == 1)
assert(state_flt[RDF.value] == 1.0)
assert(state_int() == 1)
assert(state_flt() == 1.0)
-- test state:sync
atom = seq[6]
assert(atom.type == Atom.Object)
assert(#atom == 3)
assert(atom[Patch.subject].type == Atom.URID)
assert(atom[Patch.sequenceNumber].type == Atom.Int)
assert(atom[Patch.sequenceNumber].body == 0)
local body = atom[Patch.body]
assert(body.type == Atom.Object)
assert(#body == 2)
assert(body[urid.int].type == Atom.Int)
assert(body[urid.int].body == 1)
assert(body[urid.flt].type == Atom.Float)
assert(body[urid.flt].body == 1.0)
end
test(producer, consumer)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment