zhw2590582 / WFPlayer

:ocean: WFPlayer.js is an audio waveform generator
https://wfplayer.js.org
MIT License
262 stars 32 forks source link

Is there any way to load waveform from JSON data generated on a server for larger files of around 2GB or more? #15

Closed bugcoder closed 3 years ago

bugcoder commented 3 years ago

Is there any way to load waveform from JSON? Like passing URL of JSON file or API returning JSON data for waveform:

wf.load(document.querySelector('#video')); //adding support of JSON data as well here along with media URL and element.

The same support is provided by: Wavesurfer (https://wavesurfer-js.org/) https://wavesurfer-js.org/faq/ https://github.com/bbc/audiowaveform

Actually, the browser is getting crash whenever we are passing the URL of a larger video file. Also, when are passing chunks from the server waveform is generating for the first chunk only.

zhw2590582 commented 3 years ago

Yes, actually wf.load method can be loaded AudioBuffer,Or you can parse the PCM data for audio on the server, such as using web-audio-api,Then the front-end loads PCM data through Ajax and simulates it as AudioBuffer, In one of my personal projects, that's how it works:

Node.js:

const fs = require('fs');
const { AudioContext } = require('web-audio-api');
const context = new AudioContext();

context.decodeAudioData(fs.readFileSync('/path/to/audio.mp3'), (audiobuffer) => {
    // choose your channel
    const channelData = audiobuffer.getChannelData(0);
    fs.writeFileSync('/path/to/audio.pcm', channelData);
});

browser:

fetch('/path/to/audio.pcm')
    .then((res) => res.arrayBuffer())
    .then((buffer) => {
        wf.load({
            sampleRate: 44100,
            getChannelData() {
                return new Float32Array(buffer);
            },
        });
    });
salmanAhmad143 commented 3 years ago

@zhw2590582 I am doing the same and trying to load pcm file using wf.load() like above you have mentioned-

`fetch('http://localhost:3000/images/standard-captions.pcm')

.then((res) => res.arrayBuffer())
.then((buffer) => {
    this.wf.load({
        sampleRate: 44100,
        getChannelData() {
            return new Float32Array(buffer);
        },
    });
});`

But it showing me error that -

"The load target is not a string. If you are loading a mediaElement, make sure the mediaElement.src is not empty."

I think this is due to this:

`load(target) {

   if (target instanceof HTMLVideoElement || target instanceof HTMLAudioElement) {
        this.options.mediaElement = target;
        target = target.src;
    }
    errorHandle(
        **typeof target === 'string' && target.trim(),**
        `The load target is not a string. If you are loading a mediaElement, make sure the mediaElement.src is not empty.`,
    );
    this.loader.load(target);
    this.emit('load');
    return this;
}`

Please suggest where i am doing wrong?

Thanks

zhw2590582 commented 3 years ago

I forgot to release the latest version, you now try to install the latest version: wfplayer@1.1.4

salmanAhmad143 commented 3 years ago

Hi @zhw2590582 ,

Thanks for your reply. I have updated wfplayer@1.1.4 version at my end and now the error not showing but the wave not been generating. Only a thin line is drawing in the timeline area. If i am increasing sampleRate value then length of line has been increasing but still wave not generating. See below-

timeline

This is buffer array that i am passing in load() function-

arrayBuffer

I am creating .pcm from .mp4 file using ffmpeg. Please suggest what i am making wrong?

Thanks,

zhw2590582 commented 3 years ago

In fact, WFPlayer does not yet support PCM files generated by ffmpeg, I found that the PCM data generated by ffmpeg and the PCM data generated by AudioContext are very different. I have not found a compatible method, so I suggest you use web-audio-api to generate PCM data. Another point is that the sampleRate option must be the same as the audio sampleRate, otherwise the rendering will be abnormal.

const fs = require('fs');
const { AudioContext } = require('web-audio-api');
const context = new AudioContext();

context.decodeAudioData(fs.readFileSync('/path/to/audio.mp3'), (audiobuffer) => {
    console.log(audiobuffer.sampleRate)
    // choose your channel
    const channelData = audiobuffer.getChannelData(0);
    fs.writeFileSync('/path/to/audio.pcm', channelData);
});