react-wavesurfer icon indicating copy to clipboard operation
react-wavesurfer copied to clipboard

Keeping waveforms in sync with global player

Open karlfloersch opened this issue 8 years ago β€’ 7 comments

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.

karlfloersch avatar Jul 04 '16 17:07 karlfloersch

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.

karlfloersch avatar Jul 06 '16 22:07 karlfloersch

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.

karlfloersch avatar Jul 11 '16 02:07 karlfloersch

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.

karlfloersch avatar Aug 04 '16 15:08 karlfloersch

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.

mspae avatar Aug 14 '16 15:08 mspae

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.

karlfloersch avatar Aug 15 '16 01:08 karlfloersch

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)])

mspae avatar Aug 15 '16 08:08 mspae

@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?

TrevorHinesley avatar May 10 '17 22:05 TrevorHinesley