katspaugh/wavesurfer.js

Wavesurfer incorrectly destroyed / recreated (RangeError: invalid array length)

grudus opened this issue · 4 comments

Bug description

Hi
I have problem with wavesurfer after I destroy it and recreate on a page. Consider the following scenario:

  1. User clicks the button to start recording (record plugin) -> initializing: WaveSurfer.create and RecordPlugin.create
  2. User clicks the button to stop recording -> calling waveSurfer.destroy and recordPlugin.destroy
  3. User clicks the button to start recording again -> initializing: WaveSurfer.create and RecordPlugin.create.

Now what happens: When I reacreate the wavesurfer and try to draw it (3) I got RangeError: invalid array length exception in the renderMultiCanvas.draw function and the page freezes.

After some debugging I found out there is a problematic code:

// renderMultiCanvas function

const numCanvases = Math.ceil(totalWidth / singleCanvasWidth);
        // Render all canvases if the waveform doesn't scroll
        if (!this.isScrollable) {
            for (let i = 0; i < numCanvases; i++) {
                draw(i);
            }
            return;
        }

For some reason singleCanvasWidth is 0 so it draws infinitely. I can temporarily fix my use case by doing const numCanvases = Math.ceil(totalWidth / singleCanvasWidth || 1); but I know that's not the root case.

Additionally, if I add console log inside the renderMultiCanvas I can see it is invoked constantly even when I destroy the wavesurfer instance.

Environment

  • Browser: Chrome
  • Next js (12.2.5)
  • wavesurfer.js (7.8.4)

Minimal code snippet

(Some code omitted to make it clearer)

    const visualisationRef = React.useRef<HTMLElement>(null)
    const waveSurfer = React.useRef<WaveSurfer>()
    const recordPlugin = React.useRef<RecordPlugin>()

    const cleanup = () => {

        if (recordPlugin.current) {
            if (recordPlugin.current.isRecording()) {
                recordPlugin.current.stopRecording()
            }
            recordPlugin.current.stopMic()
            recordPlugin.current.unAll()
            recordPlugin.current.destroy()
            recordPlugin.current = undefined
        }

        if (waveSurfer.current) {
            waveSurfer.current.empty()
            waveSurfer.current.unAll()
            waveSurfer.current.destroy()
            waveSurfer.current = undefined
        }
    }

    const initialize = () => {
        cleanup()

        waveSurfer.current = WaveSurfer.create({
            container: visualisationRef.current,
            barAlign: 'bottom',
            barWidth: 5,
            barRadius: 6,
            waveColor: '#fff',
            height: 48,
            cursorWidth: 0,
            barHeight: 4,
            barGap: 2,
            fillParent: true,
            hideScrollbar: true,
            interact: false,
            autoplay: false,
            mediaControls: false,
        })

        recordPlugin.current = waveSurfer.current.registerPlugin(
            RecordPlugin.create({
                renderRecordedAudio: false,
            })
        )
    }

    const startVisualisation = () => {
        initialize()
        recordPlugin.current?.startRecording()
    }

Expected result

Library and plugin is correctly unmounted and it is possible to clean and rerun the initialization