react-wavesurfer
react-wavesurfer copied to clipboard
Keeping waveforms in sync with global player
In the wavesurfer.js
gitter chat someone asks:
If I want to display a bunch of waveforms on a page
and have a central player instance [what do I do?]
His solution is to have one single page-wide instance of a player which will sync with the currently selected track (and waveform)
. That seems to make sense. From issue #10 I can see how to get a reference of the wavesurfer
object, but I'm not quite sure how to create dummy players with this lib. I want one smart wavesurfer obj which can have global state and then a bunch of dummy players which modify that global state. How might this be achieved?
Update:
I have tried to achieve this leader -> follower relationship by simply creating two components which share state as follows:
<Wavesurfer
audioFile={demoAudio}
pos={this.state.pos}
onPosChange={this.handlePosChange}
playing={this.state.playing}
/>
<Wavesurfer
audioFile={demoAudio}
pos={this.state.pos}
onPosChange={this.handlePosChange}
playing={false}
/>
However, what happens here is there are some subtle audio glitches (pops in the background). I tried removing wavesurfer 2
's onPosChange
property and this fixed the issue! However, that meant that you could no longer control wavesurfer 1
with wavesurfer 2
's visualization. I'll keep updating as I get closer to solving this issue.
After further investigation, I think I've narrowed down the scope of my question. What I'm really asking is:
How can you handle two wavesurfer objects/visualizations playing the same song while maintaining the same state? When I try to do this, using the method shown above, I hear crackling noises in the background.
Just reproduced this problem in a fork of the project: https://github.com/karlfloersch/react-wavesurfer -- I added it to the 'simple.js' example.
I think I understand the issue. The reason the popping happens is because it calls handlePosChange()
more than once for the same time moment on the 'leader' timeline. That causes it to go backwards a millisecond or two, creating the popping noise. You can test this by printing e.originalArgs[0]
in handlePosChange()
and noticing that it sometimes prints out the same number 3 times in a row.
I must be updating the leader's state in the wrong way. Though I'm not sure what the best practice is in this case.
I've figured it out. In my fork of this repo ( https://github.com/karlfloersch/react-wavesurfer ), I've added an onSeek
prop. This will be triggered every time you seek, but not when the position changes. I use the undocumented method found here: https://github.com/katspaugh/wavesurfer.js/issues/420
The event has the position (in seconds). This makes it super easy to update the "leader" player when a click event is fired on the follower players.
I may submit a PR later when I've cleaned up my code to add the onSeek
prop.
Hello @karlfloersch β sorry for the late reply.
The onSeek
callback prop is already present in the package, all public Wavesurfer events are added with onβ¦
prepended. The handlePosChange
was added by me to reconcile the differences in the formatting of seek
and audioprocess
events. (see here https://github.com/mspae/react-wavesurfer#onposchange-function)
Unfortunately right now I can't test the popping sound issue you describe since I don't have a speaker. I've put together a pen so we have some code we can talk about: http://www.webpackbin.com/VJp8yYYK-
I would consider adding an onClick
callback prop to the lib to hook into the undocumented event you describe.
Hi @mspae, no worries π
Apologies! I meant onClick
not onSeek
.
See this for what I added: https://github.com/karlfloersch/react-wavesurfer/blob/master/src/react-wavesurfer.js#L104
The popping issue had to do with me needing to bind to onClick
instead of something like onPosChange
. Basically the problem was both waves were updating each other, causing a slight stutter. Now when I set the position of both waves using onClick
it updates and there is no problem.
Ah, I understand. Okay, so a PR would be great for the onClick
prop but maybe we can have the parameters e
and progress
of the click event callback passed to the callback prop in the array originalArgs
. (Since those are the original Args) The progress formatted with _posToSec
could be passed as a posInSec
parameter. So the resulting signature would read:
{
wavesurfer,
originalArgs: [e, progress],
posInSec
}
EDIT: Just read on and saw https://github.com/karlfloersch/react-wavesurfer/blob/master/src/react-wavesurfer.js#L122 β where the formatted pos is passed to handlePosChange in the originalArgs array (thereby contradicting what I said above about only passing originalArgs :tongue: β so maybe we can just pass the formatted position after all (but also with the event) in the originalArgs array. (originalArgs: [e, this._posToSec(progress)]
)
@karlfloersch my CPU jumps through the roof when I have a bunch of Wavesurfer components like that since there's a ton of dummy audio players then--how did you circumvent that?