Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSC shapes need additional logic #1178

Open
yaxu opened this issue Feb 25, 2025 · 9 comments
Open

OSC shapes need additional logic #1178

yaxu opened this issue Feb 25, 2025 · 9 comments

Comments

@yaxu
Copy link
Member

yaxu commented Feb 25, 2025

With SuperDirt PR musikinformatik/SuperDirt#309 from @ahihi, it would be nice to send a message if either the 'sound' or 'dsp' message was present. Probably needs extra logic in the shape.

@yaxu yaxu changed the title Placeholder for issue about OSC shapes from meet OSC shapes need additional logic Feb 26, 2025
@yaxu
Copy link
Member Author

yaxu commented Feb 26, 2025

Maybe it is easiest to add additional minimal OSC shape for dsp (might need more than one for each dsp parameter, if they are separable)

That would also require ignoring the dsp parameters in the OSC message. This could either be on the tidal side by adding a exclusion list of parameters to the shape (needs a bit of work) or on the superdirt side by ignoring dsp parameters if a sound parameter is present (because they will also come in a separate message).

It seems tidiest to not send the parameters in the first place so I think option 1 would be nice. Also tagging @telephon

@yaxu
Copy link
Member Author

yaxu commented Feb 26, 2025

Oh but I guess the dsp does need to go in the same message as the sound. In fact am I right that in some sense the dsp is the sound? In that case maybe what gets sent is an OSC message with sound "sine()" plus an extra flag to say it's DSP? This could be like dsp "local" or dsp "global" if I understand this feature right.

Then on the tidal side it would just need something like dsp code = sound (pure code) # cS "dsp" "local"

But I remember @ahihi you wanted to explicitly set sound too on patterns.. I didn't understand the use case for that then..

@ahihi
Copy link
Contributor

ahihi commented Feb 26, 2025

But I remember @ahihi you wanted to explicitly set sound too on patterns.. I didn't understand the use case for that then..

the dsp code be used not only to synthesize, but also to process the output of sound. e.g. dsp "in.fold2(0.5)" # s "gabba"

@ahihi
Copy link
Contributor

ahihi commented Feb 26, 2025

what if it was something like this?

data BoolExpr a
  = Val a
  | Not (BoolExpr a) -- not sure if useful
  | All [BoolExpr a]
  | Any [BoolExpr a]

data Args
  = Named {requiredArgs :: BoolExpr String}

then superDirtShape could be

superdirtShape = OSC "/dirt/play" $ Named {requiredArgs = Any [Val "s", Val "dsp", Val "gdsp"]}

maybe with a couple of helpers to keep the simple cases easy to write

allArgs = All . map Val
anyArgs = Any . map Val

superdirtShape = OSC "/dirt/play" $ Named {requiredArgs = anyArgs ["s", "dsp", "gdsp"]}

@yaxu
Copy link
Member Author

yaxu commented Feb 26, 2025

Would it be OK to use a different param for processing sound, vs generating it? dsp for sound vs edsp for effect I think that would simplify the implementation

@ahihi
Copy link
Contributor

ahihi commented Feb 26, 2025

it would complicate the SuperDirt side, where sound is an independent module (which arguably could already use some simplification) and live-dsp is just another module that runs right after sound (activated by the presence of a dsp key) that may or may not use the output from the preceding module. i think this achieves a nice separation of concerns.

also, one thing to note regarding this:

In that case maybe what gets sent is an OSC message with sound "sine()" plus an extra flag to say it's DSP? This could be like dsp "local" or dsp "global" if I understand this feature right.

Then on the tidal side it would just need something like dsp code = sound (pure code) # cS "dsp" "local"

a pattern can have both dsp and gdsp, because it should be possible to do module-level synthesis/processing and then feed the output into a persistent global effect. example:

tidal.gdsp.mp4

@ahihi
Copy link
Contributor

ahihi commented Feb 26, 2025

i tried out #1178 (comment) and i think it does not complicate things much. the only thing that changes is that toData in the Pattern module becomes

toData :: OSC -> Event ValueMap -> Maybe [O.Datum]
toData (OSC {args = ArgList as}) e = fmap (fmap toDatum) $ mapM (\(n, v) -> Map.lookup n (value e) <|> v) as
toData (OSC {args = Named rqrd}) e
  | hasRequired rqrd = Just $ concatMap (\(n, v) -> [O.string n, toDatum v]) $ Map.toList $ value e
  | otherwise = Nothing
  where
    hasRequired (Val k)  = k `elem` ks
    hasRequired (Not x)  = not $ hasRequired x
    hasRequired (All xs) = all hasRequired xs
    hasRequired (Any xs) = any hasRequired xs
    ks = Map.keys (value e)
toData _ _ = Nothing

it occurs to me that the intermediate data structure could even be skipped by making requiredArgs a predicate function:

data Args
  = Named {requiredArgs :: [String] -> Bool}
superdirtShape = OSC "/dirt/play" $ Named {requiredArgs = any (`elem` ["s", "dsp", "gdsp"]) }
toData :: OSC -> Event ValueMap -> Maybe [O.Datum]
toData (OSC {args = ArgList as}) e = fmap (fmap toDatum) $ mapM (\(n, v) -> Map.lookup n (value e) <|> v) as
toData (OSC {args = Named rqrd}) e
  | rqrd ks   = Just $ concatMap (\(n, v) -> [O.string n, toDatum v]) $ Map.toList $ value e
  | otherwise = Nothing
  where
    ks = Map.keys (value e)
toData _ _ = Nothing

though then we lose the automatically derived Show instance on Args.

@yaxu
Copy link
Member Author

yaxu commented Mar 2, 2025

Maybe it could be even more general as a field filtervalues :: Maybe (ValueMap -> Bool)?

@ahihi
Copy link
Contributor

ahihi commented Mar 2, 2025

you mean Args would become something like this?

data Args
  = Custom {filterValues :: Maybe (ValueMap -> Bool)} -- "Named" didnt seem very descriptive anymore
  | ArgList [(String, Maybe Value)]

seems reasonable, but what is the Maybe for?

another direction of generalizability could be to allow transforming the event data in addition to filtering, e.g. ValueMap -> Maybe ValueMap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants