Giter Site home page Giter Site logo

zhw2590582 / wfplayer Goto Github PK

View Code? Open in Web Editor NEW
257.0 5.0 32.0 20.48 MB

:ocean: WFPlayer.js is an audio waveform generator

Home Page: https://wfplayer.js.org

License: MIT License

JavaScript 98.53% TypeScript 0.55% Less 0.92%
waveform generator canvas audiocontext audio

wfplayer's Introduction

WFPlayer

version license size

WFPlayer.js is an audio waveform generator

Screenshot

Demo

Checkout the demo from Github Pages

Features

  • Create waveforms without loading the entire media file
  • Customize cursor, progress, grid, ruler display and color
  • Support for loading media urls and loading media dom elements (video tags and audio tags)
  • Support for real-time change options like color or width etc
  • Listen to the playback state of media elements for playback animation
  • Adaptive parent element size and audio data
  • And more...

Install

Install with npm

$ npm install wfplayer

Or install with yarn

$ yarn add wfplayer
import WFPlayer from 'wfplayer';

Or umd builds are also available

<script src="path/to/wfplayer.js"></script>

Will expose the global variable to window.WFPlayer.

Usage

HTML

<div id="waveform" style="width: 1000px; height: 300px"></div>
<video id="video" src="path/to/video.mp4"></video>

<!-- or -->
<audio id="audio" src="path/to/audio.mp3"></audio>

JS

var wf = new WFPlayer({
    container: document.querySelector('#waveform'),
    mediaElement: document.querySelector('#video'),
});

wf.load(document.querySelector('#video'));

// or
wf.load('path/to/audio.mp3');

API

Options

var wf = new WFPlayer({
    // Mount the audio waveform of the dom
    container: '#waveform',

    // Whether to use scroll mode
    scrollable: false,

    // Media element like: video tag or audio tag
    mediaElement: null,

    // Whether use web worker
    useWorker: true,

    // Thw refresh delay time
    refreshDelay: 50,

    // Whether to display wave
    wave: true,

    // Waveform color
    waveColor: 'rgba(255, 255, 255, 0.1)',

    // Background color
    backgroundColor: 'rgb(28, 32, 34)',

    // Whether to display cursor
    cursor: true,

    // Cursor color
    cursorColor: '#ff0000',

    // Whether to display progress
    progress: true,

    // progress color
    progressColor: 'rgba(255, 255, 255, 0.5)',

    // Whether to display grid
    grid: true,

    // Grid color
    gridColor: 'rgba(255, 255, 255, 0.05)',

    // Whether to display ruler
    ruler: true,

    // Ruler color
    rulerColor: 'rgba(255, 255, 255, 0.5)',

    // Whether to display scrollbar
    scrollbar: true,

    // Scrollbar color
    scrollbarColor: 'rgba(255, 255, 255, 0.25)',

    // Whether to display ruler at the top
    rulerAtTop: true,

    // Pixel ratio
    pixelRatio: window.devicePixelRatio,

    // Which audio channel to render
    channel: 0,

    // Duration of rendering
    duration: 10,

    // The ratio of spaces on both sides
    padding: 5,

    // Waveform height scale ratio
    waveScale: 0.8,

    // Waveform Size ratio
    waveSize: 1,

    // Waveform cursor display position, default is center, optional values are: left , center
    waveAlign: 'center',
});

Instance methods and properties

Load target:

// The target can be the url address of media or a mediaElement or ArrayBuffer or Audiobuffer
wf.load(target);

Change Channel:

wf.changeChannel(1);

Jump to a certain time:

wf.seek(second);

Jump to a certain time with smooth:

wf.smoothSeek(second);

Export image:

wf.exportImage();

Modify option:

wf.setOptions({
    // Like change wave color
    waveColor: 'red',
});

Destroy instance:

wf.destroy();

Common Problem

When decoding a video to an audio waveform, it will cause insufficient browser memory.

If the video volume is too large, it will cause the front-end decoding difficult. Best practice is to use the server's FFMPEG, convert the video into audio format MP3.

-ac is the number of channels, -ar is a sample rate:

Back End

ffmpeg -i path/to/video.mp4 -ac 1 -ar 8000 path/to/audio.mp3

HTML

<div id="waveform" style="width: 1000px; height: 300px"></div>
<video id="video" src="path/to/video.mp4"></video>

JS

var wf = new WFPlayer({
    container: document.querySelector('#waveform'),
    mediaElement: document.querySelector('#video'),
});

wf.load('path/to/audio.mp3');

// or
fetch('path/to/audio.mp3')
    .then((res) => res.arrayBuffer())
    .then((arrayBuffer) => {
        const uint8 = new Uint8Array(arrayBuffer);
        wf.load(uint8);
    });

If you really don't want to use the server to transfer, I recommend to use @ffmpeg/ffmpeg

npm i -S @ffmpeg/ffmpeg

HTML

<div id="waveform" style="width: 1000px; height: 300px"></div>
<video id="video" src="path/to/video.mp4"></video>
<input type="file" id="file" />

JS

import FFmpeg from '@ffmpeg/ffmpeg';

const wf = new WFPlayer({
    container: document.querySelector('#waveform'),
    mediaElement: document.querySelector('#video'),
});

document.getElementById('file').addEventListener('change', async (event) => {
    const file = event.target.files[0];
    const { createFFmpeg, fetchFile } = FFmpeg;
    const ffmpeg = createFFmpeg({ log: true });
    await ffmpeg.load();
    ffmpeg.FS('writeFile', file.name, await fetchFile(file));
    await ffmpeg.run('-i', file.name, '-ac', '1', '-ar', '8000', 'audio.mp3');
    const uint8 = ffmpeg.FS('readFile', 'audio.mp3');

    await wf.load(uint8);
})

How to add a custom event

var wf = new WFPlayer({
    container: document.querySelector('#waveform'),
    mediaElement: document.querySelector('#video'),
});

wf.load('path/to/audio.mp3');

// click event
wf.on('click', (currentTime) => {
     wf.seek(currentTime)
});

// grab event
wf.on('grabbing', (currentTime) => {
    wf.seek(currentTime)
});

// scroll event
wf.on('scroll', (deltaY) => {
    wf.seek(wf.currentTime + deltaY / 10)
});

How to get currentTime from an event

var waveform = document.querySelector('#waveform')
var wf = new WFPlayer({
    container: waveform,
    mediaElement: document.querySelector('#video'),
});

wf.load('path/to/audio.mp3');

waveform.addEventListener('mousemove', event => {
    const currentTime = this.getCurrentTimeFromEvent(event);
    console.log(currentTime);
})

waveform.addEventListener('click', event => {
    const currentTime = this.getCurrentTimeFromEvent(event);
    console.log(currentTime);
})

Donations

We accept donations through these channels:

QQ Group

QQ Group

License

MIT © Harvey Zack

wfplayer's People

Contributors

extremlapin avatar huanghaiyang avatar moritz avatar zhw2590582 avatar zolero avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

wfplayer's Issues

getting audio infinity

image

import { useEffect, useMemo, useState } from "react";
import WFPlayer from "wfplayer";
import useTranscriptionRef from "./useTranscriptionRef";
interface Props extends Partial<WFPlayer> {
  url: string;
}
const useWFPlayer = (
  containerRef: React.RefObject<HTMLDivElement>,
  options: Props
): WFPlayer | null => {
  const [wavesurfer, setWavesurfer] = useState<WFPlayer | null>(null);
  const { waveSurferRef } = useTranscriptionRef();
  const wfPlayerProps = useMemo(() => {
    return options;
  }, [options]);
  useEffect(() => {
    if (!containerRef.current || !options?.url) return;
    console.log(WFPlayer);
    const wf = new WFPlayer({
      ...wfPlayerProps.config,
      container: containerRef.current,
    });
    if (!!wf) {
      wf.load(wfPlayerProps?.url);
      waveSurferRef.current = wf;
      setWavesurfer(wf);
    }
    wf?.on("grabbing", (currentTime: any) => {
      wf?.seek(currentTime);
    });
    return () => {
      if (wf) wf?.destroy();
    };
  }, [wfPlayerProps?.url, containerRef?.current]);

  return wavesurfer;
};
export default useWFPlayer;

Large file issue

Hi @zhw2590582

I am trying to load the wfplayer for large files like audio with duration 1.5-2 hours. It is leading to memory explosion.
I found the Issue as well as your instruction on the common problems (Links mentioned below)

https://github.com/zhw2590582/WFPlayer#common-problem ,
#19

I am already using ffmpeg -i path/to/video.mp4 -ac 1 -ar 8000 path/to/audio.mp3 on server to reduce the size of mp3. What more can I do to check why wfplayer is crashing?

Need help with making the scrolling smoother

Hi, @zhw2590582

I have a view of the waveform with the first 10 seconds. When my seeker reaches the rightmost point, the view of the waveform jumps from 0-10 seconds to 10-20 seconds. Is there any way that I can change the view by, let's say, second by second?

Thanks.

Is their any way i can show the horizontal scroll bar

Hi @zhw2590582

I am stuck in a situation where i need to show the horizontal scroll bar at the bottom of the waveform. So the user can scroll and can easily see the rest portion of wave form by simply scroll left and right. So please tell me how can i do this?

Thanks Salman,

Need help with setting up the subtitles on the waveform

So, I need to add some subtitles to my waveform shown as in the screenshot attached below. In the following screenshot, I am using wfplayer version 1.1.4. I recently updated the wfplayer version to 2.1.3. To calculate the subtitle position according to the grid, I was using two values: this.wf.drawer.gridGap and this.wf.options.pixelRatio. Can you please tell me where I can find these values? Have you changed the path to these values or removed them? If yes, then in wfplayer version 2.1.3, how do you calculate subtitle position?

wavefrom

Generate waveform while audio is playing

Is there a way to generate waveform while playing an audio.

    <div id="waveform" style="width: 1000px; height: 300px"></div>
   <audio id="audio" src={{ url_for('static', filename="audio_0.5.mp3") }}></audio>
   
 <script type="text/javascript">


var wf = new WFPlayer({

                  container: document.querySelector('#waveform'),
                  mediaElement: document.querySelector('#audio'),
              scrollable: true,
              useWorker: true,
              refreshDelay: 50,
              wave: true,
              waveColor: 'rgba(255, 255, 255, 0.1)',
              backgroundColor: 'rgb(28, 32, 34)',
              cursor: true,
              cursorColor: '#ff0000',
              progress: true,
              progressColor: 'rgba(255, 255, 255, 0.5)',
              grid: true,
              gridColor: 'rgba(255, 255, 255, 0.05)',
              ruler: true,
              rulerColor: 'rgba(255, 255, 255, 0.5)',
              rulerAtTop: true,
              pixelRatio: window.devicePixelRatio,
              channel: 0,
              duration: 10,
              padding: 5,
              waveScale: 0.8,
              
              });



   wf.load('static/audio_0.5.mp3');

I tried the above code, couldn't seem to generate it. Could you help me on this.

Feature Request: do not scroll until the end / some kind of padding

I'd love to have an option for the cursor to jump to the next page slightly before it reaches the end of the current page.

My use case is that I want to annotate songs with their lyrics for creating Karaoke playbacks. Currently if the current page ends at 10 seconds, and I want to align a word (or a line) so that it starts 9.8 seconds, there is no way to see the full waveform for the current word.

adding grab_start and grab_end events

Hi. I'd love to have grabbing_start and grabbing_end events added to pause my audio during the time i'm holding mouse, because now when i stop moving mouse it plays my audio.
I'm not using html tag, I've created audio in js code.

Add a scroll or pan option

I'd like to be able to have the user be able to scroll or panDrag through the waveform to help them find a location they'd like to seek to. Easy to add as an option?

Could you please add a speed-changing function?

I'm going to make a player like music speed changer which is a good music player.
For now, WFPlayer nearly has all the functions I want, except a speed-changing function. In my opinion, it might be called like wf.seek(second);

Thank you for your great job.

preload option availability

Is there a possibility of removing the delay while the waveform is being generated, as the audio in my case is fetched from server.
The audio is playing but the the waveform corresponding to the first 2 beats is delayed.
I have added preload="auto" to audio tag.

Browser crash on loading video/audio of duration around 2 hours

We are using mp3 file of 7.6 MB with duration around 2 hours. WFPlayer is working fine with less duration audio files and not using much memory. However, whenever we are using audio with large duration such as 2 hours, it's consuming around 4 GB of memory and keep on using more memory and browser get crash eventually.

We are using below code:

var wf = new WFPlayer({
        container: document.querySelector('#waveform')
    });

//fetching audio with duration more than 2 hours
    fetch('example.com/mp3')
        .then((res) => res.arrayBuffer())
        .then((arrayBuffer) => {
            const uint8 = new Uint8Array(arrayBuffer);
            wf.load(uint8);
        });

Memory Usage:
image

  • We are looking to use audio files with large duration with WFPlayer.

Webworker vs FFmpeg

Not an issue, just a question

Looks like the useWorker option works fine with large audio files. What's the reason for using something like FFmpeg?

Appending custom headers

Is there a way to attach custom headers to the fetch that WFPlayer does to the audio resource?

If not, how can I access protected media elements?

Connect to audiocontext.

Hello,

I am trying to connect wfplayer to an Web API audiocontext, instead of a audio/video HTMLElement..
I do not find the way to do that using WFPlayer() constructor.... I can open an audiobuffer in wf.load(), but not connect my source (audiocontext.createBufferSource()) to the wf...
Should you help please ?

Play button

I've integrated the WFPlayer into custom app, and I'm loading a remote mp3 file with cors enabled.
No errors and the waveform is displayed for the audio file.

I can't however find a way to actually start playing the audio file. Any idea of how to implement a play button?

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

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.

decodeAudioData crash

Can the decodeAudioData function be executed in a worker when the video or audio file is too large, causing insufficient memory and browser lag or even crashing?

Could you open source the demo website?

Hi,

Thank you for making the library. I think the library will be very useful for something I'm building now. Is it possible that you could show me the source code of your demo page so that I could better understand how to use it?

Thanks!

Fill the entire audio width

Is there a way to tell the player to render the audio histogram in its totality? Like duration = "max" or something equivalent?

Add `waveBorder`, `waveBorderColor`, and `waveBorderWidth` Options

Currently, the code includes functionality for drawing rectangles with specified colors. However, there's no option to control whether a border should be drawn around the rectangle, what color the border should be, or how wide the border should be. To enhance the flexibility of the code, we should introduce the following options:

  1. waveBorder: A boolean variable that allows users to enable or disable the drawing of borders around the rectangles.
  2. waveBorderColor: A variable that sets the color of the border (when waveBorder is true).
  3. waveBorderWidth: A variable that sets the width of the border (when waveBorder is true).

am getting undefinde error first time when i refresh the page

import { useEffect, useMemo, useState } from "react";
import WFPlayer from "wfplayer";
import useTranscriptionRef from "./useTranscriptionRef";
interface Props extends Partial<WFPlayer> {
  url: string;
}
const useWFPlayer = (
  containerRef: React.RefObject<HTMLDivElement>,
  options: Props
): WFPlayer | null => {
  const [wavesurfer, setWavesurfer] = useState<WFPlayer | null>(null);
  const { waveSurferRef } = useTranscriptionRef();
  const wfPlayerProps = useMemo(() => {
    return options;
  }, [options]);
  useEffect(() => {
    if (!containerRef.current || !options?.url) return;
    console.log(WFPlayer);
    const wf = new WFPlayer({
      ...wfPlayerProps.config,
      container: containerRef.current,
    });
    if (!!wf) {
      wf.load(wfPlayerProps?.url);
      waveSurferRef.current = wf;
      setWavesurfer(wf);
    }
    wf?.on("grabbing", (currentTime: any) => {
      wf?.seek(currentTime);
    });
    return () => {
      if (wf) wf?.destroy();
    };
  }, [wfPlayerProps?.url, containerRef?.current]);

  return wavesurfer;
};
export default useWFPlayer;

Screenshot (7)

Seek -- more deterministic?

Is there a way I can seek to a point in time and make sure that is always at the left edge of the control?

Right now if I seek to time X, it will seek it into view, but sometimes in the center of the visualization, sometimes towards the left, etc...

I'd like to deterministically know where the seeked point in time will be rendered.

Unusable when pixelRatio is not an integer

Step to reproduce:

  1. Change display scaling rate to 1.25x.
  2. Open https://wfplayer.js.org/
  3. The waveform doesn't load, and console shows this exception:
Uncaught WFPlayerError: pixelRatio expect a positive integer greater than or equal to 1, but got 1.25.
    at errorHandle (https://wfplayer.js.org/uncompiled/wfplayer.js:398:13)
    at https://wfplayer.js.org/uncompiled/wfplayer.js:1592:13
    at y (https://wfplayer.js.org/uncompiled/wfplayer.js:138:3586)
    at https://wfplayer.js.org/uncompiled/wfplayer.js:138:2947
    at Array.forEach (<anonymous>)
    at https://wfplayer.js.org/uncompiled/wfplayer.js:138:2878
    at f (https://wfplayer.js.org/uncompiled/wfplayer.js:138:3207)
    at WFPlayer.setOptions (https://wfplayer.js.org/uncompiled/wfplayer.js:1669:24)
    at new WFPlayer (https://wfplayer.js.org/uncompiled/wfplayer.js:1641:13)
    at initWFPlayer (https://wfplayer.js.org/assets/js/index.js:28:10)

Is there any way to load wave form faster of large video file?

Hey @zhw2590582 ,

As per your suggestion i am using web-audio-api and creating PCM file to generate wave. But the problem is PCM file is also taking long time to display wave form. Till the complete PCM file not loaded in browser wave form showing a thin line.

Is there any other fastest way to generate wave form?

Like wfplayer should not wait to load the complete PCM file data and should start to draw wave?

Please suggest. Thanks

Waveform of large audio files

Hello, I have tried your program for short audio files (10-20 seconds) and it is working fine. You have done really amazing job, thank you very much. But I need the waveform of large audio files (20min-2hours). Unfortunately, even if I use a 20min audio file, the memory explodes. Am I doing something wrong or this waveform generator is not for so large files?

React control?

Hi - I want to use this in a react project - have you considered making a react wrapper? Right now I'm hacking it in using Refs but it's messy and buggy.

npm run dev 报错无法运行

node v18.16.1报错如下:

`> [email protected] dev

npx cross-env NODE_ENV=development node ./scripts/dev.mjs

node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^

[Error: Unable to open snapshot file: No such file or directory]

Node.js v18.16.1`

node 16.14.0报错如下:
`> [email protected] dev

npx cross-env NODE_ENV=development node ./scripts/dev.mjs

node:v8:345
der.readHeader();
^

Error: Unable to deserialize cloned data due to invalid or unsupported version.
at deserialize (node:v8:345:7)
at deserialize (D:\work\github\WFPlayer\node_modules@parcel\core\lib\serializer.js:204:48)
at getAndDeserialize (D:\work\github\WFPlayer\node_modules@parcel\core\lib\RequestTracker.js:853:42)`

请问本地应该用哪个版本的node才可以?

Is there a way to remove the padding entirely?

The padding on the left and right is getting annoying because it is a different color than the background. Why can't I set padding below 1 in the options? I would like to completely remove it.
image

External source for audio file

Hi,
I am trying to generate a player from an audio that has an external source as in the following example:

<audio id="audio" src='https://bucketeer-a2765e62-ceca-4f5b-9eba-dcfd3a02354e.s3.amazonaws.com/public/motherfucouer/1.mp3' type="audio/mpeg" />

The audio source is playing, and the player is generated but the waves won't create themselves.
I checked an it works (the waves appear) with a local file. Do you have an explanation and/or a solution for me?

Thank you!

jump to point in time?

I want to control which part of the waveform is in view based on outside factors, and I want to do this when the audio is not currently playing.

I tried using wf.seek(time) to set the specific time I want in view but that doesn't auto scroll the view to a timecode that is outside of the duration currently shown. (it does if the audio is playing).

Can someone help me please? How do I scroll a specific timecode into view when the audio is not playing?

Typescript?

Any thoughts of publishing types for WFPlayer?

Add option for cursor position

It would be useful to be able to decide if the current position of the video should be in the center or in the beginning- I'm trying to create a video editor and I don't need the trailing first half of the screen before the video begins.
image
image
image

Region support

Is there any plan to support region selection in the waveform?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.