-
Notifications
You must be signed in to change notification settings - Fork 5
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
Wrapping PlayControl #3
Comments
Here is an example of what Im trying to do (Only works in Chrome on Desktop). The waveforms have been split up by chords and played through segment engine. So it sort of works... the problem is if for instance if looping is set on the wrap Player, then the tempo of the wrapped Player is wrong during first run, after it loops it gets it right. Another issue is that if I try too loop just part of the audio buffer, it will play that part but then continue in silence for as long as the audio buffer is, ignoring the loop boundaries. So I'm not sure if the way I wrap play controls this way is supported? |
Here's a codepen showing the speed problem. http://codepen.io/mdahlgrengadd/pen/Gmxrjr Will try to do a codepen also showing the second issue. |
First of all, apparently there are a couple of bugs (e.g. #2) that we have to fix very rapidly. Thanks – also in advance – for all simplified code examples that help to identify the bugs. |
Hm, thinking about this ones more, I cannot make really sense out of a PlayControl that has its own speed. The whole idea of a speed controlled engine (like a PlayControl added to a Transport) is that its speed (and position) is controlled by the master – and the set speed method of the PlayControl should be disabled (i.e. could throw an exception). This means that the bug is the other way around: the speed should always be the speed of the master. This may raise the question why we created the PlayControl as a TimeEngine that implements the speed-controlled interface. Apart from a portion of sophism, the answer is that the PlayControl could serve as an interface between a TimeEngine that does only implement the transported and/or scheduled interface and a master that only accepts TimeEngines that implement the speed-controlled interface – even there is none that I would know of. |
Ones said the above, I have to admit that there is a potential and unimplemented feature of the Transport – a "stretch" parameter – that would control a relative speed for each individual engine added to a given Transport. |
Now, there is another (actually implemented :-) feature that allows for defining an "offset" to the internal position of a TimeEngine added to a Transport. This offset is the 4th argument of Transport.add(engine, startPosition = 0, endPosition = Infinity, offsetPosition = 0). While "startPosition" and "endPosition" place (and cut) the added TimeEngine on the Transport's time line, "offsetPosition" would define the actual position inside the TimeEngine (e.g. the SegmentEngine) according to the following calculation: position = transportPosition - startPosition + offsetPosition. The same parameters can be controlled by the method setBoundaries of the object returned by Transport.add, the "Transported". |
The second comment of this issue says:
In fact, it seems to work in the example in the third comment. The code that makes this work actually looks good. |
The speed issue is fixed by removing the possibility set the speed of a "wrapped" PlayControl. |
To try to address all questions that have been raised in this issue... |
Thanks @NorbertSchnell for superb answer!
The way I first thought it would work, was as a relative speed to its master (now it sets absolute speed), and that would have been very useful. For example: Currently the segment engines have sample buffers recorded at 60 bpm, If I want to play them together in the same transport with a play engine with sample recorded at say 108 bpm, they would of course be out of sync. Setting speed of a segment engine wrapped in a play control to 1.8 would play them in sync.
Yes. I think it could be very useful, especially if speed will be disabled in play control. |
Sorry my mistake, your right! With drumloop With a chord buffer |
In order to clearly define a loop when cyclic is enabled there would have to be duration parameter or a loopStart/End couple. The last marker is not working when marker (i.e. segment) durations are given explicitly. I can see the following solutions here:
I don't like (1.) since we end up with a Matryoshka and, in addition, the Transported returned by Transport.add is already almost the same as a PlayControl, but less complex. Yes, my favorite is (3.). What do you think @mdahlgrengadd? |
@NorbertSchnell Well, I totally agree that nr 1 feels strange and since you don't like it, I guess its not an option :-) One benefit is that you can easily change relative speed even after the add to transport. Would that be possible with the other solutions? Maybe its best I describe what I want to do. The scenario I'm trying to achieve is to have a solo instrument recording "followed" by a rhyhm accompainment. And also, the other way around... to "fit" a solist recording to a rhythm (or another solo performance). Seeking on a master transport should then find the correct positions in both solo and rhythm, and changing master speed would slow down engines so they still sync. To achieve this, engines added to transport would have to have individual "stretch" possibility that can be changed during playback, because solo performance speed would fluctuate over time.
So with this option would it be possible to change stretch anytime without "retriggering" segment? |
Hmm, struggling with this. So do I create i big markerbuffer of all chords?
The decimal was required because otherwise there was a double triggering of chords (both previous segment and current I think). Also negative offset? Is this correct? (Not tried the devel branch, though) |
Would you mind putting an example of this on codepen? |
Sorry, of course! http://codepen.io/mdahlgrengadd/pen/NjMNJb In this example the "decimal" thing wont help, because then I would need to add an extra segment with 0 duration at each new chord position in the markerBuffer (so this sounds like its the already mention "segment 1-2"-bug.) You can also hear a "retriggering" of a segment when seeking, maybe that is also fixed in dev-branch. Will try soon. |
Ouch, this is my double fault: 1: I got the design wrong, 2: I forgot about it... |
@NorbertSchnell Just one question :-) I'm using your suggestion of moving the segment engine on the transport with the transported object returned from transport.add(). Since the segment engine has to be ready with the next chord on correct beat (or it will miss triggering the first segment), I move the engine after the last played segment. If I dont add a little "headroom" to the segment engine boundary, it will stop...
Heres a pen: I know its naive implementation, but still don't understand why the extra "headroom" is needed? |
Well, you are abusing :-) positionDisplay.syncPosition, which is called in series with the syncPosition method of your SegmentEngine to call setBoundaries, which will cause another syncPosition of your SegmentEngine – just before or after. The expected behaviour is undefined and I am not surprised that this actually doesn't work (even if it could by accident). Even better would be to create an extension of the SegmentEngine that reimplements its syncPosition method to generate the offset and use it in seeking by calling super.syncPosition. In addition, the extension would reimplement advancePosition and add the offset before calling super.advancePosition. (P.S.: Referring to the comments I just deleted: sorry, I did not open the right example.) |
Ok, sorry! this was only for looping to "work". You can remove those lines in syncPosition, the other problem still exist. Look at line 91-92 in this updated code pen: http://codepen.io/mdahlgrengadd/pen/aWGaKm Anyway, I will code it like you suggest instead. Just thought this could be something wrong in segment engine. |
Apologies for not understanding the code @NorbertSchnell. I managed to bypass ( or hack :-) the unexpected halting of segment engine on transport doing this:
So now it acts as more or less like how I expect (not the same as correct, though). Here's a codepen with a "sequencer" subclassed from segment engine doing this :-) |
No, no, the problem is clearly not in the SegmentEngine, but somewhere in the Transport. Its related to a confusion between jumping back and stopping. Stopping would be ok if it restarted right away when the position loops back – which it doesn't. Given that you are any way jumping back, the 14 is ok, but 12.00001 is also enough. The most important is not to stop before the jump and an elegant value should be Infinity. |
Hm, not a good hack. I'll try to make a proposition in code next week. |
Hi! I'm doing sort of a sequencer and wondering if I can add a playControl to a transport that itself is controlled by a "master" play control?:
The reason for doing it is so that during playback the wrapPlayer engine can be replaced and playback in the new engine continues from current position (like in the PlayControl example).
The text was updated successfully, but these errors were encountered: