What to do about asEvent?

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

What to do about asEvent?

jamshark70-2
From here, I'm moving the discussion to sc-dev.

http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/Pspawner-and-Rest-tp7636556p7636605.html

(At this point, it really became more of a dev topic. Also, it's a new topic, not really a reply to the original thread.)

RJK says:

> The more I look at the use of asEvent in relation to Rest, the less I like it.
>
> As far as I understand, it exists to allow EventStreams to consist of both Events and near-Events (SimpleNumber and Rest).
>
> But this always has the potential to break filtering:
> While this works:
>
>         Pseq([(), 1, ()]).play
>
> this doesn't and never will:
>
>         Pset(\dur, 0.3, Pseq([(), 1, ()]) ).play

Hm, currently, this example breaks because Pset does "inEvent = evtStream.next(event);" instead of "inEvent = evtStream.next(event).asEvent;"

But actually, "never will" work is an overstatement. After making that change to Pset:

p = Pset(\dur, 0.3, Pseq([(), 1, ()])).asStream;

p.next(());
-> ( 'dur': 0.3 )

p.next(());
-> ( 'dur': 0.3, 'delta': 1 )

p.next(());
-> ( 'dur': 0.3 )

So this particular point is not really an objection to asEvent.

> Also, the current implementation is incomplete.
> Right now asEvent is invoked in: Ppar, Pfpar, Pfindur, Pstretch, Psync, and Spawner,
> It is not used in: Pgpar or any of the other filter patterns.
> This is a bug waiting to emerge that would touch a lot more patterns to fix.

*That* is the correct diagnosis of the above Pset problem.

> IMHO, EventStreams should stream Events and only Events to guarantee consistentency.

Now this, I totally agree with. It's an accurate diagnosis and it points to the solution. The problem is that we reuse the Stream concept for both data and event streams -- making it the caller's responsibility to convert as needed. Because it's *every* caller's responsibility, we missed many of the potential callers that expect events (the incomplete implementation).

The casual mention of "EventStreams" points to a better approach. We don't have an EventStream class now, but if we did, it could be a stream wrapper that always yields either an Event, or nil. Then patterns that operate on events could do mySourcePattern.asEventStream instead of asStream, and not have to worry about conversion after that.

> 1. A version of Event::silent that is syntactically consistent with Rest?
>         Erest { *new { | dur, inEvent | ^Event.silent(dur, inEvent) } }
>
> 2. The more wild eyed possibility of a Rest wrapper (call it RRest) derived from Event instead of AbstractFunction with 'value' redefined to return the value of the Rest at its 'dur' key.

Hm, in both of these cases, the user would be responsible for knowing the difference between rest-as-value and rest-as-event. I don't quite see that as an improvement. The user should just think "rest" without having to worry about subtle distinctions in the implementation.

> you would need to copy and paste in the entire Operand/AbstractFunction interface

That in itself is a pretty significant objection to the rest wrapper idea.

I think introducing an EventStream class would be a much better way to go. It would be transparent to the user, and, for any developer writing a pattern class, it's easy to explain the difference between asStream and asEventStream.

Proof of concept:

EventStream : Routine {
        next { |inval|
                ^super.next(inval).asEvent
        }
}

+ Pattern {
        asEventStream { ^EventStream({ arg inval; this.embedInStream(inval) }) }
}

In Pset:

                var evtStream = pattern.asEventStream;

And the above example works.

hjh


_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/
search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
Reply | Threaded
Open this post in threaded view
|

Re: What to do about asEvent?

Kuivila, Ronald

> + Pattern {
> asEventStream { ^EventStream({ arg inval; this.embedInStream(inval) }) }
> }
>
> In Pset:
>
> var evtStream = pattern.asEventStream;
>

I like it.
It is much better than putting asEvent wherever a value is taken from a substream.
And it would clarify what patterns define Event streams only.  (It seems to me that a search for
invocations of that method would give you a more or less definitive list.)

But I continue to have this nagging belief that reengineering EventStreams to be Event filters
(e.g., that always return the Event they are passed) would simplify and improve the whole Patterns library.
(In particular, it would simplify the current mess with resource allocation and clean up)  
So, from that perspective, I would prefer to enforce the distinction with Rest and ERest.

I suppose that is a kind of oddball software purism....



Cheers,

RJK



_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/
search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
Reply | Threaded
Open this post in threaded view
|

Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

Kuivila, Ronald
HI James and Julian,

OK, here is a completely different approach to this.

The fundamental problem here is that some objects being embedded in a Stream (SimpleNumber and Rest) need to know when that
Stream is an event stream. Right now, Event streams and value streams both get passed an Event as the argument to next.
Instead, value streams could be passed  an instance of a derived class
EvaluationEvent : Event {}

Then, when Rest::next gets an Event, it would know to write itself into that Event's \dur key and return the Event, since it would get an EvaluationEvent
if it were being used as a value pattern.  So that would loook like:
Rest::next { | ev | (if(ev.class == Event) {^ev.put(\dur, this).yield } { ^this.yield } } 

Searching on patternpairs only turns up Pbind, Pbindf, and Pmono as patterns that would need to be altered.
(Proxies use Pbind internally.)  Perhaps there are other classes that do pattern binding as well. 
If so, I am hoping you guys might identify them.

While streaming, these all make a copy of the input event:
event = inevent.copy;
to allow the untouched original to be returned if a value stream ends.

Instead, they could allocate a single EvaluationEvent outside of that loop and do:
event.clear.parent_(inevent)

Then, if none of the constituent value streams terminated, they would return 
their event in this manner:

inevent = inevent.putAll(event).yield;

This would entail the overhead of copying the EvaluationEvent into the Event partially offset by
not needing to copy the incoming event in the first place.

What do you think?

RJK

PS: Here is what Pbind:embedInStream would look like (changes in bold and italicized)
embedInStream { arg inevent;
var event = EvaluationEvent.new;
var sawNil = false;
var streampairs = patternpairs.copy;
var endval = streampairs.size - 1;

forBy (1, endval, 2) { arg i;
streampairs.put(i, streampairs[i].asStream);
};

loop {
if (inevent.isNil) { ^nil.yield };
event.clear.parent_(inevent);
forBy (0, endval, 2) { arg i;
var name = streampairs[i];
var stream = streampairs[i+1];
var streamout = stream.next(event);
if (streamout.isNil) { ^inevent };

if (name.isSequenceableCollection) {
if (name.size > streamout.size) {
("the pattern is not providing enough values to assign to the key set:" + name).warn;
^inevent
};
name.do { arg key, i;
event.put(key, streamout[i]);
};
}{
event.put(name, streamout);
};

};
inevent = inevent.putAll(event).yield;
}
}
}


Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

jamshark70-2

On December 4, 2017 6:34:46 AM "Kuivila, Ronald" <[hidden email]> wrote:

> HI James and Julian,
>
> OK, here is a completely different approach to this.
>
> The fundamental problem here is that some objects being embedded in a Stream (SimpleNumber and Rest) need to know when that
> Stream is an event stream. Right now, Event streams and value streams both get passed an Event as the argument to next.
> Instead, value streams could be passed  an instance of a derived class
> EvaluationEvent : Event {}
>
> Then, when Rest::next gets an Event, it would know to write itself into that Event's \dur key and return the Event, since it would get an EvaluationEvent
> if it were being used as a value pattern.  So that would loook like:
> Rest::next { | ev | (if(ev.class == Event) {^ev.put(\dur, this).yield } { ^this.yield } }


Rest:embedInStream, right?

I'm going to need some time to think this over. It seems reasonable, but this is not a common usage. It's some effort, and an as yet unknown amount of risk, for something that affects not many people. I think if it were a common case, I'd remember mailing list questions, but I don't. Probably there are more important things to work on.

hjh

Sent with AquaMail for Android
http://www.aqua-mail.com

Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

Kuivila, Ronald

I'm going to need some time to think this over. It seems reasonable, but this is not a common usage. It's some effort, and an as yet unknown amount of risk, for something that affects not many people. I think if it were a common case, I'd remember mailing list questions, but I don't. Probably there are more important things to work on.

Hi James,

This is all arising from the desire to use 1 and Rest(1) in EventStream definitions, which is a brand new usage.
That usage is convenient but encourages confusion about the distinction between value streams and event streams. 
It is currently broken and the fix we have identified will involve changing every pattern that streams sub-patterns.

So, if there are more important things to work on, we should delete the asEvent logic altogether.  
I would be fine with that. We could add Erest as a syntactic convenience and move on.

But, if you are really committed to adding this capability, giving streams the ability to recognize when they are being called by an event stream
is probably the way to go.  It will touch the least and keep the value/event polyvalence confined to the classes that implement it.

Cheers,

RJK




Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

Kuivila, Ronald
Hi again,

Hi James and Julian,

OK, here is a simpler way to signal to Rest it is in a value stream:

Pbind et. al. set a flag in the Event before updating their substreams and reset that flag before yielding the updated Event

So we would have:
+Object { isValueStreamEvent { ^true } }
+Event { var <>isValueStreamEvent = false; }

Then the loop in Pbind would be:

loop {
if (inevent.isNil) { ^nil.yield };
event = inevent.copy;
event.isValueStreamEvent = true;
forBy (0, endval, 2) { arg i;
var name = streampairs[i];
var stream = streampairs[i+1];
var streamout = stream.next(event);
if (streamout.isNil) { ^inevent };

if (name.isSequenceableCollection) {
if (name.size > streamout.size) {
("the pattern is not providing enough values to assign to the key set:" + name).warn;
^inevent
};
name.do { arg key, i;
event.put(key, streamout[i]);
};
}{
event.put(name, streamout);
};

};
event.isValueStreamEvent = false;
inevent = event.yield;
}

And Rest:embedInStream
+Rest {
embedInStream { |ev| if (ev.isValueStreamEvent.not}) { ev = ev.put(\dur, this).yield }
}

But I don't think we would want to add this overhead to SimpleNumber....


RJK

On Dec 4, 2017, at 9:54 AM, Kuivila, Ronald <[hidden email]> wrote:


I'm going to need some time to think this over. It seems reasonable, but this is not a common usage. It's some effort, and an as yet unknown amount of risk, for something that affects not many people. I think if it were a common case, I'd remember mailing list questions, but I don't. Probably there are more important things to work on.

Hi James,

This is all arising from the desire to use 1 and Rest(1) in EventStream definitions, which is a brand new usage.
That usage is convenient but encourages confusion about the distinction between value streams and event streams. 
It is currently broken and the fix we have identified will involve changing every pattern that streams sub-patterns.

So, if there are more important things to work on, we should delete the asEvent logic altogether.  
I would be fine with that. We could add Erest as a syntactic convenience and move on.

But, if you are really committed to adding this capability, giving streams the ability to recognize when they are being called by an event stream
is probably the way to go.  It will touch the least and keep the value/event polyvalence confined to the classes that implement it.

Cheers,

RJK



hjh

Sent with AquaMail for Android
http://www.aqua-mail.com



Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

jamshark70-2
---- On Tue, 05 Dec 2017 01:54:16 +0800 Kuivila, Ronald <[hidden email]> wrote ----
>   OK, here is a simpler way to signal to Rest it is in a value stream:

I would suggest opening this as an issue with a "future" tag. Bug: Embedding Rest in an event-yielding Pseq can play. Wrapping the same event-yielding Pseq in a filter pattern like Pset can't handle the Rest object.

I was trying to hint earlier that this is really not a good time for me to dig deep into this issue. I'm busy.

If it's an e-mail thread, then it needs attention *right now*, or it will get lost. There's a kind of extra urgency to it, somehow.

If it's a github issue, then there's some accountability to come back to it later.

>  Pbind et. al. set a flag in the Event before updating their substreams and reset that flag before yielding the updated Event
>
>   So we would have:
> +Object { isValueStreamEvent { ^true } }
> +Event { var <>isValueStreamEvent = false; }
>
>     Then the loop in Pbind would be:
>  
>   loop {
>  if (inevent.isNil) { ^nil.yield };
>  event = inevent.copy;
> event.isValueStreamEvent = true;

Probably better to save the current value of isValueStreamEvent before clobbering it with `true`, and then restore the old value at the end (the same way that Function:prTry restores the previous exception handler, instead of clearing it).

I actually have a legitimate use case for nested Pbinds (below). If you assume that isValueStreamEvent will always be false when re-entering the Pbind evaluation cycle, then you break nested Pbinds.

>   And Rest:embedInStream +Rest {  embedInStream { |ev| if (ev.isValueStreamEvent.not}) { ev = ev.put(\dur, this).yield }
>  }

Anyway, currently, you can do `Pseq([Rest(1)]).asStream.next` (no input event) and get `Rest(1)`. With the above, you'll get an error. So I guess it could be

        embedInStream { |inEvent|
                if(inEvent.tryPerform(\isValueStreamEvent) ? true) {
                        ^this.yield
                } {
                        ^inEvent.put(\dur, this).yield
                }
        }

>   But I don't think we would want to add this overhead to SimpleNumber....

So, we would fix it only for Rest, but not numbers. I guess I would favor a solution that handles both transparently, without slowing down number-streaming. Needs more thought, which I can't do at the moment.

Coming back to nested Pbinds: I haven't used them in a while, but I have some event prototypes that modularize control signals by allocating bus numbers when the event is played, using those bus numbers for a control-event within the main event, and then automatically freeing the bus(es) once all the synths have n_ended. It's structured roughly like:

(
        instrument: \mySynth,
        freq: 440, dur: ... blah blah ...,
        ctlEv: (
                instrument: \ctlEnv,
                env: Env(.....),
                out: { ~buslock }
        )
)

... where ~buslock is the newly-allocated Bus. I had been generating these events by Pbind(... blah blah..., \ctlEv, Pbind(...)).

If we finally decide to use a flag (about which I have some doubts), then the flag needs to handle recursive usage, or this case breaks.

Also, this is a counterexample to the idea of removing inevent.copy. If Pbind does not copy the incoming event, then ctlEv will be identical to its parent: an infinitely recursive data structure. To prevent this, I would have to add a PcopyInval filter pattern or some such and change my user code: Pbind(... blah blah..., \ctlEv, PcopyInval(Pbind(...))).

I realize currently this is more like daydreaming about code (Glengarry Glen Ross: "We're not TALKING about it, we're just talking about it"). Still, think carefully and move slowly. Doing this wrong has the potential to break a lot.

hjh


_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/
search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

jamshark70-2
---- On Tue, 05 Dec 2017 09:20:41 +0800 <[hidden email]> wrote ----
> >   And Rest:embedInStream +Rest {  embedInStream { |ev| if (ev.isValueStreamEvent.not}) { ev = ev.put(\dur, this).yield }
> >  }
>  
> Anyway, currently...

PS "Anyway" reads a little funny here... I had deleted an irrelevant sentence about formatting and didn't delete the "anyway" following it... no big deal but on review, it does look weird ;)

hjh


_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/
search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

julian.rohrhuber
In reply to this post by jamshark70-2

> On 05.12.2017, at 02:20, [hidden email] wrote:
>
> I realize currently this is more like daydreaming about code (Glengarry Glen Ross: "We're not TALKING about it, we're just talking about it"). Still, think carefully and move slowly. Doing this wrong has the potential to break a lot.

Currently, I would consider using Rests and numbers *in place of* events to be a non-supported half-feature. That is, do not do this at home. I’m surprised it ever worked, but this might be my ignorance!

Nevertheless, in all this, there is a great potential for a pattern system refactoring. There are other topics, such as making patterns value filters, and reconstructing the cleanup system, which seem very promising.

Let’s aim for making the system simpler and more general at the same time. If some feature makes it a lot more complicated, let’s not implement it, because that will make it harder to maintain and extend.



signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

Kuivila, Ronald
Hi guys,

Sure, a delay of game is a good idea.
Can we remove the class extensions at the end of Rest.sc for the release of official release of 3.9? 
That would put the genie back in the bottle while we work this out.

If so, this paragraph in the help file:
A Rest may be used in event patterns to indicate that the resulting event should be a rest (i.e., silent). It should be used in one of the child patterns belonging to a Pbind, for instance.
// do nothing for 2 seconds
(note:Rest(), dur:2).play;

// intersperse pauses in pattern
Pbind(\note, Pseq([0, 4, 7, 11], inf), \dur, Pseq([2, 1, Rest(1)], inf) / 5).play;

Could be altered and expanded to this:
A Rest may be used in the definition of an event pattern (using Pbind, Pbindf and Pmono) to indicate that the resulting event should be a rest (i.e., silent).  
For example:
// do nothing for 2 seconds
(note:Rest(), dur:2).play;

// intersperse pauses in pattern
Pbind(\note, Pseq([0, 4, 7, 11], inf), \dur, Pseq([2, 1, Rest(1)], inf) / 5).play;

For patterns that sequence event patterns, Event.rest can be used to insert rests
For example:

p = Pbind(\degree, Pbrown(-7, 7,2,4), \dur, 0.2);

Pseq([p, Event.silent(1), p, Event.silent(2)], 4).play

Cheers,

RJK

PS: This is what would be deleted or commented out from Rest.sc
+ SimpleNumber {
// Some patterns call .delta on the eventstream's yield value
// since a SimpleNumber is a valid rest, it must answer 'delta' with itself
delta {}
// but Ppar and several other patterns do "put(\delta, ...)"
// so they need to convert the simplenumber into a real rest event
asEvent { ^Event.silent(this) }
}

+ Event {
asEvent {}
}

+ Nil {
// Ppar etc. need stream.next(event).asEvent to be nil when the stream ends
asEvent {}
}

note: 



On Dec 5, 2017, at 4:22 AM, [hidden email] wrote:


On 05.12.2017, at 02:20, [hidden email] wrote:

I realize currently this is more like daydreaming about code (Glengarry Glen Ross: "We're not TALKING about it, we're just talking about it"). Still, think carefully and move slowly. Doing this wrong has the potential to break a lot.

Currently, I would consider using Rests and numbers *in place of* events to be a non-supported half-feature. That is, do not do this at home. I’m surprised it ever worked, but this might be my ignorance!

Nevertheless, in all this, there is a great potential for a pattern system refactoring. There are other topics, such as making patterns value filters, and reconstructing the cleanup system, which seem very promising.

Let’s aim for making the system simpler and more general at the same time. If some feature makes it a lot more complicated, let’s not implement it, because that will make it harder to maintain and extend.



Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

jamshark70-2
On December 5, 2017 22:05:41 "Kuivila, Ronald" <[hidden email]> wrote:
> Sure, a delay of game is a good idea.
> Can we remove the class extensions at the end of Rest.sc for the release of official release of 3.9?
> That would put the genie back in the bottle while we work this out.

A few minutes over breakfast:

If those are new in 3.9, then no harm to remove them. But I'm pretty sure they were in the old Rest system too... which would make it a deprecation. Deprecating late in a beta phase is risky. When I worked in a software company, there was always a "freeze" period before a release: *no* changes to that release except critical bugfixes -- because even well-intentioned, seemingly innocuous changes can break things you didn't expect. So you get it to pass tests and then don't touch it.

FWIW "git blame" turns up:

https://github.com/supercollider/supercollider/commit/366e0090c14b3cc55fcceb37e4ef7f639a9a3ead

Altered EventStreamPlayer:next to use Event:playAndDelta to simplify compatibility with non-event streams
Ron Kuivila committed on Jan 7, 2008

and

https://github.com/supercollider/supercollider/commit/3f59d049d5019e2efc547c921df5120ffb6616ba

added method playAndDelta for use by EventStreamPlayer
Ron Kuivila committed on Jan 7, 2008

So, in fact, the bug has existed for almost 10 years: as far back as 2008, you could write:

Pset(\something, 1, Pseq([patternA, 1, patternB]))

... and get an error when it tried to "put" something into 1. Which leads to a couple of conclusions:

- It's been around for a while. If were going to take it out, we need to deprecate before removing.

- The bug has been there for 10 years, but it never came up until now. So a/ not many people are using it, so probably no harm to deprecate and b/ also no harm to take our time.

(It's still a bad idea to deprecate just before a release.)

> For patterns that sequence event patterns, Event.rest can be used to insert rests
> For example:
>
> p = Pbind(\degree, Pbrown(-7, 7,2,4), \dur, 0.2);
>
> Pseq([p, Event.silent(1), p, Event.silent(2)], 4).play
 
I don't necessarily disagree: I do see the sense behind "If you want an event, write an Event, not a number or Rest." From the user's perspective, the distinction may be merely finicky.

Hm, I guess that's the tension driving SC development, isn't it? On the one hand, we want it to be flexible so that it doesn't interrupt the creative flow too much. It *does* make human sense to write Pseq([patternA, Rest(1), patternB]), and when you're in the flow, it's annoying to have to stop and remember, oh yeah, I can't use a Rest there, I have to use Event.silent. On the other, too much flexibility is hard to maintain. I'll be the first to admit that I don't know what is the right balance.

hjh


_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/
search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
Reply | Threaded
Open this post in threaded view
|

Re: Handling Rest in event streams Re: [sc-dev] What to do about asEvent?

Kuivila, Ronald
>
> Altered EventStreamPlayer:next to use Event:playAndDelta to simplify compatibility with non-event streams
> Ron Kuivila committed on Jan 7, 2008
>

Yes, those additions to Rest are in 3.8 so we can let it hover.

But playAndDelta wasn't added to allow non-events in event streams, it was to allow an eventstream to be streamed and played
without an automatically getting a time back.  My comment was poorly worded.  

RJK







> and
>
> https://github.com/supercollider/supercollider/commit/3f59d049d5019e2efc547c921df5120ffb6616ba
>
> added method playAndDelta for use by EventStreamPlayer
> Ron Kuivila committed on Jan 7, 2008



_______________________________________________
sc-dev mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/
search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/