wavesurfer.js 7.0.0-alpha.4 → 7.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -17
- package/dist/base-plugin.d.ts +14 -5
- package/dist/base-plugin.js +5 -1
- package/dist/decoder.d.ts +8 -10
- package/dist/decoder.js +37 -44
- package/dist/event-emitter.d.ts +16 -10
- package/dist/event-emitter.js +38 -16
- package/dist/fetcher.js +2 -13
- package/dist/legacy-adapter.d.ts +7 -0
- package/dist/legacy-adapter.js +15 -0
- package/dist/player.d.ts +32 -11
- package/dist/player.js +55 -39
- package/dist/plugins/envelope.d.ts +57 -0
- package/dist/plugins/envelope.js +305 -0
- package/dist/plugins/envelope.min.js +1 -0
- package/dist/plugins/minimap.d.ts +34 -0
- package/dist/plugins/minimap.js +99 -0
- package/dist/plugins/minimap.min.js +1 -0
- package/dist/plugins/multitrack.d.ts +117 -0
- package/dist/plugins/multitrack.js +506 -0
- package/dist/plugins/multitrack.min.js +1 -0
- package/dist/plugins/record.d.ts +26 -0
- package/dist/plugins/record.js +125 -0
- package/dist/plugins/record.min.js +1 -0
- package/dist/plugins/regions.d.ts +88 -41
- package/dist/plugins/regions.js +365 -170
- package/dist/plugins/regions.min.js +1 -0
- package/dist/plugins/spectrogram.d.ts +24 -0
- package/dist/plugins/spectrogram.js +165 -0
- package/dist/plugins/timeline.d.ts +41 -0
- package/dist/plugins/timeline.js +135 -0
- package/dist/plugins/timeline.min.js +1 -0
- package/dist/renderer.d.ts +26 -12
- package/dist/renderer.js +212 -157
- package/dist/timer.d.ts +1 -1
- package/dist/wavesurfer.d.ts +136 -0
- package/dist/wavesurfer.js +213 -0
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +49 -24
- package/dist/index.d.ts +0 -104
- package/dist/index.js +0 -188
- package/dist/player-webaudio.d.ts +0 -8
- package/dist/player-webaudio.js +0 -32
- package/dist/react/useWavesurfer.d.ts +0 -5
- package/dist/react/useWavesurfer.js +0 -20
- package/dist/wavesurfer.Regions.min.js +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record audio from the microphone, render a waveform and download the audio.
|
|
3
|
+
*/
|
|
4
|
+
import BasePlugin from '../base-plugin.js';
|
|
5
|
+
class RecordPlugin extends BasePlugin {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.mediaRecorder = null;
|
|
9
|
+
this.recordedUrl = '';
|
|
10
|
+
}
|
|
11
|
+
static create(options) {
|
|
12
|
+
return new RecordPlugin(options || {});
|
|
13
|
+
}
|
|
14
|
+
loadBlob(data) {
|
|
15
|
+
const blob = new Blob(data, { type: 'audio/webm' });
|
|
16
|
+
this.recordedUrl = URL.createObjectURL(blob);
|
|
17
|
+
this.wavesurfer?.load(this.recordedUrl);
|
|
18
|
+
}
|
|
19
|
+
render(stream) {
|
|
20
|
+
if (!this.container || !this.wavesurfer)
|
|
21
|
+
return () => undefined;
|
|
22
|
+
const canvas = document.createElement('canvas');
|
|
23
|
+
canvas.width = this.container.clientWidth;
|
|
24
|
+
canvas.height = this.container.clientHeight;
|
|
25
|
+
canvas.style.zIndex = '10';
|
|
26
|
+
this.container.appendChild(canvas);
|
|
27
|
+
const canvasCtx = canvas.getContext('2d');
|
|
28
|
+
const audioContext = new AudioContext();
|
|
29
|
+
const source = audioContext.createMediaStreamSource(stream);
|
|
30
|
+
const analyser = audioContext.createAnalyser();
|
|
31
|
+
source.connect(analyser);
|
|
32
|
+
let animationId;
|
|
33
|
+
const drawWaveform = () => {
|
|
34
|
+
if (!canvasCtx)
|
|
35
|
+
return;
|
|
36
|
+
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
|
|
37
|
+
const bufferLength = analyser.frequencyBinCount;
|
|
38
|
+
const dataArray = new Uint8Array(bufferLength);
|
|
39
|
+
analyser.getByteTimeDomainData(dataArray);
|
|
40
|
+
canvasCtx.lineWidth = this.options.lineWidth || 2;
|
|
41
|
+
canvasCtx.strokeStyle = this.options.waveColor || this.wavesurfer?.options.waveColor || '';
|
|
42
|
+
canvasCtx.beginPath();
|
|
43
|
+
const sliceWidth = (canvas.width * 1.0) / bufferLength;
|
|
44
|
+
let x = 0;
|
|
45
|
+
for (let i = 0; i < bufferLength; i++) {
|
|
46
|
+
const v = dataArray[i] / 128.0;
|
|
47
|
+
const y = (v * canvas.height) / 2;
|
|
48
|
+
if (i === 0) {
|
|
49
|
+
canvasCtx.moveTo(x, y);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
canvasCtx.lineTo(x, y);
|
|
53
|
+
}
|
|
54
|
+
x += sliceWidth;
|
|
55
|
+
}
|
|
56
|
+
canvasCtx.lineTo(canvas.width, canvas.height / 2);
|
|
57
|
+
canvasCtx.stroke();
|
|
58
|
+
animationId = requestAnimationFrame(drawWaveform);
|
|
59
|
+
};
|
|
60
|
+
drawWaveform();
|
|
61
|
+
return () => {
|
|
62
|
+
if (animationId) {
|
|
63
|
+
cancelAnimationFrame(animationId);
|
|
64
|
+
}
|
|
65
|
+
if (source) {
|
|
66
|
+
source.disconnect();
|
|
67
|
+
source.mediaStream.getTracks().forEach((track) => track.stop());
|
|
68
|
+
}
|
|
69
|
+
if (audioContext) {
|
|
70
|
+
audioContext.close();
|
|
71
|
+
}
|
|
72
|
+
canvas?.remove();
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
cleanUp() {
|
|
76
|
+
this.stopRecording();
|
|
77
|
+
this.wavesurfer?.empty();
|
|
78
|
+
if (this.recordedUrl) {
|
|
79
|
+
URL.revokeObjectURL(this.recordedUrl);
|
|
80
|
+
this.recordedUrl = '';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async startRecording() {
|
|
84
|
+
this.cleanUp();
|
|
85
|
+
let stream;
|
|
86
|
+
try {
|
|
87
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
throw new Error('Error accessing the microphone: ' + err.message);
|
|
91
|
+
}
|
|
92
|
+
const onStop = this.render(stream);
|
|
93
|
+
const mediaRecorder = new MediaRecorder(stream);
|
|
94
|
+
const recordedChunks = [];
|
|
95
|
+
mediaRecorder.addEventListener('dataavailable', (event) => {
|
|
96
|
+
if (event.data.size > 0) {
|
|
97
|
+
recordedChunks.push(event.data);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
mediaRecorder.addEventListener('stop', () => {
|
|
101
|
+
onStop();
|
|
102
|
+
this.loadBlob(recordedChunks);
|
|
103
|
+
this.emit('stopRecording');
|
|
104
|
+
});
|
|
105
|
+
mediaRecorder.start();
|
|
106
|
+
this.emit('startRecording');
|
|
107
|
+
this.mediaRecorder = mediaRecorder;
|
|
108
|
+
}
|
|
109
|
+
isRecording() {
|
|
110
|
+
return this.mediaRecorder?.state === 'recording';
|
|
111
|
+
}
|
|
112
|
+
stopRecording() {
|
|
113
|
+
if (this.isRecording()) {
|
|
114
|
+
this.mediaRecorder?.stop();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
getRecordedUrl() {
|
|
118
|
+
return this.recordedUrl;
|
|
119
|
+
}
|
|
120
|
+
destroy() {
|
|
121
|
+
super.destroy();
|
|
122
|
+
this.cleanUp();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export default RecordPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Record=t():e.Record=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={d:(t,r)=>{for(var s in r)e.o(r,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:r[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>o});const r=class{constructor(){this.listeners={}}on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),()=>this.un(e,t)}once(e,t){const r=this.on(e,t),s=this.on(e,(()=>{r(),s()}));return r}un(e,t){this.listeners[e]&&(t?this.listeners[e].delete(t):delete this.listeners[e])}unAll(){this.listeners={}}emit(e,...t){this.listeners[e]&&this.listeners[e].forEach((e=>e(...t)))}},s=class extends r{constructor(e){super(),this.subscriptions=[],this.options=e}init(e){this.wavesurfer=e.wavesurfer,this.container=e.container,this.wrapper=e.wrapper}destroy(){this.subscriptions.forEach((e=>e()))}};class i extends s{constructor(){super(...arguments),this.mediaRecorder=null,this.recordedUrl=""}static create(e){return new i(e||{})}loadBlob(e){const t=new Blob(e,{type:"audio/webm"});this.recordedUrl=URL.createObjectURL(t),this.wavesurfer?.load(this.recordedUrl)}render(e){if(!this.container||!this.wavesurfer)return()=>{};const t=document.createElement("canvas");t.width=this.container.clientWidth,t.height=this.container.clientHeight,t.style.zIndex="10",this.container.appendChild(t);const r=t.getContext("2d"),s=new AudioContext,i=s.createMediaStreamSource(e),o=s.createAnalyser();let n;i.connect(o);const c=()=>{if(!r)return;r.clearRect(0,0,t.width,t.height);const e=o.frequencyBinCount,s=new Uint8Array(e);o.getByteTimeDomainData(s),r.lineWidth=this.options.lineWidth||2,r.strokeStyle=this.options.waveColor||this.wavesurfer?.options.waveColor||"",r.beginPath();const i=1*t.width/e;let a=0;for(let o=0;o<e;o++){const e=s[o]/128*t.height/2;0===o?r.moveTo(a,e):r.lineTo(a,e),a+=i}r.lineTo(t.width,t.height/2),r.stroke(),n=requestAnimationFrame(c)};return c(),()=>{n&&cancelAnimationFrame(n),i&&(i.disconnect(),i.mediaStream.getTracks().forEach((e=>e.stop()))),s&&s.close(),t?.remove()}}cleanUp(){this.stopRecording(),this.wavesurfer?.empty(),this.recordedUrl&&(URL.revokeObjectURL(this.recordedUrl),this.recordedUrl="")}async startRecording(){let e;this.cleanUp();try{e=await navigator.mediaDevices.getUserMedia({audio:!0})}catch(e){throw new Error("Error accessing the microphone: "+e.message)}const t=this.render(e),r=new MediaRecorder(e),s=[];r.addEventListener("dataavailable",(e=>{e.data.size>0&&s.push(e.data)})),r.addEventListener("stop",(()=>{t(),this.loadBlob(s),this.emit("stopRecording")})),r.start(),this.emit("startRecording"),this.mediaRecorder=r}isRecording(){return"recording"===this.mediaRecorder?.state}stopRecording(){this.isRecording()&&this.mediaRecorder?.stop()}getRecordedUrl(){return this.recordedUrl}destroy(){super.destroy(),this.cleanUp()}}const o=i;return t.default})()));
|
|
@@ -1,48 +1,95 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Regions are visual overlays on the waveform that can be used to mark segments of audio.
|
|
3
|
+
* Regions can be clicked on, dragged and resized.
|
|
4
|
+
* You can set the color and content of each region, as well as their HTML content.
|
|
5
|
+
*/
|
|
6
|
+
import BasePlugin, { type WaveSurferPluginParams } from '../base-plugin.js';
|
|
7
|
+
import EventEmitter from '../event-emitter.js';
|
|
8
|
+
export type RegionsPluginOptions = undefined;
|
|
9
|
+
export type RegionsPluginEvents = {
|
|
10
|
+
'region-created': [region: Region];
|
|
11
|
+
'region-updated': [region: Region];
|
|
12
|
+
'region-clicked': [region: Region, e: MouseEvent];
|
|
10
13
|
};
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
export type RegionEvents = {
|
|
15
|
+
remove: [];
|
|
16
|
+
update: [];
|
|
17
|
+
'update-end': [];
|
|
18
|
+
play: [];
|
|
19
|
+
click: [event: MouseEvent];
|
|
20
|
+
dblclick: [event: MouseEvent];
|
|
21
|
+
over: [event: MouseEvent];
|
|
22
|
+
leave: [event: MouseEvent];
|
|
23
|
+
};
|
|
24
|
+
export type RegionParams = {
|
|
25
|
+
id?: string;
|
|
26
|
+
start: number;
|
|
27
|
+
end?: number;
|
|
28
|
+
drag?: boolean;
|
|
29
|
+
resize?: boolean;
|
|
30
|
+
color?: string;
|
|
31
|
+
content?: string | HTMLElement;
|
|
21
32
|
};
|
|
22
|
-
declare class
|
|
23
|
-
private
|
|
24
|
-
|
|
33
|
+
declare class Region extends EventEmitter<RegionEvents> {
|
|
34
|
+
private totalDuration;
|
|
35
|
+
element: HTMLElement;
|
|
36
|
+
id: string;
|
|
37
|
+
start: number;
|
|
38
|
+
end: number;
|
|
39
|
+
drag: boolean;
|
|
40
|
+
resize: boolean;
|
|
41
|
+
color: string;
|
|
42
|
+
content?: HTMLElement;
|
|
43
|
+
constructor(params: RegionParams, totalDuration: number);
|
|
44
|
+
private initElement;
|
|
45
|
+
private renderPosition;
|
|
46
|
+
private initMouseEvents;
|
|
47
|
+
private onStartMoving;
|
|
48
|
+
private onEndMoving;
|
|
49
|
+
private onUpdate;
|
|
50
|
+
private onMove;
|
|
51
|
+
private onResize;
|
|
52
|
+
private onEndResizing;
|
|
53
|
+
_setTotalDuration(totalDuration: number): void;
|
|
54
|
+
/** Play the region from start to end */
|
|
55
|
+
play(): void;
|
|
56
|
+
/** Update the region's options */
|
|
57
|
+
setOptions(options: {
|
|
58
|
+
color?: string;
|
|
59
|
+
drag?: boolean;
|
|
60
|
+
resize?: boolean;
|
|
61
|
+
start?: number;
|
|
62
|
+
end?: number;
|
|
63
|
+
}): void;
|
|
64
|
+
/** Remove the region */
|
|
65
|
+
remove(): void;
|
|
66
|
+
}
|
|
67
|
+
declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
|
|
25
68
|
private regions;
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
private isMoving;
|
|
69
|
+
private regionsContainer;
|
|
70
|
+
/** Create an instance of RegionsPlugin */
|
|
71
|
+
constructor(options?: RegionsPluginOptions);
|
|
30
72
|
/** Create an instance of RegionsPlugin */
|
|
31
|
-
|
|
32
|
-
/**
|
|
73
|
+
static create(options?: RegionsPluginOptions): RegionsPlugin;
|
|
74
|
+
/** Called by wavesurfer, don't call manually */
|
|
75
|
+
init(params: WaveSurferPluginParams): void;
|
|
76
|
+
private initRegionsContainer;
|
|
77
|
+
/** Get all created regions */
|
|
78
|
+
getRegions(): Region[];
|
|
79
|
+
private avoidOverlapping;
|
|
80
|
+
private saveRegion;
|
|
81
|
+
/** Create a region with given parameters */
|
|
82
|
+
addRegion(options: RegionParams): Region;
|
|
83
|
+
/** The same as addRegion but with spread params */
|
|
84
|
+
add(start: number, end: number, content?: string, color?: string): Region;
|
|
85
|
+
/**
|
|
86
|
+
* Enable creation of regions by dragging on an empty space on the waveform.
|
|
87
|
+
* Returns a function to disable the drag selection.
|
|
88
|
+
*/
|
|
89
|
+
enableDragSelection(options: RegionParams): () => void;
|
|
90
|
+
/** Remove all regions */
|
|
91
|
+
clearRegions(): void;
|
|
92
|
+
/** Destroy the plugin and clean up */
|
|
33
93
|
destroy(): void;
|
|
34
|
-
private initWrapper;
|
|
35
|
-
private handleMouseDown;
|
|
36
|
-
private handleMouseMove;
|
|
37
|
-
private handleMouseUp;
|
|
38
|
-
private createRegionElement;
|
|
39
|
-
private createRegion;
|
|
40
|
-
private addRegion;
|
|
41
|
-
private updateRegion;
|
|
42
|
-
private moveRegion;
|
|
43
|
-
/** Create a region at a given start and end time, with an optional title */
|
|
44
|
-
add(startTime: number, endTime: number, title?: string): Region;
|
|
45
|
-
/** Set the background color of a region */
|
|
46
|
-
setRegionColor(region: Region, color: string): void;
|
|
47
94
|
}
|
|
48
95
|
export default RegionsPlugin;
|