import logging
from typing import cast
from light9.effect.edit import clamp
from light9.namespaces import L9
from rdfdb.syncedgraph.readonly_graph import ReadOnlyConjunctiveGraph
from rdfdb.syncedgraph.syncedgraph import SyncedGraph
from rdflib import RDF, Literal, URIRef
log = logging.getLogger()
class Pages:
"""converts between fader numbers and FaderUri, which is a mapping that can
be changed by moving between pages"""
def __init__(self, graph: SyncedGraph, ctx: URIRef):
self.graph = graph
self.ctx = ctx
self.currentFaders = {} # midi control channel num : FaderUri
def getChansToFaders(self) -> dict[int, URIRef]:
fadePage = self.getCurMappedPage()
ret = []
for f in self.graph.objects(fadePage, L9.fader):
columnLit = cast(Literal, self.graph.value(f, L9['column']))
col = int(columnLit.toPython())
ret.append((col, f))
ret.sort()
ctl_channels = list(range(81, 88 + 1))
out = {}
for chan, (col, f) in zip(ctl_channels, ret):
out[chan] = f
return out
def changePage(self, dp: int):
"""dp==-1, make the previous page active, etc. Writes to graph"""
with self.graph.currentState() as current:
allPages = sorted(current.subjects(RDF.type, L9.FadePage), key=lambda fp: str(fp))
mapping = self.getGraphMappingNode(current)
curPage = current.value(mapping, L9.outputs)
if curPage is None:
curPage = allPages[0]
idx = allPages.index(curPage)
newIdx = clamp(idx + dp, 0, len(allPages) - 1)
log.info(f'change from {idx} {newIdx}')
newPage = allPages[newIdx]
self.setCurMappedPage(mapping, newPage)
def getCurMappedPage(self) -> URIRef:
mapping = self.getGraphMappingNode(self.graph)
ret = self.graph.value(mapping, L9['outputs'])
assert ret is not None
return ret
def setCurMappedPage(self, mapping: URIRef, newPage: URIRef):
self.graph.patchObject(self.ctx, mapping, L9.outputs, newPage)
def getGraphMappingNode(self, g: ReadOnlyConjunctiveGraph | SyncedGraph) -> URIRef:
mapping = g.value(L9['midiControl'], L9['map'])
if mapping is None:
raise ValueError('no :midiControl :map ?mapping')
midiDev = g.value(mapping, L9['midiDev'])
ourDev = 'bcf2000'
if midiDev != Literal(ourDev):
raise NotImplementedError(f'need {mapping} to have :midiDev {ourDev!r}')
return mapping
def compileCurrents(self):
self.currentFaders.clear()
try:
new = self.getChansToFaders()
except ValueError:
return # e.g. empty-graph startup
self.currentFaders.update(new)
def lookupFader(self, dev: str, control: int) -> URIRef:
return {
'quneo': {
44: L9['show/dance2023/fadePage1f0'],
45: L9['show/dance2023/fadePage1f0'],
46: L9['show/dance2023/fadePage1f0'],
},
'bcf2000': self.currentFaders,
}[dev][control]