Skip to content

Commit

Permalink
Feat: [Envelope] long touch to add a point (#3178)
Browse files Browse the repository at this point in the history
* Feat: [Envelope] long touch to add a point

* Update the readme
  • Loading branch information
katspaugh authored Sep 9, 2023
1 parent 313d111 commit a34c3f3
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 99 deletions.
64 changes: 9 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,57 +54,11 @@ See more [examples](https://wavesurfer-js.org/examples).

## API reference

See the documentation on wavesurfer [methods](http://wavesurfer-js.org/docs/methods), [options](http://wavesurfer-js.org/docs/options) and [events](http://wavesurfer-js.org/docs/events).

### Wavesurfer options
- `container`: `HTMLElement | string` - Required: an HTML element or selector where the waveform will be rendered.
- `height`: `number | 'auto'` - The height of the waveform in pixels, or "auto" to fill the container height
- `waveColor`: `string | string[] | CanvasGradient` - The color of the waveform
- `progressColor`: `string | string[] | CanvasGradient` - The color of the progress mask
- `cursorColor`: `string` - The color of the playpack cursor
- `cursorWidth`: `number` - The cursor width
- `barWidth`: `number` - Render the waveform with bars like this: ▁ ▂ ▇ ▃ ▅ ▂
- `barGap`: `number` - Spacing between bars in pixels
- `barRadius`: `number` - Rounded borders for bars
- `barHeight`: `number` - A vertical scaling factor for the waveform
- `barAlign`: `'top' | 'bottom'` - Vertical bar alignment
- `minPxPerSec`: `number` - Minimum pixels per second of audio (i.e. zoom level)
- `fillParent`: `boolean` - Stretch the waveform to fill the container, true by default
- `url`: `string` - Audio URL
- `peaks`: `Array<Float32Array | number[]>` - Pre-computed audio data
- `duration`: `number` - Pre-computed duration
- `media`: `HTMLMediaElement` - Use an existing media element instead of creating one
- `autoplay`: `boolean` - Play the audio on load
- `interact`: `boolean` - Pass false to disable clicks on the waveform
- `hideScrollbar`: `boolean` - Hide the scrollbar
- `audioRate`: `number` - Audio rate
- `autoScroll`: `boolean` - Automatically scroll the container to keep the current position in viewport
- `autoCenter`: `boolean` - If autoScroll is enabled, keep the cursor in the center of the waveform during playback
- `sampleRate`: `number` - Decoding sample rate. Doesn't affect the playback. Defaults to 8000
- `splitChannels`: `WaveSurferOptions[]` - Render each audio channel as a separate waveform
- `normalize`: `boolean` - Stretch the waveform to the full height
- `plugins`: `GenericPlugin[]` - The list of plugins to initialize on start
- `renderFunction`: `(peaks: Array<Float32Array | number[]>, ctx: CanvasRenderingContext2D) => void` - Custom render function
- `fetchParams`: `RequestInit` - Options to pass to the fetch method

### Wavesurfer events
- `load`: `[url: string]` - When audio starts loading
- `loading`: `[percentage: number]` - When audio is being loaded
- `decode`: `[duration: number]` - When the audio has been decoded
- `ready`: `[duration: number]` - When the audio is both decoded and can play
- `redraw`: `[]` - When a waveform is drawn
- `play`: `[]` - When the audio starts playing
- `pause`: `[]` - When the audio pauses
- `finish`: `[]` - When the audio finishes playing
- `timeupdate`: `[currentTime: number]` - On audio position change, fires continuously during playback
- `audioprocess`: `[currentTime: number]` - An alias of timeupdate but only when the audio is playing
- `seeking`: `[currentTime: number]` - When the user seeks to a new position
- `interaction`: `[newTime: number]` - When the user interacts with the waveform (i.g. clicks or drags on it)
- `click`: `[relativeX: number]` - When the user clicks on the waveform
- `drag`: `[relativeX: number]` - When the user drags the cursor
- `scroll`: `[visibleStartTime: number, visibleEndTime: number]` - When the waveform is scrolled (panned)
- `zoom`: `[minPxPerSec: number]` - When the zoom level changes
- `destroy`: `[]` - Just before the waveform is destroyed so you can clean up your events
See the wavesurfer.js documentation on our website:

* [methods](http://wavesurfer-js.org/docs/methods)
* [options](http://wavesurfer-js.org/docs/options)
* [events](http://wavesurfer-js.org/docs/events)

## Plugins

Expand All @@ -116,7 +70,7 @@ The "official" plugins have been completely rewritten and enhanced:
* [Envelope](https://wavesurfer-js.org/examples/#envelope.js) – a graphical interface to add fade-in and -out effects and control volume
* [Record](https://wavesurfer-js.org/examples/#record.js) – records audio from the microphone and renders a waveform
* [Spectrogram](https://wavesurfer-js.org/examples/#spectrogram.js) – visualization of an audio frequency spectrum (written by @akreal)
* [Hover](https://wavesurfer-js.org/examples/#hover.js) – shows a vertical line and timestmap on waveform hover
* [Hover](https://wavesurfer-js.org/examples/#hover.js) – shows a vertical line and timestmap on waveform hover

## CSS styling

Expand Down Expand Up @@ -189,8 +143,8 @@ Have a question about integrating wavesurfer.js on your website? Feel free to as

---

* **Q**: There is a mismatch between my audio and the waveform.
* **A**: If you're using a VBR (variable bit rate) mp3 file, there might be a mismatch between the audio and the waveform. This can be fixed by converting your file to CBR (constant bit rate). See [this issue](https://github.com/katspaugh/wavesurfer.js/issues/2890#issuecomment-1601067822) for details.
* **Q**: There is a mismatch between my audio and the waveform. How do I fix it?
* **A**: If you're using a VBR (variable bit rate) audio file, there might be a mismatch between the audio and the waveform. This can be fixed by converting your file to CBR (constant bit rate). See [this issue](https://github.com/katspaugh/wavesurfer.js/issues/2890#issuecomment-1601067822) for details.


## Development
Expand Down Expand Up @@ -231,4 +185,4 @@ We appreciate your feedback and contributions!

If you encounter any issues or have suggestions for improvements, please don't hesitate to post in our [forum](https://github.com/wavesurfer-js/wavesurfer.js/discussions/categories/q-a).

We hope you enjoy using wavesurfer.ts and look forward to hearing about your experiences with the library!
We hope you enjoy using wavesurfer.js and look forward to hearing about your experiences with the library!
5 changes: 4 additions & 1 deletion examples/envelope.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ const wavesurfer = WaveSurfer.create({
url: '/examples/audio/audio.wav',
})

const isMobile = top.matchMedia('(max-width: 900px)').matches

// Initialize the Envelope plugin
const envelope = wavesurfer.registerPlugin(
EnvelopePlugin.create({
volume: 0.8,
lineColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 4,
dragPointSize: top.innerWidth > 900 ? 10 : 20,
dragPointSize: isMobile ? 20 : 12,
dragLine: !isMobile,
dragPointFill: 'rgba(0, 255, 255, 0.8)',
dragPointStroke: 'rgba(0, 0, 0, 0.5)',

Expand Down
71 changes: 28 additions & 43 deletions src/plugins/envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,15 @@ export type EnvelopePluginOptions = {
lineColor?: string
dragLine?: boolean
dragPointSize?: number
dragPointSizeMobile?: number
dragPointFill?: string
dragPointStroke?: string

/** Deprecated. Use `points` instead. */
fadeInStart?: number
/** Deprecated. Use `points` instead. */
fadeInEnd?: number
/** Deprecated. Use `points` instead. */
fadeOutStart?: number
/** Deprecated. Use `points` instead. */
fadeOutEnd?: number
}

const defaultOptions = {
points: [] as EnvelopePoint[],
lineWidth: 4,
lineColor: 'rgba(0, 0, 255, 0.5)',
dragPointSize: 10,
dragPointSizeMobile: 30,
dragPointFill: 'rgba(255, 255, 255, 0.8)',
dragPointStroke: 'rgba(255, 255, 255, 0.8)',
}
Expand Down Expand Up @@ -81,7 +70,7 @@ class Polyline extends EventEmitter<{
svg.setAttribute('height', '100%')
svg.setAttribute('viewBox', `0 0 ${width} ${height}`)
svg.setAttribute('preserveAspectRatio', 'none')
svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;')
svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4;')
svg.setAttribute('part', 'envelope')
this.svg = svg

Expand Down Expand Up @@ -123,6 +112,31 @@ class Polyline extends EventEmitter<{
const y = e.clientY - rect.top
this.emit('point-create', x / rect.width, y / rect.height)
})

// Long press on touch devices
{
let pressTimer: number

const clearTimer = () => clearTimeout(pressTimer)

svg.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
pressTimer = window.setTimeout(() => {
e.preventDefault()
const rect = svg.getBoundingClientRect()
const x = e.touches[0].clientX - rect.left
const y = e.touches[0].clientY - rect.top
this.emit('point-create', x / rect.width, y / rect.height)
}, 500)
} else {
clearTimer()
}
})

svg.addEventListener('touchmove', clearTimer)

svg.addEventListener('touchend', clearTimer)
}
}

private makeDraggable(draggable: SVGElement, onDrag: (x: number, y: number) => void) {
Expand All @@ -136,7 +150,7 @@ class Polyline extends EventEmitter<{

private createCircle(x: number, y: number) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse')
const size = Number(window.top?.innerWidth) > 900 ? this.options.dragPointSize : this.options.dragPointSizeMobile
const size = this.options.dragPointSize
const radius = size / 2
circle.setAttribute('rx', radius.toString())
circle.setAttribute('ry', radius.toString())
Expand Down Expand Up @@ -253,6 +267,7 @@ class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePluginOpti
this.options.lineColor = this.options.lineColor || defaultOptions.lineColor
this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill
this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke
this.options.dragPointSize = this.options.dragPointSize || defaultOptions.dragPointSize
}

public static create(options: EnvelopePluginOptions) {
Expand Down Expand Up @@ -454,36 +469,6 @@ class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePluginOpti
this.emit('volume-change', newVolume)
}
}

// Deprecated methods

/**
* Deprecated: use `setPoints` instead.
*/
public setStartTime() {
console.warn('[wavesurfer.js envelope plugin] `setStartTime` is deprecated, use `setPoints` instead.')
}

/**
* Deprecated: use `setPoints` instead.
*/
public setEndTime() {
console.warn('[wavesurfer.js envelope plugin] `setEndTime` is deprecated, use `setPoints` instead.')
}

/**
* Deprecated: use `setPoints` instead.
*/
public setFadeInEnd() {
console.warn('[wavesurfer.js envelope plugin] `setFadeInEnd` is deprecated, use `setPoints` instead.')
}

/**
* Deprecated: use `setPoints` instead.
*/
public setFadeOutStart() {
console.warn('[wavesurfer.js envelope plugin] `setFadeOutStart` is deprecated, use `setPoints` instead.')
}
}

export default EnvelopePlugin

0 comments on commit a34c3f3

Please sign in to comment.