Bug report no stopElement event for oneshots

I have embedded the Javascript player in my application.

I’ve noticed that for oneshots (and only oneshots) I never get a “stopElement” event. I do get the stopElement for e.g. sfx and music. I get a startElement for all element types. But I never get the stopElement for one-shots.

Here’s an example.

Syrinscape - play Wilhelm scream - yes really! (Tavern Brawl)-300-element
SyrinscapeSound.ts:117 Syrinscape - startElement: {elementId: 300, timeToFirstSample: 5.480712890625}
SyrinscapeSound.ts:132 Syrinscape - stopSample: {elementId: 300, playlistEntryId: 1781, sampleId: undefined, timeToStop: 0}
SyrinscapeSound.ts:132 Syrinscape - stopSample: {elementId: 300, playlistEntryId: 1781, sampleId: undefined, timeToStop: 0}

Note that in order to know about the stop event, I’ve had to subscribe to the stopSample. But I really don’t want to subscribe to samples, only elements and moods.

Heya @coop

Just a note here to note this has been noted in case it looks like it hasn’t.

I gather that OneShots, because of their nature (which is that they always stop) do not send a signal to say they have done so.

I gather this IS something that could be changed… dev team is looking at it. :slight_smile:

1 Like

Thanks, Ben!

It can potentially be related to the two “stopSample” events that I get for each stop. I’ve double-checked, and I only have one listener for the stopSample.

stopSample double firing sounds like a bug, does that only happen for OneShots?

That said, stopElement not firing I believe it working as designed. OneShot Elements are different in that they are never “on”, they are triggered and then potentially triggered many times simultaneously, unlike other Elements. If you want to know when the any Element has “stopped making noise”, you probably have to listen to the startSample and stopSample events, since Elements can be “started” but completely silent, and indeed become “exhausted” and never make noise again, much like OneShots do naturally.

I fact, I think the case could be made that OneShot Elements should not fire the startElement event either.

Quite possibly this should be better explained in the documentation, I’ve raised this as a something for the team to consider.

Maybe if you share your use case we can see better see what we should do about it :slight_smile:

Hey @ryan.cassar! Thanks for looking at this!

Yes - stopSample double-fires all the time. And when manually pressing stop, I get an error about “already disposed” for samples (but I currently can’t recreate that, so either I’m catching the error now or I was the one causing it). So I think it’s a general bug.

My use case is simple. My plugin is offering a simplified view and control of the full Syrinscape player. I intentionally don’t care about samples at all. Players start moods or they start elements. They never start nor stop samples.

I’m using the start/stop events to trigger changing the CSS for the play/stop buttons. When I get a startElement event (which happens for SFX, Music and Oneshots) I hide the play button and make the stop button visible. When I get a stopElement event (which happens for SFX and Music), I do the inverse.

Moods I’m handling differently.

What I just noticed while testing is that if I do a “stopAll” call while a one-shot is playing, I do get the stopElement event.

Coop

The “already disposed” warning is generated by us, I believe we just silenced that warning in a recent release of the player, since it appears to be completely harmless.

As for the UI updated for what can and can be started and/or stopped, OneShot Elements can always be started, so no need to update the UI when they raise startElement.

Triggering stop all (or changing Moods to something that does not include that OneShot) while a OneShot is playing is something of an edge case I also raised with the team. I’m not convinced they should raise stopElement in this case, specifically because it introduces a potentially quite surprising inconsistency with naturally ending OneShots not triggering stopElement.

Hope that all makes sense, feel free to continue asking for clarification (internal discussions can still change anything I have claimed here of course, I’ll try to remember to update the thread in that case, but in general probably best to not rely on stopElement events from OneShots and especially not the double stopSample events being doubled).

The feedback I got from users was they wanted visual feedback to know what was playing. And yeah, the ability to “multi-fire” a oneshot was something I just accidentally got rid of when I added the “play switches to stop” feature.

However, the request still stands. What I’m going to do is make it so that the play button is highlighted while the oneshot is playing. So you can still press play button again, but you’ll see that it’s playing.

You can also argue that if you have both startElement and stopElement events that get fired, for every startElement event, there should be a stopElement event. Where things are whacky right now - in my opinion - is that you send out startElement events for oneshot but only occassionally send out stopElement events. I think it should be either always send startElement and stopElement (which is what I’m asking) or don’t send either event (which, while consistent, would require me to rework my UI a bit).

But, based on your input and me thinking about things, I may just change the “play button turns into a stop button” mechanism of moods, sfx and music to a radio button/slider.

And, at the end of the day, you guys own the API. If it’s working as designed, I’ll adjust how I use it. Well, I already have a workaround. It just means that the app isn’t as efficient as it could be.

I think everything you’re suggesting agrees with what I was saying, except that you’re sitting on consistency by always raising stopElement, and I’m sitting on consistency by never raising stopElement (and perhaps also never raising startElement). Where OneShot Elements are significantly different is user intent - No user interaction stops a OneShot Element (other than as a side effect of Stop All, or a Start Mood). I believe startElement and stopElement are supposed to only happen in response to a user interaction.

At some point in the past, we had OneShots be highlighted as “playing” for some fixed number of seconds after the last start trigger on that Element. This approximated “it’s currently playing”, while leaving the UI clickable (which seems to be what you’ve landed on also). I think we now show the playing highlight based on the length of the Sample the Element chose to play, but I could easily be wrong about that. In any event, you can go with a fixed time period (that is bumped on every startElement), or listen to the startSample/stopSample events instead (which should work for all Elements).

Since it is school holidays in Sydney/Australia, and everyone on the dev team is a parent, we are actually all on leave at the moment, but something we can discuss when that’s all over. No guarantees we will change anything in the short term though!

No guarantees expected! I know how development shops work.

Now - go enjoy your family!

p.s. on the topic of consistency - the updateConfig doesn’t have a removeListener method. So I’m having to remove my listeners from the underlying array. Not a huge issue, but it breaks encapsulation. :smiley:

It looks to be undocumented, but it seems the addListener method returns the corresponding removeListener function, so it would be up to you to hold onto that to call it when you want. That said, it does not seem to remove the listener from the listeners array, although that should not do any harm, it is a little odd. Indeed, I don’t know that removing it from the listeners array will do anything, a quick look suggests all that does is avoid the listener getting removed when the event is deleted (which probably never happens), I suspect the listener will still be “listening” to the event.

Just catching myself up on this thread. Here are some notes on the design intent:

  • Elements can be “on” (except for oneshot elements) and/or “making sound” (including oneshot elements, e.g. playing a sample).

  • “on” elements are blue in the Master Interface.

  • “making sound” elements have an amplitude glow and button visualisation in the Master Interface.

  • Elements (including oneshot elements) can be directly started or indirectly started via mood start, which trigger a startElement event.

  • Elements (except oneshot elements) will be “on” after being started.

  • Once “on”, elements never automatically stop without direct or indirect user action unless exhausted (playlist set to not repeat).

  • Oneshot elements will never be “on”. They can be started multiple times overlapping.

  • Oneshot elements cannot be directly stopped in the Master Interface because we would need an additional different UI to support both multiple triggers and stop. But they can be directly stopped via API, which triggers a stopElement event.

  • Oneshot elements can be indirectly stopped via stop all or mood stop, which trigger a stopElement event.

You should consider the start* and stop* events to indicate “I’ve been told to start FOO” and “I’ve been told to stop FOO”, rather than “I have started FOO” and “I have stopped FOO”.

If you want to visually indicate that an element is “making sound”, regardless of its “on” state and including oneshots, you should look at the visualisation system.

Our documentation describes its use it to create a global visualiser from the global analyser node. But we also use it in the Master Interface for per element visualisers from individual element analyser nodes. If you’re interested, I can help you with that over email.

Note that in general use (not SoundSet creation) our Master Interface also generally does not concern itself with samples, either. Users typically start and stop moods and elements.

In the past, when oneshot elements did not have artwork, we used to turn them blue when triggered (like an “on” element) and then immediately (after 0.5s) reset to the grey button state to indicate it could be triggered again. Now we rely on the element visualisation to indicate that a oneshot is “making sound”.

One other thing we use in the Master Interface is the animated ring around each element which indicates “time to stop or next sample”. When you receive a startElement event you get timeToFirstSample. If this is non-zero, then there is a delay until the first sample and we show the ring as a countdown to the first sample start.

Then with each startSample event you get timeToNextSample (including any delay between samples) and timeToStop (when that sample will stop “making sound”). Sample duration should approximately equal timeToStop.

If you don’t want to use the visualisation system, you could use these timeTo* values to implement a simple visualisation with timers, without removing the ability to trigger multiple overlapping oneshots. You’ll just need to be careful to clear existing timers when a new event comes in for that element.

The visualisation system is the best way to determine what is actually being heard in the mix without having to worry about maintaining synchronised state.

Just on the double stopSample issue, it is expected that stopSample is sent once when a sample is told to stop (e.g. as a result of stop all or stop mood) with timeToStop and again when it has actually stopped with timeToStop: 0 (indicating that it has in fact finished stopping). This is because stop as a result of a user request happens with a 3s fade out, so the UI can show that a sample is no longer “active” as soon as it is told to stop, and then show when it is finally and completely done.

That said, I can see in your logs that you have two stopSample events both with timeToStop: 0, and I can see that happening in our Master Interface as well. This does look like a bug. Probably a relatively harmless one, but still a bug.

I also note that our own docs imply that the start/stop events are “when a FOO is started/stopped” and not “when I’m told to start/stop a FOO”, contrary to my advice above.

But it would still be potentially awkward to send stopElement when a oneshot sample ends. Because oneshots can have overlapping samples, it would not be correct to say that the element is stopped when one sample ends when there are more overlapping samples still playing. So if we were to consider sending stopElement for oneshots, we might need to send it only one time when the last sample has stopped. So there would still not be a 1-1 relationship between startElement and stopElement.

We will add this to the list to consider.