First time here? Check out the FAQ!
x

How to construct Kyma-fied OSC syntax using 'variable' names?

0 votes
661 views

Referring to this part of the related Q&A:

For example, in the Sound being replicated, you could do something like:

(!osc_count ge: ?VoiceNumber) true: !osc_value false: 0

to provide a default value (or to turn off the output of a Sound) when too few values were supplied.

Could I construct the hot value !osc_value with a user-supplied string in the place of 'value', by using the asHotValue command/technique as outlined on p.220 of "Kyma X Revealed!"?

For example, create a function (i.e. myFaderMaker) that constructs a hot value based on a supplied string that's available pre-compilation, and store the result of a call to that in a variable, which is then supplied to the above example code, in place of !osc_value.

Something like this:

| myFaderMaker oscValue |

myFaderMaker := [:str | ('osc_', str) asHotValue].

oscValue := myFaderMaker value: myArbitraryOSCAddress

 

then, in the sound being replicated, do something like:

(!osc_count ge: ?VoiceNumber) true: oscValue false: 0

...as long as oscValue is still in scope!

Next, in a SoundToGlobalController external to the replicator, assign incoming consecutive osc-values to an EventVariable array, and output the entire array as the GeneratedEvent (which could then be used to directly populate a sequencer field with values, for example):

| anArray, myFaderMaker2 |

myFaderMaker2 := [:str :num | ('osc_', str, '__', (num printString)) asHotValue].

anArray := EventVariable new size: oscCountMaxValue.

1 to: oscCountMaxValue do: [ :i |

    (anArray @< i) <+ (myFaderMaker2 value: myArbitraryOSCAddress value: i) ].

"replace this line with an expression that truncates anArray to the size of !osc_count ??"

anArray.

 

This inspires a few related questions:

[1] In the above code example: 

Is there a way to, at the Capytalk update rate (necessarily), truncate anArray to the size of !osc_count, or similarly, copy those values to a new array?  If so, could a version of this example be made without use of the replicator, by referring to the osc hotValues directly using an approach like in 'myFaderMaker2' above... provided that the resulting array count does not exceed !osc_count, or otherwise could somehow be made to "stop" at that hotValue-supplied index value?

[2] Where might one best place a 'mini-code-library' of predefined blocks, to make them available to the rest of the sound?

I have some more questions mulling around my brain, but these are the basics that should lead me to figure the rest out on my own. Thank you!

 

asked Dec 10, 2018 in Controllers, OSC & MIDI by thom-jordan (Practitioner) (800 points)
edited Dec 11, 2018 by thom-jordan
Hi Thom,

If I've understood what you're saying it looks like you want an STGC to output an EventVariable array. I don't think it can do that.

I asked a recent question about arrays here:
https://kyma.symbolicsound.com/qa/3994/select-between-arrays-which-then-elsewhere-different-lengths

and if you read through the thread of comments SSC says:

"An EventExpression is:
i) a number
ii) an EventValue (of the form !anEV (sometimes called hot value)) OR
iii) a function of other EventExpressions (for example !anEV + 1)

When there are several EventExpressions separated by commas, the result of evaluating that expression is still a single value.

(Sorry there isn't a way to return an Array in Capytalk..."

I suspect a block defined outside of a Sound isn't going to be in scope in any of the parameter fields. Let me know if you get that to work, that would be really handy!

One way round that could be to do all the work in a Script to the right of your Sound and simplify the expressions in the Sound's parameters using ?Variables to bring in the calculated values from the Script.
Is the way that you're using an array the same as the array structure described on page 71 of the Kyma 7 User_Guide (shown below)?

8.3.0.3 @<
Capytalk just acquired its first data structure: an Array! You can now declare an EventVariable as an array by giving it a size. After that, you can index entries in the array using the @< operator.
anArray @< !anIndex
| anArray |
anArray := EventVariable new size: 8.
anArray @< (1 s tick nextIndexMod: size)
(anArray @< !Index) <+ !Value

If using an STGC to output an EventVariable array doesn't work, then perhaps a variation of the code ssc provided in answer#2 of the link you included, might be used with a way to set the upper-bound via a Capytalk hotValue (e.g. !osc_count, etc.), in a script, and then refer to the resulting array(s) elsewhere in the sound, to the left of the script. The osc-syntax necessary to populate the arrays in the script would still need to be constructed, possibly as I suggested above using something like:

myFaderMaker2 := [:str :num | ('osc_', str, '__', (num printString)) asHotValue].


For convenience, here is ssc's code from answer#2 of the link you provided:

| valueArrays flattenedArray rowLengths cumulativeRowLengths value |

valueArrays := #(
    #(0 0.5 1 {5/4} {4/3} {3/2} {9/5} 2 {18/8} {12/5} 3 {18/5} 4 6 7 {36/5} 8)
    #(0 {9/8 * 0.5} {9/8} {(9/8) * (5/4)} {(9/8) *(3/2)} {(9/8) * (8/5)} {18/8})
    ).

flattenedArray := valueArrays inject: Array new into: [ :flat :array | flat, array].

rowLengths := valueArrays collect: [ :a | a size].

cumulativeRowLengths :=
    1 to: valueArrays size collect: [ :i |
        (1 to: i - 1) inject: 0 into: [ :length :index |
            length + (rowLengths at: index)]].

value := (!Row of: cumulativeRowLengths) + (!Column min: (!Row of: rowLengths) - 1) of: flattenedArray.

value


Regarding the blocks library, I was similarly considering using a Script placed at the right of a sound, but was then wondering if there may be any alternative approaches. This should work well I think, in lieu of any other possible approaches.

I need to read through your previously-posted questions since we seem to have some specific areas of common interest.  Thanks for your input !
Another alternative might be to include an STGC in the afore-mentioned replicated sound, to broadcast the arbitrarily-constructed oscValue as a known GeneratedEvent value, then refer to that known value elsewhere in the sound (or even then supply the GeneratedEvent value name by encapsulating the whole routine in a user-defined class, akin to the approach used by NE_Labs in their ReadOSC class/object):


| myFaderMaker oscValue |

myFaderMaker := [:str | ('osc_', str) asHotValue].

oscValue := myFaderMaker value: myArbitraryOSCAddress


then within the value field of the STCG contained within the replicated sound:

(!osc_count ge: ?VoiceNumber) true: oscValue false: 0


The whole reason for doing this is to be able to supply an arbitrary string 'str' as a parameter, and then be able to automatically generate syntax to access incoming osc values as !Osc_str__1, !Osc_str__2, !Osc_str__3, ... , !Osc_str__N (where N = !osc_count). instead of having to supply values in a form like what's shown below, to each and every parameter that wishes to use values provided via osc.

!Osc_str__1 !Osc_str__2 !Osc_str__3 !Osc_str__4
!Osc_str__5 !Osc_str__6 !Osc_str__7 !Osc_str__8
!Osc_str__9 !Osc_str__10 !Osc_str__11 !Osc_str__12
!Osc_str__13 !Osc_str__14 !Osc_str__15 !Osc_str__16
!Osc_str__17 !Osc_str__18 !Osc_str__19 !Osc_str__20
!Osc_str__21 !Osc_str__22 !Osc_str__23 !Osc_str__24
!Osc_str__25 !Osc_str__26 !Osc_str__27 !Osc_str__28
!Osc_str__29 !Osc_str__30 !Osc_str__31 !Osc_str__32
!Osc_str__33 !Osc_str__34 !Osc_str__35 !Osc_str__36
!Osc_str__37 !Osc_str__38 !Osc_str__39 !Osc_str__40
!Osc_str__41 !Osc_str__42 !Osc_str__43 !Osc_str__44
!Osc_str__45 !Osc_str__46 !Osc_str__47 !Osc_str__48 ...
What's setting your osc_count? Is that something that would change at run-time or would you know that at compile time?
Ideally it'd be provided at run-time, akin to ssc's answer to related question:  https://kyma.symbolicsound.com/qa/3992/how-to-parse-incoming-osc-messages-of-unknown-length?show=3993#a3993

Using this approach, one can easily set the replicated values whose suffix is greater than !osc_count to 0 or other supplied default value... but then I'm not sure how to get subsequent parameter fields that might refer to the whole "collection" to ignore any unused values at the end of the collection.
OK re-reading SSC's answer, what I understood was that whatever process is at the far end of your OSC connection is going to work out how long the array is and send that length in a separate OSC message. That message then sets the value in !osc_count.

It looks like you could just use the replicator with an appropriate suffix as SSC suggested or use your faderMaker block in a Script and make the maximum allowable number of Sounds. Each Sound then has the test to see if it's within the range set by !osc_count:

(!osc_count ge: ?VoiceNumber) true: !osc_value false: 0
If I'm understanding correctly, how might I use the Script to make the maximum allowable number of sounds INSTEAD of the replicator?

This is sounding more and more like a fruitful approach. I can at least start fiddling around with this and subsequent variations and see what happens. There were too many unknowns in my understanding earlier to begin an attempt without falling down the rabbit hole for a month.. now it seems more like a week, tops... I'd like it to be more like a day.. we'll see.. thanks for the help!
A Script can do the same kinds of things a Replicator can. If you've got a Sound in the input of a Script called "mySound" you can do something like this in the Script:


    (1 to: 10) do: [:i |
        mySound start: 0 s soundNumber: i oscName: ('myName' & i) asHotValue ].


That will create 10 copies of "mySound", starting them all simultaneously at start-up. (I haven't got Kyma turned on right now so I think that should be right, or something very similar).

and then in your Sound you can put the ?soundNumber and ?osc_name variables:

    (!osc_count ge: ?soundNumber) true: ?osc_name false: 0
Hi Thom,
Could you please describe the end-result/behavior you are after? Do you want to populate the Array fields of a StepSequencer? We have a feeling there *may* be a different approach, but we need to understand what your end goal is first.
Thanks for your help!
Yes, that's what I'm looking to do right now, and then after that start populating other values/fields via OSC.  

I got it working by setting EndIndex in the Sequencer to the received !osc_count, though now there's some new questions pertaining to how the VCS interacts with the incoming OSC values.  I want to attach an example of my sound so I'm opening a new question related to this.

BTW, does the different approach you mentioned feature use of the "MIDI voice from Script" object? Because I remember seeing somewhere in the documentation that it may be preferred above other approaches when working with note-level events.  I haven't tried that yet, since I'm much more drawn to using a Sequencer-based approach, to be able to mix values coming from OSC with other sources being generated live within the sound.. although I haven't gotten to that yet so I'm not sure yet what the difference might be.

This reminds me, I also remember seeing somewhere awhile back, some mention of using the Step Sequencer as a data-structure, for a way to set and maintain arrays of values for use elsewhere within a sound.. just as a way of thinking about its possible uses, not as another differently-functioning mode or anything like that.. I'm not sure if there's anything to be gained with this kind of mindset.
You could create an "array" of EventValues (HotValues) using a collect: statement:

    (1 to: 8) collect: [:i| !myHotValue suffix: i]

It's not really an "array" but you could use those EventValues as a kind of data structure. You could put that expression in any number of Sounds and then anything that set any of the values (like OSC or a STGC) would be received by all Sounds that contain that expression.

I don't think you'd need a StepSequencer to do that. The StepSequencer adds the functionality of rhythmically stepping through the values but you don't need that if you're just using it as a data structure.
I've often used "collect:" for writing similar kinds of expressions within a script, while not seeing the simplicity of using a much more direct approach like you've suggested.

Thanks for the clarification.. it increased my understanding of the manner and value in a more direct approach

Please log in or register to answer this question.

...