Corrected DESC for last uploaded file
65
public/explore/mysteries/index.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
session_start();
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
$root = realpath($_SERVER["DOCUMENT_ROOT"]);
|
||||
require "$root/database/credentials.php";
|
||||
// Connect the database
|
||||
try {
|
||||
$db = new PDO("mysql:host=$host;dbname=$database;charset=utf8",
|
||||
$user,
|
||||
$password,
|
||||
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
die("Error : ".$e->getMessage());
|
||||
}
|
||||
|
||||
$req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` WHERE species="unknown" ORDER BY `entry_timestamp` ASC LIMIT 1');
|
||||
$req->execute();
|
||||
$result = $req->fetchAll();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Explore | Chiro - Canto</title>
|
||||
<link rel="stylesheet" type="text/css" href="/styles/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include("$root/menu.php");?>
|
||||
<?php include("$root/header.php");?>
|
||||
<section>
|
||||
<h3>Mystery recording</h3>
|
||||
<?php
|
||||
foreach ($result as $row)
|
||||
{
|
||||
?>
|
||||
<div class="sound">
|
||||
<h3><?=$row['file_name']?></h3>
|
||||
<h4><em><?=$row['species']?></em></h4>
|
||||
<p>Recorded on <?=$row['date']?> at <?=$row['time']?></p>
|
||||
<?php
|
||||
if (file_exists($root."/storage/spectrograms/".$row['file_name'].'.png'))
|
||||
{
|
||||
?>
|
||||
<img id="spectrogram" src="<?="/storage/spectrograms/".$row['file_name'].'.png'?>" alt="bat sound spectrogram">
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<br>
|
||||
<audio src="<?="/storage/records/".$row['file_name']?>" controls></audio>
|
||||
<p><?=$row['license']?> <?=$row['recordist_name']?></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</section>
|
||||
<?php include("$root/footer.php");?>
|
||||
</body>
|
||||
<script src="/scripts/script.js"></script>
|
||||
</html>
|
69
public/explore/spectrograms/index.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
session_start();
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
$root = realpath($_SERVER["DOCUMENT_ROOT"]);
|
||||
require "$root/database/credentials.php";
|
||||
// Connect the database
|
||||
try {
|
||||
$db = new PDO("mysql:host=$host;dbname=$database;charset=utf8",
|
||||
$user,
|
||||
$password,
|
||||
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
die("Error : ".$e->getMessage());
|
||||
}
|
||||
|
||||
$req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` ORDER BY `entry_timestamp` ASC LIMIT 1');
|
||||
$req->execute();
|
||||
$result = $req->fetchAll();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Explore | Chiro - Canto</title>
|
||||
<link rel="stylesheet" type="text/css" href="/styles/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include("$root/menu.php");?>
|
||||
<?php include("$root/header.php");?>
|
||||
<section>
|
||||
<h3>Mystery recording</h3>
|
||||
<?php
|
||||
foreach ($result as $row)
|
||||
{
|
||||
?>
|
||||
<div class="sound">
|
||||
<h3><?=$row['file_name']?></h3>
|
||||
<h4><em><?=$row['species']?></em></h4>
|
||||
<p>Recorded on <?=$row['date']?> at <?=$row['time']?></p>
|
||||
<?php
|
||||
if (file_exists($root."/storage/spectrograms/".$row['file_name'].'.png'))
|
||||
{
|
||||
?>
|
||||
<!-- <img id="spectrogram" src="<?="/storage/spectrograms/".$row['file_name'].'.png'?>" alt="bat sound spectrogram"> -->
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div id="waveform"></div>
|
||||
<br>
|
||||
<audio src="<?="/storage/records/".$row['file_name']?>" id="audio" controls></audio>
|
||||
<p><?=$row['license']?> <?=$row['recordist_name']?></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</section>
|
||||
<?php include("$root/footer.php");?>
|
||||
</body>
|
||||
<script src="/scripts/wavesurfer/src/wavesurfer.js"></script>
|
||||
<script src="scripts/script.js"></script>
|
||||
<script src="/scripts/script.js"></script>
|
||||
|
||||
</html>
|
11
public/explore/spectrograms/scripts/script.js
Normal file
@ -0,0 +1,11 @@
|
||||
var wavesurfer = WaveSurfer.create({
|
||||
container: '#waveform',
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple'
|
||||
});
|
||||
wavesurfer.on('ready', function () {
|
||||
wavesurfer.play();
|
||||
});
|
||||
let audio = document.getElementById('audio');
|
||||
let file_path = audio.src;
|
||||
wavesurfer.load(file_path);
|
@ -13,7 +13,7 @@ try {
|
||||
die("Error : ".$e->getMessage());
|
||||
}
|
||||
|
||||
$req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` ORDER BY `entry_timestamp` ASC LIMIT 1');
|
||||
$req = $db->prepare('SELECT id, recordist_name, file_name, license, species, sound_type, date, time FROM `records` ORDER BY `entry_timestamp` DESC LIMIT 1');
|
||||
$req->execute();
|
||||
if ($data = $req->fetch())
|
||||
{
|
||||
@ -22,6 +22,15 @@ if ($data = $req->fetch())
|
||||
<h3><?=$data['file_name']?></h3>
|
||||
<h4><em><?=$data['species']?></em></h4>
|
||||
<p>Recorded on <?=$data['date']?> at <?=$data['time']?></p>
|
||||
<?php
|
||||
if (file_exists($root."/storage/spectrograms/".$data['file_name'].'.png'))
|
||||
{
|
||||
?>
|
||||
<img id="spectrogram" src="<?="/storage/spectrograms/".$data['file_name'].'.png'?>" alt="bat sound spectrogram">
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<br>
|
||||
<audio src="<?="../storage/records/".$data['file_name']?>" controls></audio>
|
||||
<p><?=$data['license']?> <?=$data['recordist_name']?></p>
|
||||
</div>
|
||||
|
22
public/scripts/wavesurfer/.babelrc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": "last 3 version",
|
||||
"debug": false,
|
||||
"modules": "commonjs"
|
||||
}]
|
||||
],
|
||||
"plugins": ["transform-class-properties", "add-module-exports"],
|
||||
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": [
|
||||
["istanbul", {
|
||||
"exclude": [
|
||||
"spec/**/*.spec.js"
|
||||
]
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
11
public/scripts/wavesurfer/.editorconfig
Normal file
@ -0,0 +1,11 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
51
public/scripts/wavesurfer/.esdoc.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"source": "./src",
|
||||
"destination": "./doc",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "esdoc-standard-plugin"
|
||||
},
|
||||
{
|
||||
"name": "esdoc-brand-plugin"
|
||||
},
|
||||
{
|
||||
"name": "esdoc-accessor-plugin",
|
||||
"option": {
|
||||
"access": ["public", "protected"],
|
||||
"autoPrivate": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "esdoc-ecmascript-proposal-plugin",
|
||||
"option": {
|
||||
"classProperties": true,
|
||||
"objectRestSpread": true,
|
||||
"exportExtensions": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "esdoc-coverage-plugin",
|
||||
"option": {
|
||||
"enable": true,
|
||||
"kind": [
|
||||
"class",
|
||||
"method",
|
||||
"member",
|
||||
"get",
|
||||
"set",
|
||||
"constructor",
|
||||
"function",
|
||||
"variable"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "esdoc-integrate-test-plugin",
|
||||
"option": {
|
||||
"source": "./spec/",
|
||||
"interfaces": ["describe", "it", "context", "suite", "test"],
|
||||
"includes": ["(spec|Spec|test|Test)\\.js$"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
57
public/scripts/wavesurfer/.eslintrc.js
Normal file
@ -0,0 +1,57 @@
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
extends: ['eslint:recommended'], // extending recommended config and config derived from eslint-config-prettier
|
||||
parser: 'babel-eslint',
|
||||
globals: {
|
||||
WaveSurfer: true,
|
||||
Float32Array: true,
|
||||
Uint32Array: true,
|
||||
Promise: true,
|
||||
Uint8Array: true,
|
||||
ArrayBuffer: true,
|
||||
__VERSION__: true
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true
|
||||
},
|
||||
rules: {
|
||||
eqeqeq: 'off',
|
||||
'semi': 2,
|
||||
'curly': "error",
|
||||
"indent": ["error", 4, {
|
||||
"ignoredNodes": ["TemplateLiteral"],
|
||||
"SwitchCase": 1
|
||||
}],
|
||||
'comma-dangle': ["error", "never"],
|
||||
'comma-spacing': ["error", { "before": false, "after": true }],
|
||||
'no-console': 1,
|
||||
'no-unused-vars': 'off',
|
||||
'no-var': 'error',
|
||||
'no-unreachable': 2,
|
||||
'no-extra-semi': "error",
|
||||
'no-multi-spaces': "error",
|
||||
'no-multiple-empty-lines': "error",
|
||||
'space-infix-ops': "error",
|
||||
'valid-jsdoc': [2, {
|
||||
'requireReturn': false,
|
||||
'requireReturnType': false
|
||||
}],
|
||||
'no-trailing-spaces': "error",
|
||||
'no-dupe-keys': "error",
|
||||
'require-jsdoc': 2,
|
||||
'no-duplicate-imports': "error",
|
||||
'space-before-function-paren': ["error", "never"],
|
||||
'keyword-spacing': ["error", {"before": true}]
|
||||
},
|
||||
'overrides': [
|
||||
{
|
||||
'files': ['example/**/*.js', 'spec/**/*.js'],
|
||||
'rules': {
|
||||
'no-var': 'off',
|
||||
'no-console': 'off',
|
||||
'require-jsdoc': 0,
|
||||
'valid-jsdoc': 0
|
||||
}
|
||||
}]
|
||||
};
|
26
public/scripts/wavesurfer/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Hey, thank you for testing and contributing to wavesurfer.js!
|
||||
|
||||
## Please make sure you can check all of these points below before opening an issue:
|
||||
|
||||
(You don't have to post this section)
|
||||
|
||||
- [ ] I have checked [the FAQ](https://wavesurfer-js.org/faq/) and it doesn't solve my problem.
|
||||
- [ ] I have checked [the documentation](https://wavesurfer-js.org/docs/) and it doesn't solve my problem
|
||||
- [ ] I have searched for [already open issues](https://github.com/katspaugh/wavesurfer.js/issues) which desribe my problem.
|
||||
- [ ] The issue I'm having is related to and caused by wavesurfer.js, not by other software (which maybe packages and uses wavesurfer incorrectly) – In that case you should open the issue on the respective project pages.
|
||||
|
||||
## Please make sure you provide the following information (if applicable):
|
||||
|
||||
### Wavesurfer.js version(s):
|
||||
|
||||
|
||||
### Browser and operating system version(s):
|
||||
|
||||
|
||||
### Code needed to reproduce the issue:
|
||||
|
||||
(Please reduce your code as much as possible and only post the minimum code needed to reproduce the issue. [A Code pen](http://codepen.io/) is an excellent way to share such code)
|
||||
|
||||
|
||||
### Use behaviour needed to reproduce the issue:
|
||||
|
21
public/scripts/wavesurfer/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Hey, thank you for contributing to wavesurfer.js!
|
||||
|
||||
To review/merge open PRs it is very helpful to know as much as possible about the changes which are being introduced. Reviewing PRs is very time consuming, please be patient, it can take some time to do properly.
|
||||
|
||||
**Title:** Please make sure the name of your PR is as descriptive as possible (Describe the feature that is introduced or the bug that is being fixed).
|
||||
|
||||
## Please make sure you provide the information below:
|
||||
|
||||
### Short description of changes:
|
||||
|
||||
|
||||
### Breaking in the external API:
|
||||
|
||||
|
||||
### Breaking changes in the internal API:
|
||||
|
||||
|
||||
### Todos/Notes:
|
||||
|
||||
|
||||
### Related Issues and other PRs:
|
51
public/scripts/wavesurfer/.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: wavesurfer.js
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Update system
|
||||
run: sudo apt-get update
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get install -y ubuntu-restricted-addons chromium-codecs-ffmpeg-extra gstreamer1.0-libav gstreamer1.0-plugins-ugly gstreamer1.0-vaapi
|
||||
- name: Using Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Cache Node.js modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
# npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
path: ~/.npm
|
||||
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-node-
|
||||
${{ runner.OS }}-
|
||||
- name: Install Node.js modules
|
||||
run: npm install
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Test
|
||||
run: npm run test
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
path-to-lcov: ./coverage/lcov/lcov.info
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Documentation
|
||||
run: npm run doc
|
36
public/scripts/wavesurfer/.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: NPM Package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
publish-npm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- name: Cache Node.js modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
# npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
path: ~/.npm
|
||||
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-node-
|
||||
${{ runner.OS }}-
|
||||
- run: npm install
|
||||
- name: Tag & publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
||||
run: |
|
||||
OLD_VERSION=$(npm show wavesurfer.js version)
|
||||
NEW_VERSION=$(node -p 'require("./package.json").version')
|
||||
if [ $NEW_VERSION != $OLD_VERSION ]; then
|
||||
git tag "$NEW_VERSION"
|
||||
git push --tags
|
||||
npm publish
|
||||
fi
|
19
public/scripts/wavesurfer/.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/dist
|
||||
|
||||
/node_modules
|
||||
/bower_components
|
||||
/npm-debug.log
|
||||
/coverage
|
||||
/_SpecRunner.html
|
||||
/doc
|
||||
/_site
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
.project
|
||||
.chrome
|
||||
.build_cache
|
||||
.pydevproject
|
||||
|
||||
package-lock.json
|
||||
.package.json.swp
|
12
public/scripts/wavesurfer/.htmlhintrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tagname-lowercase": true,
|
||||
"attr-lowercase": true,
|
||||
"attr-value-double-quotes": false,
|
||||
"doctype-first": false,
|
||||
"tag-pair": true,
|
||||
"spec-char-escape": false,
|
||||
"id-unique": true,
|
||||
"src-not-empty": true,
|
||||
"attr-no-duplication": true,
|
||||
"title-require": true
|
||||
}
|
3
public/scripts/wavesurfer/.npmignore
Normal file
@ -0,0 +1,3 @@
|
||||
# Intentionally left blank, so that npm does not ignore anything by default,
|
||||
# but relies on the package.json "files" array to explicitly define what ends
|
||||
# up in the package.
|
18
public/scripts/wavesurfer/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"breakOnLoad": true,
|
||||
"sourceMaps": true,
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack://WaveSurfer.[name]/./*": "${webRoot}/*",
|
||||
"webpack://WaveSurfer/./*": "${webRoot}/*",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
374
public/scripts/wavesurfer/CHANGES.md
Normal file
@ -0,0 +1,374 @@
|
||||
wavesurfer.js changelog
|
||||
=======================
|
||||
|
||||
x.x.x (unreleased)
|
||||
------------------
|
||||
- Playhead plugin: add a new plugin that allows the setting of a independent
|
||||
"play head", or song-start position. (#2209)
|
||||
- Markers plugin: fix a bug where markers at the end of a track would cause
|
||||
incorrect click-to-seek behavior (#2208)
|
||||
- Regions plugin:
|
||||
- Fix mouseup not firing if click & drag a region handle & release outside browser window (#2213)
|
||||
- Added new `showTooltip` param allowing disabling region `title` tooltip (#2213)
|
||||
- Nullify `onaudioprocess` on remove to not execute in background (#2218)
|
||||
|
||||
4.6.0 (04.03.2021)
|
||||
------------------
|
||||
- Webaudio: fix `decodeAudioData` handling in Safari (#2201)
|
||||
- Markers plugin: add new plugin that allows for timeline markers (#2196)
|
||||
|
||||
4.5.0 (14.02.2021)
|
||||
------------------
|
||||
- Split channels: `overlay` param now properly displays a single canvas (#2161)
|
||||
- Fixed memory leak with `destroy()` in `WebAudio` backend (#1940)
|
||||
- Fixed `WaveSurfer.load(url)` not working when passing a HTMLMediaElement as
|
||||
the url parameter, with the WebAudio backend.
|
||||
- Microphone plugin: remove deprecated `MediaStream.stop` call (#2168)
|
||||
- Regions plugin: stop region dragging when mouse leaves canvas (#2158)
|
||||
|
||||
4.4.0 (13.01.2021)
|
||||
------------------
|
||||
|
||||
- Use Webpack 5 for build (#2093)
|
||||
- Fix seeking issues for `WebAudio` backend (#2149)
|
||||
- Use `splitChannelsOptions` to color wave bars (#2150)
|
||||
|
||||
4.3.0 (12.12.2020)
|
||||
------------------
|
||||
|
||||
- Add `relativeNormalization` option to maintain proportionality between
|
||||
waveforms when `splitChannels` and `normalize` are `true` (#2108)
|
||||
- WebAudio backend: set playback rate modifying directly the playback
|
||||
property of the source node (#2118)
|
||||
- Spectrogram plugin: Use `ImageData` to draw pixel-by-pixel (#2127)
|
||||
|
||||
4.2.0 (20.10.2020)
|
||||
------------------
|
||||
|
||||
- Fix performance issues with `seekTo` while audio is playing (#2045)
|
||||
- Trigger `waveform-ready` event when provided peaks are drawn (#2031)
|
||||
|
||||
4.1.1 (24.09.2020)
|
||||
------------------
|
||||
|
||||
- Revert Code cleanup for Observer class (#2069)
|
||||
|
||||
4.1.0 (16.09.2020)
|
||||
------------------
|
||||
|
||||
- Don't call HTMLMediaElement#load when given peaks and preload == 'none'.
|
||||
Prevents browsers from pre-fetching audio (#1969, #1990)
|
||||
- `seekTo` bugfix inc. basic unit tests (#2047)
|
||||
- Fix unhandled `AbortError` thrown during `cancelAjax` (#2063)
|
||||
- Remove `util.extend`: deprecated since v3.3.0 (#1995)
|
||||
- Remove `util.ajax`: deprecated since v3.0.0 (#2033)
|
||||
- Regions plugin:
|
||||
- Removed `col-resize` cursor when resize is disabled (#1985)
|
||||
- Improved and unified loop playback logic (#1868)
|
||||
- Check `minLength` before resizing region (#2001)
|
||||
- Dragging and resizing will continue outside canvas (#2006)
|
||||
- `regionsMinLength` parameter to assign a min length to those regions for which the `minLength` is not specified (#2009)
|
||||
- Revert PR #1926 click propagation on regions. Use event parameter passed
|
||||
in `region-click` if you need `stopPropagation`. (#2024)
|
||||
- Edgescroll works for both edges (#2011)
|
||||
- Microphone plugin: move to separate directory (#1997)
|
||||
- Minimap plugin: move plugin to separate directory (#1999)
|
||||
- Cursor plugin: move plugin to separate directory (#1998)
|
||||
- Elan plugin: move plugin to separate directory (#2019)
|
||||
- Spectrogram plugin: move to separate directory (#1996)
|
||||
- Mediasession plugin: move to separate directory (#2020)
|
||||
- Timeline plugin: move to separate directory (#2018)
|
||||
|
||||
4.0.1 (23.06.2020)
|
||||
------------------
|
||||
|
||||
- Fixes for event handling with certain plugins (regions, microphone).
|
||||
The crash would have involved '_disabledEventEmissions' (#1975)
|
||||
|
||||
4.0.0 (21.06.2020)
|
||||
------------------
|
||||
|
||||
- Fixed mediaelement-webaudio playback under Safari (#1964)
|
||||
- Fixed the `destroy` method of the `MediaElementWebAudio` backend. Instead of
|
||||
destroying only the media element, the audio nodes are disconnected and the
|
||||
audio context is closed. This was done by splitting the `destroy` method of the
|
||||
`WebAudio` backend, so it calls the new `destroyWebAudio` method to cancel
|
||||
everything related to WebAudio (#1927)
|
||||
- Removed private methods of plugins and generalized plugins' access, so they can be extended creating custom
|
||||
plugins (#1928)
|
||||
- Added plugin inheritance example (#1921)
|
||||
- Added compatibility for Gatsby and other static site generators (#1938)
|
||||
- Minimap plugin: added the ability to use a customized regions plugin using a new parameter
|
||||
`regionsPluginName` (#1943)
|
||||
- Fixed waveform display to not always connect to the sample=0 point (#1942)
|
||||
- Elan plugin: optional params.tiers (#1910)
|
||||
- Regions plugin:
|
||||
- Split `regions.js` into `region.js` (containing `Region` class) and `index.js`.
|
||||
Both files moved into the `src/plugin/regions` directory. This makes it easier
|
||||
to extend these classes and use them in custom plugins (#1934)
|
||||
- Fixed channelCount assignment (#1858)
|
||||
- Fixed click propagation issue (#1926)
|
||||
- Fixed switch loop region (#1929)
|
||||
- Added ability to specify time format for Regions tooltip using timeformatCallback (#1948)
|
||||
- Add `splitChannelsOptions` param and `setFilteredChannels` method to configure how channels are drawn (#1947)
|
||||
- Added checks in `minimap` plugin for `drawer` presence (#1953)
|
||||
- Add `setDisabledEventEmissions` method to optionally disable calls to event handlers for specific events (#1960)
|
||||
- Drawer: removed private methods to allow overriding them (#1962)
|
||||
- Add optional `setMute` method to backends to fix muting behavior with the `MediaElement` backend (#1966)
|
||||
|
||||
3.3.3 (16.04.2020)
|
||||
------------------
|
||||
|
||||
- Change default `desynchronized` drawing context attribute to `false` (#1908)
|
||||
|
||||
3.3.2 (07.04.2020)
|
||||
------------------
|
||||
|
||||
- Use `requestAnimationFrame` for clearWave (#1884)
|
||||
- Fix `Unable to get property 'toLowerCase' of undefined or null reference`
|
||||
in IE11 (#1771)
|
||||
- Spectrogram plugin: correct the hamming windfunc formula (#1850)
|
||||
|
||||
3.3.1 (13.01.2020)
|
||||
------------------
|
||||
|
||||
- Regions plugin:
|
||||
- Improve handles style support (#1839)
|
||||
- Add support for a context menu event on a region (#1844)
|
||||
- Fix for handle position when using `channelIdx` param (#1845)
|
||||
|
||||
3.3.0 (29.12.2019)
|
||||
------------------
|
||||
|
||||
- `wavesurfer.exportPCM` now accepts an optional `end` argument and returns
|
||||
a Promise (#1728)
|
||||
- Add `wavesurfer.setPlayEnd(position)` to set a point in seconds for
|
||||
playback to stop at (#1795)
|
||||
- Add `drawingContextAttributes` option and enable canvas `desynchronized`
|
||||
hint (#1642)
|
||||
- Add `barMinHeight` option (#1693)
|
||||
- Expose progress to the `dblclick` event (#1790)
|
||||
- Deprecate `util.extend` and replace usage with `Object.assign` (#1825)
|
||||
- Regions plugin:
|
||||
- Add `start` argument to `play` and `playLoop` methods (#1794)
|
||||
- Add `maxRegions` option to limit max numbers of created regions (#1793)
|
||||
- Don't assign to module object (#1823)
|
||||
- Allow setting the `handleColor` inside `addRegion` (#1798)
|
||||
- Disable drag selection before enabling it (#1698)
|
||||
- Add `channelIdx` option to select specific channel to draw on (#1829)
|
||||
- Refactor for improved readability (#1826)
|
||||
- Cursor plugin: fix time visibility (#1802)
|
||||
|
||||
3.2.0 (24.10.2019)
|
||||
------------------
|
||||
|
||||
- New `MediaElementWebAudio` backend (#1767):
|
||||
- Allows you to use Web Audio API with big audio files, loading audio
|
||||
like with MediaElement backend (HTML5 audio tag), so you can use the
|
||||
same methods of MediaElement backend for loading and playback. This way,
|
||||
the audio resource is not loaded entirely from server, but in ranges,
|
||||
allowing you to use WebAudio features, like filters, on audio files with
|
||||
a long duration. You can also supply peaks data, so the entire audio file
|
||||
does not have to be decoded.
|
||||
For example:
|
||||
```
|
||||
wavesurfer.load(url | HTMLMediaElement, peaks, preload, duration);
|
||||
wavesurfer.play();
|
||||
wavesurfer.setFilter(customFilter);
|
||||
```
|
||||
- Add `barRadius` option to create waveforms with rounded bars (#953)
|
||||
- Throw error when the url parameter supplied to `wavesurfer.load()`
|
||||
is empty (#1773, #1775)
|
||||
- Specify non-minified wavesurfer.js in `main` entry of `package.json` (#1759)
|
||||
- Add `dblclick` event listener to wavesurfer wrapper (#1764)
|
||||
- Fix `destroy()` in `MediaElement` backend (#1778)
|
||||
- Cursor plugin: flip position of time text to left of the cursor where needed
|
||||
to improve readability (#1776)
|
||||
- Regions plugin: change region end handler position (#1762, #1781)
|
||||
|
||||
3.1.0 (26.09.2019)
|
||||
------------------
|
||||
|
||||
- Add `autoCenter` and `autoCenterRate` options (#1699)
|
||||
- Make sure `isReady` is true before firing the `ready` event (#1749)
|
||||
- Improve fetch error messages (#1748)
|
||||
- Use `MediaElement` backend for browsers that don't support WebAudio (#1739)
|
||||
- Regions plugin:
|
||||
- Use `isResizing` and `isDragging` to filter events in
|
||||
region-updated listener (#1716)
|
||||
- Fix `playLoop` and `loop` option for clips with duration <15s (#1626)
|
||||
- Spectrogram plugin: fix variable name in click handler (#1742)
|
||||
- Minimap plugin: fix left/width calculations for regions on retina/4k
|
||||
screens (#1743)
|
||||
- New example: video-annotation (#1726)
|
||||
|
||||
3.0.0 (11.07.2019)
|
||||
------------------
|
||||
|
||||
- Add `wavesurfer.getActivePlugins()`: return map of plugins
|
||||
that are currently initialised
|
||||
- Replace usage of `util.ajax` with `util.fetchFile` (#1365)
|
||||
- Update progress when seeking with HTML media controls (#1535)
|
||||
- Make sure mute/volume is updated when using `MediaElement` backend (#1615)
|
||||
- Refactor `MultiCanvas` and add `CanvasEntry` class (#1617)
|
||||
- Fix `wavesurfer.isReady`: make it a public boolean, the
|
||||
broken `isReady` method is removed (#1597)
|
||||
- Add support for `Blob` output type in `wavesurfer.exportImage` (#1610)
|
||||
- Fix fallback to Audio Element in browsers that don't support Web Audio (#1614)
|
||||
- `util.getId()` now accepts a `prefix` argument (#1619)
|
||||
- Improve documentation for `xhr` option (#1656)
|
||||
- Fix: the `progressWave` should not be rendered when specifying the same
|
||||
value for the `progressColor` and `waveColor` options (#1620)
|
||||
- Cursor plugin:
|
||||
- Add `formatTimeCallback` option
|
||||
- Add `followCursorY` option (#1605)
|
||||
- Remove deprecated `enableCursor` method (#1646)
|
||||
- Hide the cursor elements before first mouseover if `hideOnBlur` is set (#1663)
|
||||
- Spectrogram plugin:
|
||||
- Fix `ready` listener when loading multiple audio files (#1572)
|
||||
- Allow user to specify a colorMap (#1436)
|
||||
- Regions plugin:
|
||||
- Fix `ready` listener when loading multiple audio files (#1602)
|
||||
- Add `snapToGridInterval` and `snapToGridOffset` options (#1632)
|
||||
- Allow drawing regions over existing regions, if the underlying ones are not
|
||||
draggable or resizable (#1633)
|
||||
- Calculate the duration at event time to allow predefined regions to be
|
||||
dragged and resized (#1673)
|
||||
- Remove deprecated `initRegions` method (#1646)
|
||||
- Timeline plugin: fix `ready` listener when loading multiple
|
||||
audio files
|
||||
- Minimap plugin: remove deprecated `initMinimap` method (#1646)
|
||||
|
||||
Check `UPGRADE.md` for backward incompatible changes since v2.x.
|
||||
|
||||
2.2.1 (18.03.2019)
|
||||
------------------
|
||||
|
||||
- Add `backgroundColor` option (#1118)
|
||||
- Spectrogram plugin: fix click handler (#1585)
|
||||
- Cursor plugin: fix `displayTime` (#1589)
|
||||
|
||||
2.2.0 (07.03.2019)
|
||||
------------------
|
||||
|
||||
- Add `rtl` option (#1296)
|
||||
- Fix peaks rendering issue on zooming and scrolling multicanvas (#1570)
|
||||
- Add `duration` option to specify an explicit audio length (#1441)
|
||||
- Spectrogram plugin: fix event listener removal (#1571)
|
||||
- Regions plugin: display regions before file load using `duration`
|
||||
option (#1441)
|
||||
- Build: switch to terser-webpack-plugin for minifying
|
||||
|
||||
2.1.3 (21.01.2019)
|
||||
------------------
|
||||
|
||||
- Fix removeOnAudioProcess for Safari (#1215, #1367, #1398)
|
||||
|
||||
2.1.2 (06.01.2019)
|
||||
------------------
|
||||
|
||||
- Fix computing peaks when buffer is not set (#1530)
|
||||
- Cursor plugin: fix displayed time (#1543)
|
||||
- Cursor plugin: document new params (#1516)
|
||||
- Add syntax highlighting in examples (#1522)
|
||||
|
||||
2.1.1 (18.11.2018)
|
||||
------------------
|
||||
|
||||
- Fix order of arguments for PluginClass.constructor (#1472)
|
||||
- Microphone plugin: Safari support (#1377)
|
||||
- Minimap plugin: fix styling issues and add support for zooming (#1464)
|
||||
- Timeline plugin: add duration parameter handling (#1491)
|
||||
- Cursor plugin: add showTime option (#1143)
|
||||
- Fix: progress bar did not reach 100% when audio file is small (#1502)
|
||||
|
||||
2.1.0 (29.09.2018)
|
||||
------------------
|
||||
|
||||
- Add wavesurfer.js logo, created by @entonbiba (#1409)
|
||||
- Library version number is now available as `WaveSurfer.VERSION` (#1430)
|
||||
- Fix `setSinkId` that used deprecated API (#1428)
|
||||
- Set `isReady` attribute to false when emptying wavesufer (#1396, #1403)
|
||||
- Microphone plugin: make it work in MS Edge browser (#627)
|
||||
- Timeline plugin: display more tick marks as the user zooms in closely (#1455)
|
||||
- Cursor plugin: fix `destroy` (#1435)
|
||||
|
||||
2.0.6 (14.06.2018)
|
||||
------------------
|
||||
|
||||
- Build library using webpack 4 (#1376)
|
||||
- Add `audioScriptProcessor` option to use custom script processor node (#1389)
|
||||
- Added `mute` and `volume` events (#1345)
|
||||
|
||||
2.0.5 (26.02.2018)
|
||||
------------------
|
||||
|
||||
- Fix `util.ajax` on iterating `requestHeaders` (#1329)
|
||||
- Add version information to distributed files (#1330)
|
||||
- Regions plugin: prevent click when creating / updating region (#1295)
|
||||
- Add `wavesurfer.isReady` method (#1333)
|
||||
|
||||
2.0.4 (14.02.2018)
|
||||
------------------
|
||||
|
||||
- Added `xhr` option to configure util.ajax for authorization (#1310, #1038, #1100)
|
||||
- Fix `setCurrentTime` method (#1292)
|
||||
- Fix `getScrollX` method: Check bounds when `scrollParent: true` (#1312)
|
||||
- Minimap plugin: fix initial load, canvas click did not work (#1265)
|
||||
- Regions plugin: fix dragging a region utside of scrollbar (#430)
|
||||
|
||||
2.0.3 (22.01.2018)
|
||||
------------------
|
||||
|
||||
- Added support for selecting different audio output devices using `setSinkId` (#1293)
|
||||
- Replace deprecated playbackRate.value setter (#1302)
|
||||
- Play method now properly returns a Promise (#1229)
|
||||
|
||||
2.0.2 (10.01.2018)
|
||||
------------------
|
||||
|
||||
- Added `barGap` parameter to set the space between bars (#1058)
|
||||
- Replace deprecated gain.value setter (#1277)
|
||||
- MediaElement backend: Update progress on pause events (#1267)
|
||||
- Restore missing MediaSession plugin (#1286)
|
||||
|
||||
2.0.1 (18.12.2017)
|
||||
------------------
|
||||
|
||||
- Core library and the plugins were refactored to be modular so it can be used with a module bundler
|
||||
- Code updated to ES6/ES7 syntax and is transpiled with babel and webpack
|
||||
- New plugin API
|
||||
- `MultiCanvas` renderer is now the default
|
||||
- Added getters and setters for height and color options (#1145)
|
||||
- Introduce an option to prevent removing media element on destroy (#1163)
|
||||
- Added duration parameter for the load function (#1239)
|
||||
- New soundtouch.js filter to preserve pitch when changing tempo (#149)
|
||||
- Add `getPlaybackRate` method (#1022)
|
||||
- Switched to BSD license (#1060)
|
||||
- Added `setCurrentTime` method
|
||||
- Added `util.debounce` (#993)
|
||||
|
||||
1.2.4 (11.11.2016)
|
||||
------------------
|
||||
|
||||
- Fix a problem of Web Audio not playing in Safari on initial load (#749)
|
||||
|
||||
1.2.3 (09.11.2016)
|
||||
------------------
|
||||
|
||||
- Add a 'waveform-ready' event, triggered when waveform is drawn with MediaElement backend (#736)
|
||||
- Add a 'preload' parameter to load function to choose the preload HTML5 audio attribute value if MediaElement backend is choosen (#854)
|
||||
|
||||
1.2.2 (31.10.2016)
|
||||
------------------
|
||||
|
||||
- Deterministic way to mute and unmute a track (#841)
|
||||
- Replace jasmine with karma / jasmine test suite (#849)
|
||||
- Regions plugin: fix a bug when clicking on scroll-bar in Firefox (#851)
|
||||
|
||||
1.2.1 (01.10.2016)
|
||||
------------------
|
||||
|
||||
- Added changelog (#824)
|
||||
- Correct AMD module name for plugins (#831)
|
||||
- Fix to remove small gaps between regions (#834)
|
1
public/scripts/wavesurfer/CNAME
Normal file
@ -0,0 +1 @@
|
||||
wavesurfer-js.org
|
29
public/scripts/wavesurfer/LICENSE
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2012-2021, katspaugh and contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
152
public/scripts/wavesurfer/README.md
Normal file
@ -0,0 +1,152 @@
|
||||
# [wavesurfer.js](https://wavesurfer-js.org)
|
||||
|
||||
[![npm version](https://img.shields.io/npm/v/wavesurfer.js.svg?style=flat)](https://www.npmjs.com/package/wavesurfer.js)
|
||||
![npm](https://img.shields.io/npm/dm/wavesurfer.js.svg) [![Join the chat at https://gitter.im/katspaugh/wavesurfer.js](https://badges.gitter.im/katspaugh/wavesurfer.js.svg)](https://gitter.im/katspaugh/wavesurfer.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Interactive navigable audio visualization using Web Audio and Canvas.
|
||||
|
||||
[![Screenshot](https://raw.githubusercontent.com/katspaugh/wavesurfer.js/gh-pages/example/screenshot.png "Screenshot")](https://wavesurfer-js.org)
|
||||
|
||||
See a [tutorial](https://wavesurfer-js.org/docs) and [examples](https://wavesurfer-js.org/examples) on [wavesurfer-js.org](https://wavesurfer-js.org).
|
||||
|
||||
## Browser support
|
||||
wavesurfer.js works only in [modern browsers supporting Web Audio](http://caniuse.com/audio-api).
|
||||
|
||||
It will fallback to Audio Element without graphics in other browsers (IE 11 and lower). You can also try [wavesurfer.swf](https://github.com/laurentvd/wavesurfer.swf) which is a Flash-based fallback.
|
||||
|
||||
## FAQ
|
||||
### Can the audio start playing before the waveform is drawn?
|
||||
Yes, if you use the `backend: 'MediaElement'` option. See here: https://wavesurfer-js.org/example/audio-element/. The audio will start playing as you press play. A thin line will be displayed until the whole audio file is downloaded and decoded to draw the waveform.
|
||||
|
||||
### Can drawing be done as file loads?
|
||||
No. Web Audio needs the whole file to decode it in the browser. You can however load pre-decoded waveform data to draw the waveform immediately. See here: https://wavesurfer-js.org/example/audio-element/ (the "Pre-recoded Peaks" section).
|
||||
|
||||
## API in examples
|
||||
|
||||
Choose a container:
|
||||
```html
|
||||
<div id="waveform"></div>
|
||||
```
|
||||
Create an instance, passing the container selector and [options](https://wavesurfer-js.org/docs/options.html):
|
||||
|
||||
```javascript
|
||||
var wavesurfer = WaveSurfer.create({
|
||||
container: '#waveform',
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple'
|
||||
});
|
||||
```
|
||||
|
||||
Subscribe to some [events](https://wavesurfer-js.org/docs/events.html):
|
||||
|
||||
```javascript
|
||||
wavesurfer.on('ready', function () {
|
||||
wavesurfer.play();
|
||||
});
|
||||
```
|
||||
|
||||
Load an audio file from a URL:
|
||||
|
||||
```javascript
|
||||
wavesurfer.load('example/media/demo.wav');
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
See the documentation on all available [methods](https://wavesurfer-js.org/docs/methods.html), [options](https://wavesurfer-js.org/docs/options.html) and [events](https://wavesurfer-js.org/docs/events.html) on the [homepage](https://wavesurfer-js.org/docs/).
|
||||
|
||||
## Upgrade
|
||||
|
||||
See the [upgrade](https://github.com/katspaugh/wavesurfer.js/blob/master/UPGRADE.md) document if you're upgrading from a previous version of wavesurfer.js.
|
||||
|
||||
## Using with a module bundler
|
||||
|
||||
Install Wavesurfer:
|
||||
```bash
|
||||
npm install wavesurfer.js --save
|
||||
# or
|
||||
yarn add wavesurfer.js
|
||||
```
|
||||
|
||||
Use it with a module system like this:
|
||||
```javascript
|
||||
// import
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
|
||||
// commonjs/requirejs
|
||||
var WaveSurfer = require('wavesurfer.js');
|
||||
|
||||
// amd
|
||||
define(['WaveSurfer'], function(WaveSurfer) {
|
||||
// ... code
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## Related projects
|
||||
|
||||
For a list of projects using wavesurfer.js, check out
|
||||
[the projects page](https://wavesurfer-js.org/projects/).
|
||||
|
||||
## Development
|
||||
|
||||
[![Build Status](https://github.com/katspaugh/wavesurfer.js/workflows/wavesurfer.js/badge.svg?branch=master)](https://github.com/katspaugh/wavesurfer.js/actions?workflow=wavesurfer.js)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/katspaugh/wavesurfer.js/badge.svg)](https://coveralls.io/github/katspaugh/wavesurfer.js)
|
||||
![Size](https://img.shields.io/bundlephobia/minzip/wavesurfer.js.svg?style=flat)
|
||||
|
||||
Install development dependencies:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
Development tasks automatically rebuild certain parts of the library when files are changed (`start` – wavesurfer, `start:plugins` – plugins). Start a dev task and go to `localhost:8080/example/` to test the current build.
|
||||
|
||||
Start development server for core library:
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
Start development server for plugins:
|
||||
|
||||
```
|
||||
npm run start:plugins
|
||||
```
|
||||
|
||||
Build all the files. (generated files are placed in the `dist` directory.)
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
Running tests only:
|
||||
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
Build documentation with esdoc (generated files are placed in the `doc` directory.)
|
||||
```
|
||||
npm run doc
|
||||
```
|
||||
|
||||
If you want to use [the VS Code - Debugger for Chrome](https://github.com/Microsoft/vscode-chrome-debug), there is already a [launch.json](.vscode/launch.json) with a properly configured ``sourceMapPathOverrides`` for you.
|
||||
|
||||
## Editing documentation
|
||||
The homepage and documentation files are maintained in the [`gh-pages` branch](https://github.com/katspaugh/wavesurfer.js/tree/gh-pages). Contributions to the documentation are especially welcome.
|
||||
|
||||
## Updating the NPM package
|
||||
When preparing a new release, update the version in the `package.json` and have it merged to master. The new version of the package will be published to NPM automatically via GitHub Actions.
|
||||
|
||||
## Credits
|
||||
|
||||
The main maintainer: <img src="https://avatars.githubusercontent.com/u/305679" width="16" height="16" /> [Thijs Triemstra](https://github.com/thijstriemstra)
|
||||
|
||||
Many thanks to [all the awesome contributors](https://github.com/katspaugh/wavesurfer.js/contributors)!
|
||||
|
||||
## License
|
||||
|
||||
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
|
||||
|
||||
This work is licensed under a
|
||||
[BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
|
69
public/scripts/wavesurfer/UPGRADE.md
Normal file
@ -0,0 +1,69 @@
|
||||
# Upgrade
|
||||
|
||||
## Upgrading to version 3 from version 2
|
||||
|
||||
- `util.ajax` was deprecated; use `util.fetchFile instead.
|
||||
- The `xhr` wavesurfer option has changed to work with `util.fetchFile`.
|
||||
- The `MultiCanvas` renderer was refactored and a new `CanvasEntry` class was added to represent
|
||||
a canvas instance in a `MultiCanvas`.
|
||||
|
||||
## Upgrading to version 2 from version 1
|
||||
|
||||
The wavesurfer.js core library and the plugins were refactored to be modular so it can be used with a module bundler.
|
||||
You can still use wavesurfer without, e.g. with `<script>` tags. The code was also updated to ES6/ES7 syntax and
|
||||
is transpiled with Babel and Webpack. Read below how to update your code.
|
||||
|
||||
The API has mostly stayed the same but there are some changes to consider:
|
||||
|
||||
1. **MultiCanvas renderer is now the default:** It provides all functionality of the Canvas renderer. – Most likely you
|
||||
can simply remove the renderer option – The Canvas renderer has been removed. (The `renderer` option still exists but
|
||||
wavesurfer expects it to be a renderer object, not merely a string.)
|
||||
|
||||
2. **Constructor functions instead of object constructors**
|
||||
|
||||
```javascript
|
||||
// Old:
|
||||
var wavesurfer = Object.create(WaveSurfer);
|
||||
Wavesurfer.init(options);
|
||||
|
||||
// New:
|
||||
var wavesurfer = WaveSurfer.create(options);
|
||||
// ... or
|
||||
var wavesurfer = new WaveSurfer(options);
|
||||
wavesurfer.init();
|
||||
```
|
||||
|
||||
3. **New plugin API:** Previously all plugins had their own initialisation API. The new API replaces all
|
||||
these different ways to do the same thing with one plugin API built into the core library. Plugins are now
|
||||
added as a property of the wavesurfer configuration object during creation. You don't need to initialise the
|
||||
plugins yourself anymore. Below is an example of initialising wavesurfer with plugins (Note the different ways
|
||||
to import the library at the top):
|
||||
|
||||
```javascript
|
||||
// EITHER - accessing modules with <script> tags
|
||||
var WaveSurfer = window.WaveSurfer;
|
||||
var TimelinePlugin = window.WaveSurfer.timeline;
|
||||
var MinimapPlugin = window.WaveSurfer.minimap;
|
||||
|
||||
// OR - importing as es6 module
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import TimelinePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.timeline.min.js';
|
||||
import MinimapPlugin from 'wavesurfer.js/dist/plugin/wavesurfer.minimap.min.js';
|
||||
|
||||
// OR - importing as require.js/commonjs modules
|
||||
var WaveSurfer = require('wavesurfer.js');
|
||||
var TimelinePlugin = require('wavesurfer.js/dist/plugin/wavesurfer.timeline.min.js');
|
||||
var MinimapPlugin = require('wavesurfer.js/dist/plugin/wavesurfer.minimap.min.js');
|
||||
|
||||
// ... initialising waveform with plugins
|
||||
var wavesurfer = WaveSurfer.create({
|
||||
container: '#waveform',
|
||||
waveColor: 'violet',
|
||||
plugins: [
|
||||
TimelinePlugin.create({
|
||||
container: '#wave-timeline'
|
||||
}),
|
||||
MinimapPlugin.create()
|
||||
]
|
||||
});
|
||||
```
|
28
public/scripts/wavesurfer/appveyor.yml
Normal file
@ -0,0 +1,28 @@
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: ''
|
||||
install:
|
||||
- choco install -y googlechrome --ignore-checksums
|
||||
- choco install -y firefox --ignore-checksums
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- set CI=true
|
||||
- npm install --global npm@latest
|
||||
- set PATH=%APPDATA%\npm;%PATH%
|
||||
- npm install
|
||||
matrix:
|
||||
fast_finish: true
|
||||
# Disable automatic builds
|
||||
build: off
|
||||
# Do not build on gh tags
|
||||
skip_tags: true
|
||||
shallow_clone: true
|
||||
test_script:
|
||||
- node --version
|
||||
- npm --version
|
||||
- npm run lint
|
||||
- npm run build
|
||||
- npm run test
|
||||
- npm run doc
|
||||
cache:
|
||||
- '%APPDATA%\npm-cache'
|
||||
- node_modules -> package.json
|
BIN
public/scripts/wavesurfer/assets/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
11
public/scripts/wavesurfer/assets/readme.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Logo is free to use with this project, link back to the official github page to support the project.
|
||||
https://github.com/katspaugh/wavesurfer.js
|
||||
|
||||
Available Logo Versions:
|
||||
- svg text logo (black/white)
|
||||
- svg full logo (black/white)
|
||||
- svg symbol logo
|
||||
|
||||
- png text logo (black/white)
|
||||
- png full logo (black/white)
|
||||
- png symbol logo
|
BIN
public/scripts/wavesurfer/assets/wavesurfer-black-text.png
Normal file
After Width: | Height: | Size: 25 KiB |
52
public/scripts/wavesurfer/assets/wavesurfer-black-text.svg
Normal file
@ -0,0 +1,52 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1211" height="223" viewBox="0 0 1211 223">
|
||||
<metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39 ">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""/>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<?xpacket end="w"?></metadata>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
filter: url(#filter);
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
font-size: 291.667px;
|
||||
text-anchor: middle;
|
||||
fill: #e5e5e5;
|
||||
font-family: Verdana;
|
||||
}
|
||||
</style>
|
||||
<filter id="filter" filterUnits="userSpaceOnUse">
|
||||
<feFlood result="flood" flood-color="#171717"/>
|
||||
<feComposite result="composite" operator="in" in2="SourceGraphic"/>
|
||||
<feBlend result="blend" in2="SourceGraphic"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="black" class="cls-1">
|
||||
<text id="wavesurfer.js" class="cls-2" transform="translate(604.972 165.274) scale(0.635 0.642)"><tspan x="0">wavesurfer.js</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/scripts/wavesurfer/assets/wavesurfer-black.png
Normal file
After Width: | Height: | Size: 53 KiB |
58
public/scripts/wavesurfer/assets/wavesurfer-black.svg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
public/scripts/wavesurfer/assets/wavesurfer-symbol.png
Normal file
After Width: | Height: | Size: 27 KiB |
31
public/scripts/wavesurfer/assets/wavesurfer-symbol.svg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
public/scripts/wavesurfer/assets/wavesurfer-white-text.png
Normal file
After Width: | Height: | Size: 50 KiB |
59
public/scripts/wavesurfer/assets/wavesurfer-white-text.svg
Normal file
@ -0,0 +1,59 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1211" height="223" viewBox="0 0 1211 223">
|
||||
<metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39 ">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""/>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<?xpacket end="w"?></metadata>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
stroke: #000;
|
||||
stroke-linejoin: round;
|
||||
stroke-opacity: 0.11;
|
||||
stroke-width: 2px;
|
||||
filter: url(#filter);
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
font-size: 291.667px;
|
||||
text-anchor: middle;
|
||||
fill: #e5e5e5;
|
||||
font-family: Verdana;
|
||||
}
|
||||
</style>
|
||||
<filter id="filter" filterUnits="userSpaceOnUse">
|
||||
<feFlood result="flood" flood-color="#fdfdfd"/>
|
||||
<feComposite result="composite" operator="in" in2="SourceGraphic"/>
|
||||
<feBlend result="blend" in2="SourceGraphic"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g style="fill: ; filter: url(#filter)">
|
||||
<g id="white" class="cls-1" style="stroke: inherit; filter: none; fill: inherit">
|
||||
<text id="wavesurfer.js" class="cls-2" transform="translate(604.972 165.274) scale(0.635 0.642)"><tspan x="0">wavesurfer.js</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<use xlink:href="#white" style="stroke: #000; filter: none; fill: none"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/scripts/wavesurfer/assets/wavesurfer-white.png
Normal file
After Width: | Height: | Size: 73 KiB |
65
public/scripts/wavesurfer/assets/wavesurfer-white.svg
Normal file
After Width: | Height: | Size: 34 KiB |
14
public/scripts/wavesurfer/bower.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "wavesurfer.js",
|
||||
"version": "4.6.0",
|
||||
"homepage": "https://wavesurfer-js.org",
|
||||
"authors": [
|
||||
"katspaugh <katspaugh@gmail.com>"
|
||||
],
|
||||
"description": "Navigable waveform built on Web Audio and Canvas",
|
||||
"main": "dist/wavesurfer.js",
|
||||
"keywords": [
|
||||
"waveform", "audio", "music", "player"
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
31
public/scripts/wavesurfer/build-config/fragments/banner.js
Normal file
@ -0,0 +1,31 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const datefns = require('date-fns');
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
const date = datefns.format(new Date(), 'yyyy-MM-dd');
|
||||
const pckg = require(path.join(rootDir, 'package.json'));
|
||||
|
||||
// library JS banner with copyright and version info
|
||||
// prettier-ignore
|
||||
const jsBanner = `${pckg.name} ${pckg.version} (${date})
|
||||
${pckg.homepage}
|
||||
@license ${pckg.license}`;
|
||||
const libBanner = new webpack.BannerPlugin({
|
||||
banner: jsBanner,
|
||||
test: /\.js$/
|
||||
});
|
||||
|
||||
// plugin JS banner with copyright and version info
|
||||
// prettier-ignore
|
||||
const jsPluginBanner = `${pckg.name} [name] plugin ${pckg.version} (${date})
|
||||
${pckg.homepage}
|
||||
@license ${pckg.license}`;
|
||||
const pluginBanner = new webpack.BannerPlugin({
|
||||
banner: jsPluginBanner,
|
||||
test: /\.js$/
|
||||
});
|
||||
|
||||
module.exports = { libBanner, pluginBanner };
|
46
public/scripts/wavesurfer/build-config/fragments/common.js
Normal file
@ -0,0 +1,46 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const datefns = require('date-fns');
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
const pckg = require(path.join(rootDir, 'package.json'));
|
||||
|
||||
// enable logging of deprecation warnings stacktrace
|
||||
process.traceDeprecation = true;
|
||||
|
||||
// inject JS version number
|
||||
const jsVersionPlugin = new webpack.DefinePlugin({
|
||||
__VERSION__: JSON.stringify(pckg.version)
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
context: rootDir,
|
||||
mode: 'development',
|
||||
output: {
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: true,
|
||||
globalObject: 'this'
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
stats: {
|
||||
colors: true
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [jsVersionPlugin]
|
||||
};
|
27
public/scripts/wavesurfer/build-config/fragments/dev.js
Normal file
@ -0,0 +1,27 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
devtool: 'eval-source-map',
|
||||
devServer: {
|
||||
static: [
|
||||
{
|
||||
directory: path.resolve(__dirname, '..', '..'),
|
||||
staticOptions: {},
|
||||
serveIndex: true,
|
||||
watch: {
|
||||
ignored: [
|
||||
/.chrome/,
|
||||
/node_modules/,
|
||||
/bower_components/,
|
||||
/coverage/,
|
||||
/docs/,
|
||||
/spec/
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
18
public/scripts/wavesurfer/build-config/fragments/htmlinit.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const path = require('path');
|
||||
const banner = require('./banner');
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
'html-init': path.join(rootDir, 'src', 'html-init.js')
|
||||
},
|
||||
output: {
|
||||
path: path.join(rootDir, 'dist'),
|
||||
filename: 'wavesurfer-[name].js',
|
||||
library: ['WaveSurfer', '[name]']
|
||||
},
|
||||
plugins: [banner.libBanner]
|
||||
};
|
18
public/scripts/wavesurfer/build-config/fragments/main.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const path = require('path');
|
||||
const banner = require('./banner');
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
wavesurfer: path.join(rootDir, 'src', 'wavesurfer.js')
|
||||
},
|
||||
output: {
|
||||
path: path.join(rootDir, 'dist'),
|
||||
filename: '[name].js',
|
||||
library: 'WaveSurfer'
|
||||
},
|
||||
plugins: [banner.libBanner]
|
||||
};
|
22
public/scripts/wavesurfer/build-config/fragments/min.js
Normal file
@ -0,0 +1,22 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
parallel: true,
|
||||
extractComments: false,
|
||||
terserOptions: {
|
||||
output: {
|
||||
// preserve license comments
|
||||
comments: /@license/i
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
};
|
79
public/scripts/wavesurfer/build-config/fragments/plugins.js
Normal file
@ -0,0 +1,79 @@
|
||||
/* eslint-env node */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const banner = require('./banner');
|
||||
|
||||
const rootDir = path.resolve(__dirname, '..', '..');
|
||||
const pluginSrcDir = path.join(rootDir, 'src', 'plugin');
|
||||
|
||||
// find plugins
|
||||
const PLUGINS = [];
|
||||
fs.readdirSync(pluginSrcDir).forEach(plugin => {
|
||||
findInDirectory(plugin, pluginSrcDir);
|
||||
});
|
||||
|
||||
/**
|
||||
* findInDirectory - Description: search plugins and push them in PLUGINS Array: if finds a directory, take the plugin
|
||||
* called index.js
|
||||
*
|
||||
* @param {String} plugin Name of plugin
|
||||
*
|
||||
* @param {String} directory Path of plugin directory
|
||||
*/
|
||||
function findInDirectory(plugin, directory) {
|
||||
const pluginPath = path.join(directory, plugin);
|
||||
let relativePluginPath = null;
|
||||
|
||||
if (fs.statSync(pluginPath).isDirectory()) {
|
||||
fs.readdirSync(pluginPath).forEach(pluginInDir => {
|
||||
if (pluginInDir === 'index.js') {
|
||||
const pathInDirectory = path.join(pluginPath, pluginInDir);
|
||||
relativePluginPath = path.relative(pluginSrcDir, pathInDirectory);
|
||||
PLUGINS.push(relativePluginPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
relativePluginPath = path.relative(pluginSrcDir, pluginPath);
|
||||
PLUGINS.push(relativePluginPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* buildPluginEntry - Description: build the plugin entry based on PLUGINS array: if plugin name is index.js, it is
|
||||
* it is renamed with his parent directory name
|
||||
*
|
||||
* @param {Array} plugins Name of plugins in src/plugin
|
||||
*
|
||||
* @returns {object} Entry object { name: nameUrl }
|
||||
*/
|
||||
function buildPluginEntry(plugins) {
|
||||
const result = {};
|
||||
plugins.forEach(
|
||||
plugin => {
|
||||
let basename = path.basename(plugin, '.js');
|
||||
if (basename === 'index') {
|
||||
basename = path.basename(path.dirname(plugin));
|
||||
}
|
||||
return (result[path.basename(basename, '.js')] = path.join(
|
||||
pluginSrcDir,
|
||||
plugin
|
||||
));
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: buildPluginEntry(PLUGINS),
|
||||
output: {
|
||||
path: path.join(rootDir, 'dist', 'plugin'),
|
||||
filename: 'wavesurfer.[name].js',
|
||||
library: ['WaveSurfer', '[name]'],
|
||||
publicPath: 'localhost:8080/dist/plugin/'
|
||||
},
|
||||
devServer: {
|
||||
publicPath: 'localhost:8080/dist/plugin/'
|
||||
},
|
||||
plugins: [banner.pluginBanner]
|
||||
};
|
5
public/scripts/wavesurfer/build-config/fragments/prod.js
Normal file
@ -0,0 +1,5 @@
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = {
|
||||
devtool: 'source-map'
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const dev = require('./fragments/dev');
|
||||
const htmlinit = require('./fragments/htmlinit');
|
||||
|
||||
module.exports = merge(common, dev, htmlinit);
|
@ -0,0 +1,8 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const dev = require('./fragments/dev');
|
||||
const main = require('./fragments/main');
|
||||
|
||||
module.exports = merge(common, dev, main);
|
@ -0,0 +1,8 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const dev = require('./fragments/dev');
|
||||
const plugins = require('./fragments/plugins');
|
||||
|
||||
module.exports = merge(common, dev, plugins);
|
@ -0,0 +1,8 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const prod = require('./fragments/prod');
|
||||
const htmlinit = require('./fragments/htmlinit');
|
||||
|
||||
module.exports = merge(common, prod, htmlinit);
|
13
public/scripts/wavesurfer/build-config/webpack.prod.htmlinit.min.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const prod = require('./fragments/prod');
|
||||
const min = require('./fragments/min');
|
||||
const htmlinit = require('./fragments/htmlinit');
|
||||
|
||||
module.exports = merge(common, prod, min, htmlinit, {
|
||||
output: {
|
||||
filename: 'wavesurfer-[name].min.js'
|
||||
}
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const prod = require('./fragments/prod');
|
||||
const main = require('./fragments/main');
|
||||
|
||||
module.exports = merge(common, prod, main);
|
13
public/scripts/wavesurfer/build-config/webpack.prod.main.min.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const prod = require('./fragments/prod');
|
||||
const min = require('./fragments/min');
|
||||
const main = require('./fragments/main');
|
||||
|
||||
module.exports = merge(common, prod, min, main, {
|
||||
output: {
|
||||
filename: 'wavesurfer.min.js'
|
||||
}
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const prod = require('./fragments/prod');
|
||||
const plugins = require('./fragments/plugins');
|
||||
|
||||
module.exports = merge(common, prod, plugins);
|
13
public/scripts/wavesurfer/build-config/webpack.prod.plugins.min.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
|
||||
const common = require('./fragments/common');
|
||||
const prod = require('./fragments/prod');
|
||||
const min = require('./fragments/min');
|
||||
const plugins = require('./fragments/plugins');
|
||||
|
||||
module.exports = merge(common, prod, min, plugins, {
|
||||
output: {
|
||||
filename: 'wavesurfer.[name].min.js'
|
||||
}
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
<html ng-app="mdWavesurferApp">
|
||||
<head>
|
||||
<title>wavesurfer.js | Angular Material</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href='http://fonts.googleapis.com/css?family=Roboto:400,500' rel='stylesheet' type='text/css'>
|
||||
<link href='https://cdnjs.cloudflare.com/ajax/libs/material-design-iconic-font/2.2.0/css/material-design-iconic-font.min.css'
|
||||
rel='stylesheet' type='text/css'>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/github.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.0-rc3/angular-material.min.css"
|
||||
rel="stylesheet" type="text/css"/>
|
||||
|
||||
<link rel="stylesheet" href="main.css"/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-animate.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-aria.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.0-rc3/angular-material.min.js"></script>
|
||||
<script type="text/javascript" src="../../dist/wavesurfer.js"></script>
|
||||
<script type="text/javascript" src="wavesurfer.directive.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body ng-controller="MainController">
|
||||
<md-wavesurfer-audio player-wave-color="gray" player-progress-color="black"
|
||||
player-backend="MediaElement">
|
||||
<!-- This is analogous to HTML <source> element -->
|
||||
<md-wavesurfer-source src="../media/demo.wav" title="czskamaarù – Trou"></md-wavesurfer-source>
|
||||
<md-wavesurfer-source src="../panner/media.wav" title="日本人の話し"></md-wavesurfer-source>
|
||||
<md-wavesurfer-source src="../elan/transcripts/001z.mp3"
|
||||
title="Рассказы о сновидениях"></md-wavesurfer-source>
|
||||
<!-- <md-wavesurfer-source -->
|
||||
<!-- src="http://download.wavetlan.com/SVV/Media/HTTP/MP3/Nero_SmartTrax/NeroSmartTrax_test1_MPEG2_Stereo_CBR_48kbps_22050Hz.mp3" -->
|
||||
<!-- title="Remote: Nero SmartTrax- Test 1"></md-wavesurfer-source> -->
|
||||
<!-- <md-wavesurfer-source -->
|
||||
<!-- src="http://download.wavetlan.com/SVV/Media/HTTP/MP3/Nero_SmartTrax/NeroSmartTrax_test2_MPEG1_Mono_CBR_64kbps_44100Hz.mp3" -->
|
||||
<!-- title="Remote: Nero SmartTrax- Test 2"></md-wavesurfer-source> -->
|
||||
|
||||
<!-- Long list, using previously loaded files, need to test container.-->
|
||||
<md-wavesurfer-source ng-repeat="item in longList" src="{{item.url}}"
|
||||
title="{{item.title}}"></md-wavesurfer-source>
|
||||
|
||||
|
||||
</md-wavesurfer-audio>
|
||||
</body>
|
||||
</html>
|
35
public/scripts/wavesurfer/example/angular-material/main.css
Normal file
@ -0,0 +1,35 @@
|
||||
.text-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left !important;;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right !important;;
|
||||
}
|
||||
|
||||
.text-justify {
|
||||
text-align: justify !important;;
|
||||
}
|
||||
|
||||
md-icon[md-font-icon] {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
md-toolbar.md-toolbar-sm .md-toolbar-tools {
|
||||
max-height: 32px;
|
||||
padding: 6px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
md-toolbar.md-toolbar-sm {
|
||||
min-height: 24px;
|
||||
}
|
||||
.md-player-controls .md-button {
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
|
||||
|
30
public/scripts/wavesurfer/example/angular-material/main.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/* global angular */
|
||||
|
||||
/**
|
||||
* Created by intelWorx on 19/11/2015.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
angular
|
||||
.module('mdWavesurferApp', ['mdWavesurfer'])
|
||||
.config(function($mdIconProvider) {
|
||||
//$mdIconProvider.fontSet('zmdi', 'fontawesome');
|
||||
})
|
||||
.controller('MainController', [
|
||||
'$scope',
|
||||
function($scope) {
|
||||
$scope.urls = [
|
||||
'../media/demo.wav',
|
||||
'../panner/media.wav',
|
||||
'../elan/transcripts/001z.mp3'
|
||||
];
|
||||
$scope.longList = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
$scope.longList.push({
|
||||
title: 'Long List test: ' + i,
|
||||
url: $scope.urls[Math.floor(3 * Math.random())]
|
||||
});
|
||||
}
|
||||
}
|
||||
]);
|
||||
})();
|
@ -0,0 +1,30 @@
|
||||
<md-card layout="column">
|
||||
<div ng-transclude=""></div>
|
||||
<md-toolbar class="md-toolbar-sm">
|
||||
<div class="text-center md-toolbar-tools">
|
||||
<h2 style="font-size: 24px;">Audio Player</h2>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-list layout="column">
|
||||
<md-virtual-repeat-container style="height: 250px">
|
||||
<md-list-item md-virtual-repeat="track in audio.tracks" md-start-index="audio.selectedIndex"
|
||||
ng-click="audio.setTrack($index, true)">
|
||||
<p>
|
||||
{{$index+1}}. {{track.title}}
|
||||
</p>
|
||||
|
||||
<div class="text-right md-secondary">
|
||||
{{track.duration | mdWavesurferTimeFormat}}
|
||||
</div>
|
||||
<md-divider ng-if="$index < audio.tracks.length-1"></md-divider>
|
||||
</md-list-item>
|
||||
</md-virtual-repeat-container>
|
||||
</md-list>
|
||||
<md-wavesurfer-player
|
||||
url="{{audio.currentTrack.src}}#{{audio.selectedIndex}}"
|
||||
title="{{audio.selectedIndex+1}}. {{audio.currentTrack.title}}"
|
||||
extra-buttons="audio.extraButtons"
|
||||
properties="audio.playerProperties"
|
||||
>
|
||||
</md-wavesurfer-player>
|
||||
</md-card>
|
@ -0,0 +1,70 @@
|
||||
<div layout="column" class="audioPlayerWrapper">
|
||||
<md-toolbar class="md-toolbar-sm {{control.themeClass}} {{control.toolbarClass}} " style="color: #333333; background:
|
||||
none;">
|
||||
<div class="md-toolbar-tools" layout="row">
|
||||
<div flex="initial" layout-align="center start">
|
||||
{{control.currentTime | mdWavesurferTimeFormat}}
|
||||
</div>
|
||||
<div flex="grow" class="text-center">
|
||||
{{control.title}}
|
||||
</div>
|
||||
<div flex="initial" layout-align="center end" class="text-right">
|
||||
{{control.surfer.getDuration() | mdWavesurferTimeFormat}}
|
||||
</div>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-divider></md-divider>
|
||||
<md-content>
|
||||
<div class="waveSurferWave"></div>
|
||||
</md-content>
|
||||
<md-divider md-inset></md-divider>
|
||||
<md-toolbar class="{{control.themeClass}} {{control.toolbarClass}} md-player-controls">
|
||||
<div layout="row" class="md-toolbar-tools">
|
||||
<div flex="initial" layout-align="center start" ng-show="control.extraButtons.length">
|
||||
<md-button ng-click="btn.action()"
|
||||
ng-repeat="btn in control.extraButtons" class="{{control.themeClass}} {{btn.class}}">
|
||||
<md-tooltip>
|
||||
{{btn.title}}
|
||||
</md-tooltip>
|
||||
<md-icon md-font-icon="{{btn.icon}}"></md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
|
||||
<div flex layout="row" layout-align="center center">
|
||||
<md-button ng-click="control.surfer.skipBackward()" type="button"
|
||||
ng-disabled="!control.surfer.isPlaying()">
|
||||
<md-tooltip>
|
||||
Rewind
|
||||
</md-tooltip>
|
||||
<md-icon md-font-icon="zmdi zmdi-fast-rewind"></md-icon>
|
||||
</md-button>
|
||||
|
||||
<md-button ng-disabled="!control.isReady" type="button"
|
||||
ng-click="control.surfer.playPause()">
|
||||
<md-tooltip>
|
||||
{{control.surfer.isPlaying() ? 'Pause' : 'Play'}}
|
||||
</md-tooltip>
|
||||
<md-icon ng-show="control.surfer.isPlaying()" md-font-icon="zmdi zmdi-pause"></md-icon>
|
||||
<md-icon ng-show="!control.surfer.isPlaying()" md-font-icon="zmdi zmdi-play"></md-icon>
|
||||
</md-button>
|
||||
|
||||
<md-button type="button" ng-click="control.surfer.skipForward()"
|
||||
ng-disabled="!control.surfer.isPlaying()">
|
||||
<md-tooltip>
|
||||
Skip forward
|
||||
</md-tooltip>
|
||||
<md-icon md-font-icon="zmdi zmdi-fast-forward"></md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
<div flex="initial" layout-align="center end" >
|
||||
<md-button type="button" ng-click="control.toggleMute()" ng-disabled="!control.surfer.isPlaying()">
|
||||
<md-tooltip>
|
||||
Toggle mute
|
||||
</md-tooltip>
|
||||
<md-icon md-font-icon="zmdi zmdi-volume-off" ng-show="control.isMute"></md-icon>
|
||||
<md-icon md-font-icon="zmdi zmdi-volume-up" ng-show="!control.isMute"></md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
</div>
|
@ -0,0 +1,463 @@
|
||||
/* global angular */
|
||||
|
||||
/**
|
||||
* Created by intelWorx on 19/11/2015.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Main module, your application should depend on this
|
||||
* @module {mdWavesurfer}
|
||||
*/
|
||||
let app = angular.module('mdWavesurfer', ['ngMaterial']);
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $mdWavesurferUtils
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Utility service for this directive, exposes method:
|
||||
* - getLength(url), which returns a promise for the length of the audio specified by URL
|
||||
*
|
||||
* ```js
|
||||
* app.directive('myFancyDirective', function(mdWavesurferUtils) {
|
||||
* return {
|
||||
* restrict: 'e',
|
||||
* link: function(scope, el, attrs) {
|
||||
* mdWavesurferUtils(attrs.url)
|
||||
* .then(function(l){
|
||||
* scope.length = l;
|
||||
* }, function(){
|
||||
* someErrorhandler()
|
||||
* })
|
||||
* ;
|
||||
* }
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
app.factory('mdWavesurferUtils', [
|
||||
'$q',
|
||||
'$document',
|
||||
'$timeout',
|
||||
function($q, $document, $timeout) {
|
||||
return {
|
||||
getLength: function(object) {
|
||||
let deferred = $q.defer();
|
||||
let estimateLength = function(url) {
|
||||
let audio = $document[0].createElement('audio');
|
||||
audio.src = url;
|
||||
audio.addEventListener(
|
||||
'loadeddata',
|
||||
function listener() {
|
||||
deferred.resolve(this.duration);
|
||||
audio.removeEventListener(
|
||||
'loadeddata',
|
||||
listener
|
||||
);
|
||||
audio.src = 'data:audio/mpeg,0'; //destroy loading.
|
||||
}
|
||||
);
|
||||
|
||||
audio.addEventListener('error', function(e) {
|
||||
deferred.resolve(e.target.error);
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof object === 'string') {
|
||||
//this is a URL
|
||||
estimateLength(object);
|
||||
} else {
|
||||
$timeout(function() {
|
||||
deferred.reject(
|
||||
new DOMError(
|
||||
'NotSupportedError',
|
||||
'Specified argument is not supported'
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name mdWavesurferTimeFormat
|
||||
*
|
||||
* Simple filter to convert value in seconds to MM:SS format
|
||||
*
|
||||
* @param Number duration in seconds
|
||||
*/
|
||||
app.filter('mdWavesurferTimeFormat', function() {
|
||||
return function(input) {
|
||||
if (!input) {
|
||||
return '00:00';
|
||||
}
|
||||
|
||||
const minutes = Math.floor(input / 60);
|
||||
const seconds = Math.floor(input) % 60;
|
||||
|
||||
return (
|
||||
(minutes < 10 ? '0' : '') +
|
||||
minutes +
|
||||
':' +
|
||||
(seconds < 10 ? '0' : '') +
|
||||
seconds
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('mdWavesurferAudioController', [
|
||||
'$attrs',
|
||||
'$element',
|
||||
function(attributes, $element) {
|
||||
let audio = this;
|
||||
|
||||
audio.tracks = [];
|
||||
audio.selectedIndex = audio.selectedIndex || 0;
|
||||
audio.currentTrack = null;
|
||||
|
||||
//adds to an audio track
|
||||
audio.addTrack = function(trackScope) {
|
||||
if (audio.tracks.indexOf(trackScope) < 0) {
|
||||
audio.tracks.push(trackScope);
|
||||
}
|
||||
|
||||
if (!audio.currentTrack) {
|
||||
audio.currentTrack = audio.tracks[audio.selectedIndex];
|
||||
}
|
||||
};
|
||||
|
||||
//remove audio track
|
||||
audio.removeTrack = function(trackScope) {
|
||||
const idx = audio.tracks.indexOf(trackScope);
|
||||
if (idx >= 0) {
|
||||
audio.tracks.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
|
||||
audio.playerProperties = {};
|
||||
let nKey;
|
||||
for (let attr in attributes) {
|
||||
if (attr.match(/^player/)) {
|
||||
nKey = attr.replace(/^player([A-Z])/, function(m, $1) {
|
||||
return $1.toLowerCase();
|
||||
});
|
||||
audio.playerProperties[nKey] = attributes[attr];
|
||||
}
|
||||
}
|
||||
|
||||
let getPlayer = function() {
|
||||
return $element
|
||||
.find('md-wavesurfer-player')
|
||||
.controller('mdWavesurferPlayer');
|
||||
};
|
||||
let setAutoPlay = function(forcePlay) {
|
||||
let controller = getPlayer();
|
||||
if (
|
||||
controller &&
|
||||
(forcePlay || controller.surfer.isPlaying())
|
||||
) {
|
||||
controller.autoPlay = true;
|
||||
}
|
||||
};
|
||||
audio.setTrack = function(idx, forcePlay) {
|
||||
if (audio.tracks.length > idx) {
|
||||
if (audio.selectedIndex === idx) {
|
||||
let ctrl = getPlayer();
|
||||
ctrl.surfer.playPause();
|
||||
} else {
|
||||
setAutoPlay(forcePlay);
|
||||
audio.currentTrack = audio.tracks[idx];
|
||||
audio.selectedIndex = idx;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
audio.extraButtons = [
|
||||
{
|
||||
icon: 'zmdi zmdi-skip-previous',
|
||||
title: 'Previous',
|
||||
action: function($event) {
|
||||
if (audio.selectedIndex > 0) {
|
||||
audio.setTrack(audio.selectedIndex - 1);
|
||||
}
|
||||
},
|
||||
class: ''
|
||||
},
|
||||
{
|
||||
icon: 'zmdi zmdi-skip-next',
|
||||
title: 'Next',
|
||||
action: function($event) {
|
||||
if (audio.selectedIndex < audio.tracks.length - 1) {
|
||||
audio.setTrack(audio.selectedIndex + 1);
|
||||
}
|
||||
},
|
||||
class: ''
|
||||
}
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name md-wavesurfer-audio
|
||||
*
|
||||
* Directive for playing a set of audio files. This directive is analogous to `<audio>` HTML tag.
|
||||
* The audio files, should be specified using the `md-wavesurfer-source`
|
||||
*
|
||||
* WaveSurfer properties can be passed in using the prefix : player-* for attributes, e.g. `player-wave-color` is
|
||||
* equivalent to WaveSurfer's waveColor option.
|
||||
*
|
||||
* Must be used as an element.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <md-wavesurfer-audio player-wave-color="gray" player-progress-color="black" player-backend="MediaElement">
|
||||
* <md-wavesurfer-source src="source1" title="Title-1"></md-wavesurfer-source>
|
||||
* <md-wavesurfer-source src="source2" title="Title-2"></md-wavesurfer-source>
|
||||
* <md-wavesurfer-source src="source3" title="Title-3"></md-wavesurfer-source>
|
||||
* ...
|
||||
* <md-wavesurfer-source src="sourceN" title="Рассказы о сновидениях"></md-wavesurfer-source>
|
||||
* </md-wavesurfer-audio>
|
||||
* ```
|
||||
*
|
||||
* @param string player-* specifies WaveSurfer properties.
|
||||
*
|
||||
*/
|
||||
app.directive('mdWavesurferAudio', [
|
||||
function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'md-player-audio.partial.html',
|
||||
transclude: true,
|
||||
controller: 'mdWavesurferAudioController',
|
||||
controllerAs: 'audio'
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
*
|
||||
* @name md-wavesurfer-source
|
||||
*
|
||||
* This directive is used within the `md-wavesurfer-audio` directive to specify an audio file source, it is
|
||||
* synonymous to `<source>` tag in HTML
|
||||
*
|
||||
* The directive cannot be used as standalone.
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
* ```html
|
||||
* <md-wavesurfer-source src="source3" title="Title-3" album-art="Album-Art-Url" duration=""></md-wavesurfer-source>
|
||||
* ```
|
||||
* @param String src the URL to the audio file, this is required.
|
||||
* @param String title track title
|
||||
* @param String album-art the album art URL
|
||||
* @param Number duration the length of the audio file in seconds, will be auto-detected if not specified.
|
||||
*
|
||||
*/
|
||||
app.directive('mdWavesurferSource', [
|
||||
'mdWavesurferUtils',
|
||||
function(mdWavesurferUtils) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
require: '^mdWavesurferAudio',
|
||||
scope: {
|
||||
src: '@',
|
||||
albumArt: '@',
|
||||
title: '@',
|
||||
duration: '='
|
||||
},
|
||||
link: function(scope, element, attrs, audio) {
|
||||
audio.addTrack(scope);
|
||||
|
||||
if (!scope.duration) {
|
||||
mdWavesurferUtils.getLength(scope.src).then(
|
||||
function(dur) {
|
||||
scope.duration = dur;
|
||||
},
|
||||
function(e) {
|
||||
scope.duration = 0;
|
||||
console.log(
|
||||
'Failed to get audio length, reason: ',
|
||||
e.message
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
element.on('$destroy', function() {
|
||||
audio.removeTrack(audio);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
app.controller('mdWavesurferPlayerController', [
|
||||
'$element',
|
||||
'$scope',
|
||||
'$attrs',
|
||||
'$interval',
|
||||
'$mdTheming',
|
||||
function($element, $scope, attributes, $interval, $mdTheme) {
|
||||
let control = this,
|
||||
timeInterval;
|
||||
|
||||
control.themeClass = 'md-' + $mdTheme.defaultTheme() + '-theme';
|
||||
control.isReady = false;
|
||||
control.surfer = null;
|
||||
|
||||
control.toggleMute = function() {
|
||||
if (control.surfer) {
|
||||
control.surfer.toggleMute();
|
||||
control.isMute = !control.isMute;
|
||||
}
|
||||
};
|
||||
|
||||
let initWaveSurfer = function() {
|
||||
control.isReady = false;
|
||||
control.currentTime = 0;
|
||||
if (!control.surfer) {
|
||||
let options = {
|
||||
container: $element[0].querySelector(
|
||||
'.waveSurferWave'
|
||||
)
|
||||
},
|
||||
defaults = {
|
||||
scrollParent: true,
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple'
|
||||
};
|
||||
|
||||
options = angular.extend(
|
||||
defaults,
|
||||
attributes,
|
||||
control.properties || {},
|
||||
options
|
||||
);
|
||||
control.surfer = WaveSurfer.create(options);
|
||||
|
||||
control.surfer.on('ready', function() {
|
||||
control.isReady = true;
|
||||
if (control.autoPlay) {
|
||||
control.surfer.play();
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
control.surfer.on('pause', function() {
|
||||
stopInterval();
|
||||
});
|
||||
|
||||
control.surfer.on('finish', function() {
|
||||
stopInterval();
|
||||
});
|
||||
|
||||
control.surfer.on('play', function() {
|
||||
startInterval();
|
||||
});
|
||||
}
|
||||
|
||||
control.title = control.title || control.src.split('/').pop();
|
||||
control.surfer.load(control.src);
|
||||
};
|
||||
|
||||
let startInterval = function() {
|
||||
timeInterval = $interval(function() {
|
||||
control.currentTime = control.isReady
|
||||
? control.surfer.getCurrentTime()
|
||||
: 0;
|
||||
}, 1000);
|
||||
},
|
||||
stopInterval = function() {
|
||||
$interval.cancel(timeInterval);
|
||||
};
|
||||
|
||||
initWaveSurfer();
|
||||
|
||||
$scope.$watch('control.src', function(src1, src2) {
|
||||
if (src1 != src2) {
|
||||
initWaveSurfer();
|
||||
}
|
||||
});
|
||||
|
||||
$element.on('$destroy', function() {
|
||||
if (control.surfer) {
|
||||
control.surfer.destroy();
|
||||
}
|
||||
stopInterval();
|
||||
});
|
||||
|
||||
$scope.$watch(
|
||||
function() {
|
||||
let div = $element[0].querySelector('.audioPlayerWrapper');
|
||||
return div ? div.offsetWidth : 0;
|
||||
},
|
||||
function(width) {
|
||||
if (width < 1) {
|
||||
//hidden
|
||||
control.surfer.pause();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
*
|
||||
* @name md-wavesurfer-player
|
||||
*
|
||||
* @usage
|
||||
* This directive can be used as a stand-alone directive to display Audio WaveSurfer with a few controls, by default
|
||||
* this will only display play/pause, fast-forward, rewind and mute toggle buttons, however, you can add extra
|
||||
* buttons using the `extra-buttons` parameters.
|
||||
*
|
||||
* ```html
|
||||
* <md-wavesurfer-player url="trackUrl" title="Track Title"
|
||||
* extra-buttons="extraButtons" properties="properties">
|
||||
* </md-wavesurfer-player>
|
||||
* ```
|
||||
*
|
||||
* @param {string} url the URL of the audio file
|
||||
* @param {string} title title of the audio track
|
||||
* @param {object} properties an object specifying init options for WaveSurfer
|
||||
* @param {boolean} auto-play specifies if the player should start as soon as it's loaded.
|
||||
* @param {object[]} extra-buttons a list of extra buttons to add to the control panel
|
||||
* each button should be an object with the following properties:
|
||||
* {
|
||||
* title: "button title"
|
||||
* action: "call back to call when button is clicked, executed in parent scope",
|
||||
* icon: "md-font-icon parameter for the button"
|
||||
* class: "extra classes to add to the button."
|
||||
* }
|
||||
*
|
||||
* Every other attribute passed to this directive is assumed to a WaveSurver init parameter.
|
||||
*/
|
||||
app.directive('mdWavesurferPlayer', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'md-player.partial.html',
|
||||
scope: {
|
||||
src: '@url',
|
||||
title: '@',
|
||||
extraButtons: '=',
|
||||
toolbarClass: '@',
|
||||
autoPlay: '=',
|
||||
properties: '='
|
||||
},
|
||||
controller: 'mdWavesurferPlayerController',
|
||||
controllerAs: 'control',
|
||||
bindToController: true
|
||||
};
|
||||
});
|
||||
})();
|
65
public/scripts/wavesurfer/example/angular/app.js
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/* global angular */
|
||||
|
||||
let app = angular.module('ngWavesurfer', []);
|
||||
|
||||
app.directive('ngWavesurfer', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
|
||||
link: function($scope, $element, $attrs) {
|
||||
$element.css('display', 'block');
|
||||
|
||||
let options = angular.extend({ container: $element[0] }, $attrs);
|
||||
let wavesurfer = WaveSurfer.create(options);
|
||||
|
||||
if ($attrs.url) {
|
||||
wavesurfer.load($attrs.url, $attrs.data || null);
|
||||
}
|
||||
|
||||
$scope.$emit('wavesurferInit', wavesurfer);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('PlaylistController', function($scope) {
|
||||
let activeUrl = null;
|
||||
|
||||
$scope.paused = true;
|
||||
|
||||
$scope.$on('wavesurferInit', function(e, wavesurfer) {
|
||||
$scope.wavesurfer = wavesurfer;
|
||||
|
||||
$scope.wavesurfer.on('play', function() {
|
||||
$scope.paused = false;
|
||||
});
|
||||
|
||||
$scope.wavesurfer.on('pause', function() {
|
||||
$scope.paused = true;
|
||||
});
|
||||
|
||||
$scope.wavesurfer.on('finish', function() {
|
||||
$scope.paused = true;
|
||||
$scope.wavesurfer.seekTo(0);
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
|
||||
$scope.play = function(url) {
|
||||
if (!$scope.wavesurfer) {
|
||||
return;
|
||||
}
|
||||
|
||||
activeUrl = url;
|
||||
|
||||
$scope.wavesurfer.once('ready', function() {
|
||||
$scope.wavesurfer.play();
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
$scope.wavesurfer.load(activeUrl);
|
||||
};
|
||||
|
||||
$scope.isPlaying = function(url) {
|
||||
return url == activeUrl;
|
||||
};
|
||||
});
|
120
public/scripts/wavesurfer/example/angular/index.html
Normal file
@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Angular</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- AngularJS -->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="app.js"></script>
|
||||
</head>
|
||||
|
||||
<body ng-app="ngWavesurfer" ng-controller="PlaylistController">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">wavesurfer.js Angular Demo</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div class="row" style="margin: 30px 0">
|
||||
<div class="col-sm-10">
|
||||
<ng-wavesurfer url="../media/demo.wav" wave-color="#337ab7" progress-color="#23527c" height="64">
|
||||
</ng-wavesurfer>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-success btn-block" ng-click="wavesurfer.playPause()">
|
||||
<span id="play" ng-show="paused">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
</span>
|
||||
|
||||
<span id="pause" ng-show="!paused">
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-group" id="playlist">
|
||||
<a href=""
|
||||
ng-class="{ 'list-group-item': true, active: isPlaying('../media/demo.wav') }"
|
||||
ng-click="play('../media/demo.wav')">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
czskamaarù – Trou
|
||||
<span class="badge">0:21</span>
|
||||
</a>
|
||||
|
||||
<a href=""
|
||||
ng-class="{ 'list-group-item': true, active: isPlaying('../panner/media.wav') }"
|
||||
ng-click="play('../panner/media.wav')">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
日本人の話し
|
||||
<span class="badge">1:04</span>
|
||||
</a>
|
||||
|
||||
<a href=""
|
||||
ng-class="{ 'list-group-item': true, active: isPlaying('../elan/transcripts/001z.mp3') }"
|
||||
ng-click="play('../elan/transcripts/001z.mp3')">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Рассказы о сновидениях
|
||||
<span class="badge badge-info">1:26</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<p>
|
||||
Audio sources:<br />
|
||||
<a rel="nofollow" href="http://www.jamendo.com/en/track/661578/trou"><b>Trou</b> <span class="muted">by</span> <b>czskamaarù</b></a>,
|
||||
<a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
332
public/scripts/wavesurfer/example/annotation/annotations.json
Normal file
@ -0,0 +1,332 @@
|
||||
[
|
||||
{
|
||||
"start": 1.1,
|
||||
"end": 1.8,
|
||||
"data": {},
|
||||
"attributes": { "label": "abc", "highlight": true }
|
||||
},
|
||||
{ "start": 2.7, "end": 4.1, "data": {} },
|
||||
{ "start": 6, "end": 8.2, "data": {} },
|
||||
{ "start": 8.6, "end": 12.2, "data": {} },
|
||||
{ "start": 12.8, "end": 16.2, "data": {} },
|
||||
{ "start": "17.8", "end": "18.6", "data": { "note": "羅生門" } },
|
||||
{ "start": "19.19", "end": "20.5", "data": { "note": "芥川龍之介" } },
|
||||
{
|
||||
"start": "22.5",
|
||||
"end": "24.5",
|
||||
"data": { "note": "ある日の暮方の事である。" }
|
||||
},
|
||||
{
|
||||
"start": "25.19",
|
||||
"end": "26.3",
|
||||
"data": { "note": "一人の下人げにんが、" }
|
||||
},
|
||||
{
|
||||
"start": "26.8",
|
||||
"end": "28",
|
||||
"data": { "note": "羅生門らしょうもんの下で" }
|
||||
},
|
||||
{
|
||||
"start": "28.3",
|
||||
"end": "29.6",
|
||||
"data": { "note": "雨やみを待っていた。" }
|
||||
},
|
||||
{
|
||||
"start": "30.8",
|
||||
"end": "34.5",
|
||||
"data": { "note": "広い門の下には、この男のほかに誰もいない。" }
|
||||
},
|
||||
{
|
||||
"start": "35.4",
|
||||
"end": "39.8",
|
||||
"data": {
|
||||
"note":
|
||||
"ただ、所々丹塗の剥げた、大きな円柱に、蟋蟀が一匹とまっている。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "40.3",
|
||||
"end": "42.4",
|
||||
"data": { "note": "蟋蟀が一匹とまっている。" }
|
||||
},
|
||||
{ "start": 43.5, "end": 54.2, "data": {} },
|
||||
{ "start": 55, "end": 57.7, "data": {} },
|
||||
{ "start": 59.7, "end": 64.3, "data": {} },
|
||||
{ "start": 64.6, "end": 67.8, "data": {} },
|
||||
{ "start": 68.7, "end": 71.4, "data": {} },
|
||||
{ "start": 72.7, "end": 81.6, "data": {} },
|
||||
{ "start": 81.9, "end": 84.7, "data": {} },
|
||||
{ "start": 85.6, "end": 89.3, "data": {} },
|
||||
{ "start": 89.6, "end": 92.4, "data": {} },
|
||||
{ "start": 93.6, "end": 94.3, "data": {} },
|
||||
{ "start": 94.5, "end": 96.9, "data": {} },
|
||||
{ "start": 97.4, "end": 98.3, "data": {} },
|
||||
{ "start": 98.6, "end": 99.7, "data": {} },
|
||||
{ "start": 100.2, "end": 106, "data": {} },
|
||||
{ "start": 106.3, "end": 107.7, "data": {} },
|
||||
{ "start": 109, "end": 113.2, "data": {} },
|
||||
{ "start": 113.6, "end": 117.9, "data": {} },
|
||||
{ "start": 119.5, "end": 123.7, "data": {} },
|
||||
{ "start": 124.5, "end": 130.2, "data": {} },
|
||||
{ "start": 130.5, "end": 131.6, "data": {} },
|
||||
{ "start": 132.8, "end": 134.3, "data": {} },
|
||||
{ "start": 134.6, "end": 136.4, "data": {} },
|
||||
{ "start": 137.1, "end": 139.6, "data": {} },
|
||||
{ "start": 141.1, "end": 146.1, "data": {} },
|
||||
{ "start": 147.9, "end": 150.9, "data": {} },
|
||||
{ "start": 151.6, "end": 157.7, "data": {} },
|
||||
{ "start": 158.2, "end": 162.2, "data": {} },
|
||||
{ "start": 163.8, "end": 168.4, "data": {} },
|
||||
{ "start": 168.7, "end": 170.6, "data": {} },
|
||||
{ "start": 170.9, "end": 172.2, "data": {} },
|
||||
{ "start": 172.5, "end": 174.4, "data": {} },
|
||||
{ "start": 174.7, "end": 175.6, "data": {} },
|
||||
{ "start": 176, "end": 177.7, "data": {} },
|
||||
{ "start": 179.2, "end": 183, "data": {} },
|
||||
{ "start": 183.5, "end": 192.2, "data": {} },
|
||||
{ "start": 193, "end": 196.9, "data": {} },
|
||||
{ "start": 197.9, "end": 199.3, "data": {} },
|
||||
{ "start": 199.6, "end": 203.1, "data": {} },
|
||||
{ "start": 203.7, "end": 205, "data": {} },
|
||||
{ "start": 205.3, "end": 211.8, "data": {} },
|
||||
{ "start": 212.7, "end": 215.5, "data": {} },
|
||||
{ "start": 215.7, "end": 216.6, "data": {} },
|
||||
{ "start": 217.1, "end": 219.1, "data": {} },
|
||||
{ "start": 219.5, "end": 221.9, "data": {} },
|
||||
{ "start": 222.5, "end": 224, "data": {} },
|
||||
{ "start": 225.2, "end": 232.1, "data": {} },
|
||||
{ "start": 233.5, "end": 238.1, "data": {} },
|
||||
{ "start": 239.1, "end": 240.7, "data": {} },
|
||||
{ "start": 241.1, "end": 243.8, "data": {} },
|
||||
{ "start": 244.5, "end": 246.9, "data": {} },
|
||||
{ "start": 247.1, "end": 248.7, "data": {} },
|
||||
{ "start": 249, "end": 251.5, "data": {} },
|
||||
{ "start": 251.7, "end": 257.6, "data": {} },
|
||||
{ "start": 259.6, "end": 261.7, "data": {} },
|
||||
{ "start": 262, "end": 264.5, "data": {} },
|
||||
{ "start": 265.4, "end": 272.4, "data": {} },
|
||||
{ "start": 272.9, "end": 275.3, "data": {} },
|
||||
{ "start": 277.6, "end": 282.4, "data": {} },
|
||||
{ "start": 283.1, "end": 286.6, "data": {} },
|
||||
{ "start": 287, "end": 288.7, "data": {} },
|
||||
{ "start": 289.4, "end": 294.3, "data": {} },
|
||||
{ "start": 295.8, "end": 297.3, "data": {} },
|
||||
{ "start": 298.5, "end": 305, "data": {} },
|
||||
{ "start": 306, "end": 306.8, "data": {} },
|
||||
{ "start": 307.2, "end": 308, "data": {} },
|
||||
{ "start": 308.4, "end": 312, "data": {} },
|
||||
{ "start": 312.6, "end": 315.8, "data": {} },
|
||||
{ "start": 316.3, "end": 318.8, "data": {} },
|
||||
{ "start": 319.3, "end": 321, "data": {} },
|
||||
{ "start": 321.7, "end": 324.1, "data": {} },
|
||||
{ "start": 324.8, "end": 325.6, "data": {} },
|
||||
{ "start": 326, "end": 329.7, "data": {} },
|
||||
{ "start": 331.8, "end": 333.6, "data": {} },
|
||||
{ "start": 333.9, "end": 335.9, "data": {} },
|
||||
{ "start": 336.5, "end": 340.7, "data": {} },
|
||||
{ "start": 341.6, "end": 344.5, "data": {} },
|
||||
{ "start": 344.8, "end": 347.2, "data": {} },
|
||||
{ "start": 348.4, "end": 350.8, "data": {} },
|
||||
{ "start": 351.4, "end": 353.4, "data": {} },
|
||||
{ "start": 355.3, "end": 357, "data": {} },
|
||||
{ "start": 357.3, "end": 359, "data": {} },
|
||||
{ "start": 359.4, "end": 361.3, "data": {} },
|
||||
{ "start": 361.6, "end": 363.2, "data": {} },
|
||||
{ "start": 364.1, "end": 370.3, "data": {} },
|
||||
{ "start": 370.6, "end": 371.9, "data": {} },
|
||||
{ "start": 372.2, "end": 374.3, "data": {} },
|
||||
{ "start": 375.1, "end": 382.1, "data": {} },
|
||||
{ "start": 383, "end": 386.1, "data": {} },
|
||||
{ "start": 387.1, "end": 397.1, "data": {} },
|
||||
{ "start": 398.5, "end": 406.6, "data": {} },
|
||||
{ "start": 406.9, "end": 412.2, "data": {} },
|
||||
{ "start": 413.2, "end": 415.2, "data": {} },
|
||||
{ "start": 415.6, "end": 416.2, "data": {} },
|
||||
{ "start": 416.5, "end": 418.9, "data": {} },
|
||||
{ "start": 419.9, "end": 424.3, "data": {} },
|
||||
{ "start": 425.7, "end": 431.2, "data": {} },
|
||||
{ "start": 432.4, "end": 436, "data": {} },
|
||||
{ "start": 436.5, "end": 437.1, "data": {} },
|
||||
{ "start": 437.3, "end": 441.8, "data": {} },
|
||||
{ "start": 442.7, "end": 452, "data": {} },
|
||||
{ "start": 453.4, "end": 454.9, "data": {} },
|
||||
{ "start": 455.5, "end": 458.8, "data": {} },
|
||||
{ "start": 459.3, "end": 461.2, "data": {} },
|
||||
{ "start": 463, "end": 470, "data": {} },
|
||||
{ "start": 470.7, "end": 473.4, "data": {} },
|
||||
{ "start": 473.7, "end": 478.9, "data": {} },
|
||||
{ "start": 480.4, "end": 486.2, "data": {} },
|
||||
{ "start": 486.9, "end": 490.6, "data": {} },
|
||||
{ "start": 490.9, "end": 492.6, "data": {} },
|
||||
{ "start": 493.9, "end": 495.7, "data": {} },
|
||||
{ "start": 496.2, "end": 500.9, "data": {} },
|
||||
{ "start": 501.9, "end": 505.5, "data": {} },
|
||||
{ "start": 506.5, "end": 508.9, "data": {} },
|
||||
{ "start": 509.3, "end": 509.9, "data": {} },
|
||||
{ "start": 510.2, "end": 513.4, "data": {} },
|
||||
{ "start": 514.1, "end": 521.5, "data": {} },
|
||||
{ "start": 522.6, "end": 528.1, "data": {} },
|
||||
{ "start": 528.5, "end": 534.6, "data": {} },
|
||||
{ "start": 536.7, "end": 539.7, "data": {} },
|
||||
{ "start": 540.1, "end": 541.1, "data": {} },
|
||||
{ "start": 542, "end": 547.3, "data": {} },
|
||||
{ "start": 548.3, "end": 554, "data": {} },
|
||||
{ "start": 556.2, "end": 561.2, "data": {} },
|
||||
{ "start": 561.8, "end": 567.8, "data": {} },
|
||||
{ "start": 568.2, "end": 569.9, "data": {} },
|
||||
{ "start": 570.8, "end": 575.2, "data": {} },
|
||||
{ "start": 575.6, "end": 579.6, "data": {} },
|
||||
{ "start": 580.9, "end": 582.9, "data": {} },
|
||||
{ "start": 583.2, "end": 585.1, "data": {} },
|
||||
{ "start": 587.6, "end": 594.9, "data": {} },
|
||||
{ "start": 596.3, "end": 601.2, "data": {} },
|
||||
{ "start": 602.6, "end": 616.4, "data": {} },
|
||||
{ "start": 617.4, "end": 619.2, "data": {} },
|
||||
{ "start": 620.5, "end": 623.4, "data": {} },
|
||||
{ "start": 625.1, "end": 628.5, "data": {} },
|
||||
{ "start": 629, "end": 630.4, "data": {} },
|
||||
{ "start": 630.9, "end": 632.9, "data": {} },
|
||||
{ "start": 633.8, "end": 635.2, "data": {} },
|
||||
{ "start": 635.7, "end": 637.9, "data": {} },
|
||||
{ "start": 638.4, "end": 639.8, "data": {} },
|
||||
{ "start": 640.9, "end": 644.6, "data": {} },
|
||||
{ "start": 645.6, "end": 648.7, "data": {} },
|
||||
{ "start": 649.5, "end": 651.7, "data": {} },
|
||||
{ "start": 653.6, "end": 664.1, "data": {} },
|
||||
{ "start": 664.7, "end": 668.9, "data": {} },
|
||||
{ "start": 669.6, "end": 678.7, "data": {} },
|
||||
{ "start": 681.9, "end": 687, "data": {} },
|
||||
{ "start": 687.8, "end": 693.3, "data": {} },
|
||||
{ "start": 694.4, "end": 705.4, "data": {} },
|
||||
{ "start": 706.3, "end": 711.2, "data": {} },
|
||||
{ "start": 711.6, "end": 713.3, "data": {} },
|
||||
{ "start": 714.4, "end": 719.6, "data": {} },
|
||||
{ "start": 720.3, "end": 725.6, "data": {} },
|
||||
{ "start": 726.9, "end": 729, "data": {} },
|
||||
{ "start": 730.2, "end": 735.5, "data": {} },
|
||||
{ "start": 735.9, "end": 736.2, "data": {} },
|
||||
{ "start": 737.4, "end": 738.7, "data": {} },
|
||||
{ "start": 739.5, "end": 740.1, "data": {} },
|
||||
{ "start": 740.3, "end": 742.1, "data": {} },
|
||||
{ "start": 742.5, "end": 745.8, "data": {} },
|
||||
{ "start": 746.4, "end": 749.6, "data": {} },
|
||||
{ "start": 750, "end": 752.7, "data": {} },
|
||||
{ "start": 753.9, "end": 757.5, "data": {} },
|
||||
{ "start": 759.1, "end": 761.2, "data": {} },
|
||||
{ "start": 761.8, "end": 764.7, "data": {} },
|
||||
{ "start": 764.9, "end": 766.6, "data": {} },
|
||||
{ "start": 767.5, "end": 769.3, "data": {} },
|
||||
{ "start": 769.8, "end": 771.9, "data": {} },
|
||||
{ "start": 773.4, "end": 774.4, "data": {} },
|
||||
{ "start": 774.7, "end": 775.2, "data": {} },
|
||||
{ "start": 775.6, "end": 777, "data": {} },
|
||||
{ "start": 777.8, "end": 780.4, "data": {} },
|
||||
{ "start": 780.7, "end": 786.4, "data": {} },
|
||||
{ "start": 787.5, "end": 789.1, "data": {} },
|
||||
{ "start": 790, "end": 793.7, "data": {} },
|
||||
{ "start": 794.2, "end": 797.3, "data": {} },
|
||||
{ "start": 797.5, "end": 798.4, "data": {} },
|
||||
{ "start": 798.9, "end": 801.1, "data": {} },
|
||||
{ "start": 802.1, "end": 807.3, "data": {} },
|
||||
{ "start": 807.6, "end": 810.9, "data": {} },
|
||||
{ "start": 812.1, "end": 813.3, "data": {} },
|
||||
{ "start": 813.9, "end": 816.5, "data": {} },
|
||||
{ "start": 817.1, "end": 819, "data": {} },
|
||||
{ "start": 820.1, "end": 821.3, "data": {} },
|
||||
{ "start": 821.9, "end": 822.3, "data": {} },
|
||||
{ "start": 822.7, "end": 828.4, "data": {} },
|
||||
{ "start": 828.7, "end": 830.7, "data": {} },
|
||||
{ "start": 831.9, "end": 837.6, "data": {} },
|
||||
{ "start": 839.3, "end": 840, "data": {} },
|
||||
{ "start": 840.2, "end": 842.7, "data": {} },
|
||||
{ "start": 843.7, "end": 847.2, "data": {} },
|
||||
{ "start": 848.3, "end": 852.7, "data": {} },
|
||||
{ "start": 853.9, "end": 855.8, "data": {} },
|
||||
{ "start": 856.1, "end": 857.6, "data": {} },
|
||||
{ "start": 858.2, "end": 860.5, "data": {} },
|
||||
{ "start": 862.3, "end": 863.2, "data": {} },
|
||||
{ "start": 863.5, "end": 864.8, "data": {} },
|
||||
{ "start": 865.2, "end": 866.5, "data": {} },
|
||||
{ "start": 867.1, "end": 868.7, "data": {} },
|
||||
{ "start": 869.9, "end": 871.4, "data": {} },
|
||||
{ "start": 871.7, "end": 873, "data": {} },
|
||||
{ "start": 873.3, "end": 875, "data": {} },
|
||||
{ "start": 876.1, "end": 876.9, "data": {} },
|
||||
{ "start": 877.1, "end": 880.6, "data": {} },
|
||||
{ "start": 881.1, "end": 883.7, "data": {} },
|
||||
{ "start": 886.5, "end": 887.5, "data": {} },
|
||||
{ "start": 888, "end": 890.6, "data": {} },
|
||||
{ "start": 891.9, "end": 892.6, "data": {} },
|
||||
{ "start": 892.9, "end": 893.9, "data": {} },
|
||||
{ "start": 894.2, "end": 895.8, "data": {} },
|
||||
{ "start": 896.2, "end": 897.2, "data": {} },
|
||||
{ "start": 897.6, "end": 899.5, "data": {} },
|
||||
{ "start": 901.9, "end": 904.2, "data": {} },
|
||||
{ "start": 905.3, "end": 907.3, "data": {} },
|
||||
{ "start": 908.5, "end": 910.8, "data": {} },
|
||||
{ "start": 914.3, "end": 917.5, "data": {} },
|
||||
{ "start": 918.1, "end": 925.4, "data": {} },
|
||||
{ "start": 926.1, "end": 930.9, "data": {} },
|
||||
{ "start": 931.2, "end": 938.4, "data": {} },
|
||||
{ "start": 938.8, "end": 939.8, "data": {} },
|
||||
{ "start": 940.2, "end": 941.6, "data": {} },
|
||||
{ "start": 943.2, "end": 944.4, "data": {} },
|
||||
{ "start": 945, "end": 950.3, "data": {} },
|
||||
{ "start": 951.4, "end": 953.7, "data": {} },
|
||||
{ "start": 954.2, "end": 958, "data": {} },
|
||||
{ "start": 959.4, "end": 962.8, "data": {} },
|
||||
{ "start": 963.9, "end": 971.7, "data": {} },
|
||||
{ "start": 972.2, "end": 976.9, "data": {} },
|
||||
{ "start": 978, "end": 979, "data": {} },
|
||||
{ "start": 979.5, "end": 988, "data": {} },
|
||||
{ "start": 989, "end": 992.2, "data": {} },
|
||||
{ "start": 993, "end": 995.1, "data": {} },
|
||||
{ "start": 995.6, "end": 997.6, "data": {} },
|
||||
{ "start": 998.7, "end": 1003.1, "data": {} },
|
||||
{ "start": 1004.2, "end": 1006.7, "data": {} },
|
||||
{ "start": 1007, "end": 1008.4, "data": {} },
|
||||
{ "start": 1008.8, "end": 1011.9, "data": {} },
|
||||
{ "start": 1012.1, "end": 1012.8, "data": {} },
|
||||
{ "start": 1013.4, "end": 1017, "data": {} },
|
||||
{ "start": 1017.9, "end": 1022.5, "data": {} },
|
||||
{ "start": 1024.2, "end": 1026.4, "data": {} },
|
||||
{ "start": 1027.3, "end": 1034.4, "data": {} },
|
||||
{ "start": 1035.3, "end": 1038.9, "data": {} },
|
||||
{ "start": 1039.3, "end": 1042.3, "data": {} },
|
||||
{ "start": 1043.5, "end": 1048.4, "data": {} },
|
||||
{ "start": 1049.2, "end": 1050.7, "data": {} },
|
||||
{ "start": 1051.2, "end": 1053.6, "data": {} },
|
||||
{ "start": 1054.1, "end": 1056.5, "data": {} },
|
||||
{ "start": 1056.9, "end": 1059.1, "data": {} },
|
||||
{ "start": 1059.5, "end": 1067, "data": {} },
|
||||
{ "start": 1067.3, "end": 1069.1, "data": {} },
|
||||
{ "start": 1069.9, "end": 1072.4, "data": {} },
|
||||
{ "start": 1072.9, "end": 1076.5, "data": {} },
|
||||
{ "start": 1077, "end": 1078.8, "data": {} },
|
||||
{ "start": 1080.6, "end": 1081.4, "data": {} },
|
||||
{ "start": 1082.6, "end": 1086.5, "data": {} },
|
||||
{ "start": 1087.6, "end": 1088.4, "data": {} },
|
||||
{ "start": 1088.8, "end": 1096.7, "data": {} },
|
||||
{ "start": 1097.6, "end": 1104.9, "data": {} },
|
||||
{ "start": 1105.4, "end": 1108.9, "data": {} },
|
||||
{ "start": 1109.6, "end": 1114.6, "data": {} },
|
||||
{ "start": 1116, "end": 1119.5, "data": {} },
|
||||
{ "start": 1120.4, "end": 1127.8, "data": {} },
|
||||
{ "start": 1130.1, "end": 1132.5, "data": {} },
|
||||
{ "start": 1133.1, "end": 1134.2, "data": {} },
|
||||
{ "start": 1134.6, "end": 1136.7, "data": {} },
|
||||
{ "start": 1137.3, "end": 1139.1, "data": {} },
|
||||
{ "start": 1140.9, "end": 1141.5, "data": {} },
|
||||
{ "start": 1141.9, "end": 1143, "data": {} },
|
||||
{ "start": 1143.5, "end": 1145.4, "data": {} },
|
||||
{ "start": 1146.1, "end": 1148.6, "data": {} },
|
||||
{ "start": 1148.8, "end": 1150.7, "data": {} },
|
||||
{ "start": 1151.9, "end": 1153.1, "data": {} },
|
||||
{ "start": 1153.7, "end": 1158.2, "data": {} },
|
||||
{ "start": 1159.5, "end": 1160.6, "data": {} },
|
||||
{ "start": 1161.7, "end": 1162.9, "data": {} },
|
||||
{ "start": 1163.3, "end": 1165, "data": {} },
|
||||
{ "start": 1166.9, "end": 1168.2, "data": {} },
|
||||
{ "start": 1168.5, "end": 1169.5, "data": {} },
|
||||
{ "start": 1172.4, "end": 1174.3, "data": {} }
|
||||
]
|
8
public/scripts/wavesurfer/example/annotation/app.css
Normal file
@ -0,0 +1,8 @@
|
||||
region.wavesurfer-region:before {
|
||||
content: attr(data-region-label);
|
||||
}
|
||||
|
||||
region.wavesurfer-region[data-region-highlight] {
|
||||
border: 1px solid rgb(86, 180, 239);
|
||||
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.05) inset, 0px 0px 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
257
public/scripts/wavesurfer/example/annotation/app.js
Normal file
@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Create a WaveSurfer instance.
|
||||
*/
|
||||
var wavesurfer; // eslint-disable-line no-var
|
||||
|
||||
/**
|
||||
* Init & load.
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Init wavesurfer
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: '#waveform',
|
||||
height: 100,
|
||||
pixelRatio: 1,
|
||||
scrollParent: true,
|
||||
normalize: true,
|
||||
minimap: true,
|
||||
backend: 'MediaElement',
|
||||
plugins: [
|
||||
WaveSurfer.regions.create(),
|
||||
WaveSurfer.minimap.create({
|
||||
height: 30,
|
||||
waveColor: '#ddd',
|
||||
progressColor: '#999',
|
||||
cursorColor: '#999'
|
||||
}),
|
||||
WaveSurfer.timeline.create({
|
||||
container: '#wave-timeline'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
wavesurfer.util
|
||||
.fetchFile({
|
||||
responseType: 'json',
|
||||
url: 'rashomon.json'
|
||||
})
|
||||
.on('success', function(data) {
|
||||
wavesurfer.load(
|
||||
'http://www.archive.org/download/mshortworks_001_1202_librivox/msw001_03_rashomon_akutagawa_mt_64kb.mp3',
|
||||
data
|
||||
);
|
||||
});
|
||||
|
||||
/* Regions */
|
||||
|
||||
wavesurfer.on('ready', function() {
|
||||
wavesurfer.enableDragSelection({
|
||||
color: randomColor(0.1)
|
||||
});
|
||||
|
||||
if (localStorage.regions) {
|
||||
loadRegions(JSON.parse(localStorage.regions));
|
||||
} else {
|
||||
// loadRegions(
|
||||
// extractRegions(
|
||||
// wavesurfer.backend.getPeaks(512),
|
||||
// wavesurfer.getDuration()
|
||||
// )
|
||||
// );
|
||||
fetch('annotations.json')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
loadRegions(data);
|
||||
saveRegions();
|
||||
});
|
||||
}
|
||||
});
|
||||
wavesurfer.on('region-click', function(region, e) {
|
||||
e.stopPropagation();
|
||||
// Play on click, loop on shift click
|
||||
e.shiftKey ? region.playLoop() : region.play();
|
||||
});
|
||||
wavesurfer.on('region-click', editAnnotation);
|
||||
wavesurfer.on('region-updated', saveRegions);
|
||||
wavesurfer.on('region-removed', saveRegions);
|
||||
wavesurfer.on('region-in', showNote);
|
||||
|
||||
wavesurfer.on('region-play', function(region) {
|
||||
region.once('out', function() {
|
||||
wavesurfer.play(region.start);
|
||||
wavesurfer.pause();
|
||||
});
|
||||
});
|
||||
|
||||
/* Toggle play/pause buttons. */
|
||||
let playButton = document.querySelector('#play');
|
||||
let pauseButton = document.querySelector('#pause');
|
||||
wavesurfer.on('play', function() {
|
||||
playButton.style.display = 'none';
|
||||
pauseButton.style.display = '';
|
||||
});
|
||||
wavesurfer.on('pause', function() {
|
||||
playButton.style.display = '';
|
||||
pauseButton.style.display = 'none';
|
||||
});
|
||||
|
||||
|
||||
document.querySelector(
|
||||
'[data-action="delete-region"]'
|
||||
).addEventListener('click', function() {
|
||||
let form = document.forms.edit;
|
||||
let regionId = form.dataset.region;
|
||||
if (regionId) {
|
||||
wavesurfer.regions.list[regionId].remove();
|
||||
form.reset();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Save annotations to localStorage.
|
||||
*/
|
||||
function saveRegions() {
|
||||
localStorage.regions = JSON.stringify(
|
||||
Object.keys(wavesurfer.regions.list).map(function(id) {
|
||||
let region = wavesurfer.regions.list[id];
|
||||
return {
|
||||
start: region.start,
|
||||
end: region.end,
|
||||
attributes: region.attributes,
|
||||
data: region.data
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load regions from localStorage.
|
||||
*/
|
||||
function loadRegions(regions) {
|
||||
regions.forEach(function(region) {
|
||||
region.color = randomColor(0.1);
|
||||
wavesurfer.addRegion(region);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract regions separated by silence.
|
||||
*/
|
||||
function extractRegions(peaks, duration) {
|
||||
// Silence params
|
||||
const minValue = 0.0015;
|
||||
const minSeconds = 0.25;
|
||||
|
||||
let length = peaks.length;
|
||||
let coef = duration / length;
|
||||
let minLen = minSeconds / coef;
|
||||
|
||||
// Gather silence indeces
|
||||
let silences = [];
|
||||
Array.prototype.forEach.call(peaks, function(val, index) {
|
||||
if (Math.abs(val) <= minValue) {
|
||||
silences.push(index);
|
||||
}
|
||||
});
|
||||
|
||||
// Cluster silence values
|
||||
let clusters = [];
|
||||
silences.forEach(function(val, index) {
|
||||
if (clusters.length && val == silences[index - 1] + 1) {
|
||||
clusters[clusters.length - 1].push(val);
|
||||
} else {
|
||||
clusters.push([val]);
|
||||
}
|
||||
});
|
||||
|
||||
// Filter silence clusters by minimum length
|
||||
let fClusters = clusters.filter(function(cluster) {
|
||||
return cluster.length >= minLen;
|
||||
});
|
||||
|
||||
// Create regions on the edges of silences
|
||||
let regions = fClusters.map(function(cluster, index) {
|
||||
let next = fClusters[index + 1];
|
||||
return {
|
||||
start: cluster[cluster.length - 1],
|
||||
end: next ? next[0] : length - 1
|
||||
};
|
||||
});
|
||||
|
||||
// Add an initial region if the audio doesn't start with silence
|
||||
let firstCluster = fClusters[0];
|
||||
if (firstCluster && firstCluster[0] != 0) {
|
||||
regions.unshift({
|
||||
start: 0,
|
||||
end: firstCluster[firstCluster.length - 1]
|
||||
});
|
||||
}
|
||||
|
||||
// Filter regions by minimum length
|
||||
let fRegions = regions.filter(function(reg) {
|
||||
return reg.end - reg.start >= minLen;
|
||||
});
|
||||
|
||||
// Return time-based regions
|
||||
return fRegions.map(function(reg) {
|
||||
return {
|
||||
start: Math.round(reg.start * coef * 10) / 10,
|
||||
end: Math.round(reg.end * coef * 10) / 10
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Random RGBA color.
|
||||
*/
|
||||
function randomColor(alpha) {
|
||||
return (
|
||||
'rgba(' +
|
||||
[
|
||||
~~(Math.random() * 255),
|
||||
~~(Math.random() * 255),
|
||||
~~(Math.random() * 255),
|
||||
alpha || 1
|
||||
] +
|
||||
')'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit annotation for a region.
|
||||
*/
|
||||
function editAnnotation(region) {
|
||||
let form = document.forms.edit;
|
||||
form.style.opacity = 1;
|
||||
(form.elements.start.value = Math.round(region.start * 10) / 10),
|
||||
(form.elements.end.value = Math.round(region.end * 10) / 10);
|
||||
form.elements.note.value = region.data.note || '';
|
||||
form.onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
region.update({
|
||||
start: form.elements.start.value,
|
||||
end: form.elements.end.value,
|
||||
data: {
|
||||
note: form.elements.note.value
|
||||
}
|
||||
});
|
||||
form.style.opacity = 0;
|
||||
};
|
||||
form.onreset = function() {
|
||||
form.style.opacity = 0;
|
||||
form.dataset.region = null;
|
||||
};
|
||||
form.dataset.region = region.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display annotation.
|
||||
*/
|
||||
function showNote(region) {
|
||||
if (!showNote.el) {
|
||||
showNote.el = document.querySelector('#subtitle');
|
||||
}
|
||||
showNote.el.textContent = region.data.note || '–';
|
||||
}
|
||||
|
253
public/scripts/wavesurfer/example/annotation/index.html
Normal file
@ -0,0 +1,253 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Annotation tool</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- plugins -->
|
||||
<script src="../../dist/plugin/wavesurfer.timeline.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.regions.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.minimap.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="../trivia.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">wavesurfer.js Annotations Tool</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<p id="subtitle" class="text-center text-info"> </p>
|
||||
|
||||
<div id="wave-timeline"></div>
|
||||
|
||||
<div id="waveform">
|
||||
<!-- Here be waveform -->
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin: 30px 0">
|
||||
<div class="col-sm-10">
|
||||
<p>
|
||||
Click on a region to enter an annotation.<br />
|
||||
Shift-click plays a region in a loop.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-primary btn-block" data-action="play">
|
||||
<span id="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
</span>
|
||||
|
||||
<span id="pause" style="display: none">
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form role="form" name="edit" style="opacity: 0; transition: opacity 300ms linear; margin: 30px 0;">
|
||||
<div class="form-group">
|
||||
<label for="start">Start</label>
|
||||
<input class="form-control" id="start" name="start" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="end">End</label>
|
||||
<input class="form-control" id="end" name="end" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="note">Note</label>
|
||||
<textarea id="note" class="form-control" rows="3" name="note"></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success btn-block">Save</button>
|
||||
<center><i>or</i></center>
|
||||
<button type="button" class="btn btn-danger btn-block" data-action="delete-region">Delete</button>
|
||||
</form>
|
||||
|
||||
<div class="row">
|
||||
<h4>Region events:</h4>
|
||||
|
||||
<ul>
|
||||
<li><code>region-in</code> – When playback enters a region. Callback will receive the <code>Region</code> object.</li>
|
||||
<li><code>region-out</code>– When playback leaves a region. Callback will receive the <code>Region</code> object.</li>
|
||||
<li><code>region-mouseenter</code> - When the mouse moves over a region. Callback will receive the <code>Region</code> object, and a <code>MouseEvent</code> object.</li>
|
||||
<li><code>region-mouseleave</code> - When the mouse leaves a region. Callback will receive the <code>Region</code> object, and a <code>MouseEvent</code> object.</li>
|
||||
<li><code>region-click</code> - When the mouse clicks on a region. Callback will receive the <code>Region</code> object, and a <code>MouseEvent</code> object.</li>
|
||||
<li><code>region-dblclick</code> - When the mouse double-clicks on a region. Callback will receive the <code>Region</code> object, and a <code>MouseEvent</code> object.</li>
|
||||
<li><code>region-created</code> – When a region is created. Callback will receive the <code>Region</code> object.</li>
|
||||
<li><code>region-updated</code> – When a region is updated. Callback will receive the <code>Region</code> object.</li>
|
||||
<li><code>region-update-end</code> – When dragging or resizing is finished. Callback will receive the <code>Region</code> object.</li>
|
||||
<li><code>region-removed</code> – When a region is removed. Callback will receive the <code>Region</code> object.</li>
|
||||
</ul>
|
||||
|
||||
<h4>Regions Plugin</h4>
|
||||
|
||||
<p>Regions are visual overlays on waveform that can be used to play and
|
||||
loop portions of audio. Regions can be dragged and resized.</p>
|
||||
|
||||
<p>Visual customization is possible via CSS (using the selectors
|
||||
<code>.wavesurfer-region</code> and <code>.wavesurfer-handle</code>).</p>
|
||||
|
||||
<p>To enable the plugin, add the script <code>plugin/wavesurfer.regions.js</code> to
|
||||
your page.</p>
|
||||
|
||||
<p>After doing that, use <code>wavesurfer.addRegion()</code> to create Region objects.</p>
|
||||
|
||||
<h5>Exposed Methods</h5>
|
||||
|
||||
<ul>
|
||||
<li><code>addRegion(options)</code> – Creates a region on the waveform. Returns a <code>Region</code> object. See <a href="#region-options">Region Options</a>, <a href="#region-methods">Region Methods</a> and <a href="#region-events">Region Events</a> below.
|
||||
|
||||
<ul>
|
||||
<li><strong>Note:</strong> You cannot add regions until the audio has finished loading, otherwise the <code>start:</code> and <code>end:</code> properties of the new region will be set to <code>0</code>, or an unexpected value.</li>
|
||||
</ul></li>
|
||||
<li><code>clearRegions()</code> – Removes all regions.</li>
|
||||
<li><code>enableDragSelection(options)</code> – Lets you create regions by selecting.
|
||||
areas of the waveform with mouse. <code>options</code> are Region objects' params (see <a href="#region-options">below</a>).</li>
|
||||
<li><code>disableDragSelection()</code> - Disables ability to create regions.</li>
|
||||
</ul>
|
||||
|
||||
<h5>Region Options</h5>
|
||||
|
||||
<table><thead>
|
||||
<tr>
|
||||
<th>option</th>
|
||||
<th>type</th>
|
||||
<th>default</th>
|
||||
<th>description</th>
|
||||
</tr>
|
||||
</thead><tbody>
|
||||
<tr>
|
||||
<td><code>id</code></td>
|
||||
<td>string</td>
|
||||
<td>random</td>
|
||||
<td>The id of the region.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>start</code></td>
|
||||
<td>float</td>
|
||||
<td><code>0</code></td>
|
||||
<td>The start position of the region (in seconds).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>end</code></td>
|
||||
<td>float</td>
|
||||
<td><code>0</code></td>
|
||||
<td>The end position of the region (in seconds).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>loop</code></td>
|
||||
<td>boolean</td>
|
||||
<td><code>false</code></td>
|
||||
<td>Whether to loop the region when played back.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>drag</code></td>
|
||||
<td>boolean</td>
|
||||
<td><code>true</code></td>
|
||||
<td>Allow/dissallow dragging the region.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>resize</code></td>
|
||||
<td>boolean</td>
|
||||
<td><code>true</code></td>
|
||||
<td>Allow/dissallow resizing the region.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>color</code></td>
|
||||
<td>string</td>
|
||||
<td><code>"rgba(0, 0, 0, 0.1)"</code></td>
|
||||
<td>HTML color code.</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<h5>Region Methods</h5>
|
||||
|
||||
<ul>
|
||||
<li><code>remove()</code> - Remove the region object.</li>
|
||||
<li><code>update(options)</code> - Modify the settings of the region.</li>
|
||||
<li><code>play()</code> - Play the audio region from the start to end position.</li>
|
||||
</ul>
|
||||
|
||||
<h5>Region Events</h5>
|
||||
|
||||
<p>General events:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>in</code> - When playback enters the region.</li>
|
||||
<li><code>out</code> - When playback leaves the region.</li>
|
||||
<li><code>remove</code> - Happens just before the region is removed.</li>
|
||||
<li><code>update</code> - When the region's options are updated.</li>
|
||||
</ul>
|
||||
|
||||
<p>Mouse events:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>click</code> - When the mouse clicks on the region. Callback will receive a <code>MouseEvent</code>.</li>
|
||||
<li><code>dblclick</code> - When the mouse double-clicks on the region. Callback will receive a <code>MouseEvent</code>.</li>
|
||||
<li><code>over</code> - When mouse moves over the region. Callback will receive a <code>MouseEvent</code>.</li>
|
||||
<li><code>leave</code> - When mouse leaves the region. Callback will receive a <code>MouseEvent</code>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<p>
|
||||
The sound file is from <a href="https://librivox.org/librivox-multilingual-short-works-collection-001-by-various/">librivox.org</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
47072
public/scripts/wavesurfer/example/annotation/rashomon.json
Normal file
139
public/scripts/wavesurfer/example/audio-element/index.html
Normal file
@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Media Element Example</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.regions.js"></script>
|
||||
|
||||
<!-- Demo -->
|
||||
<script src="main.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">Media Element Fallback Example</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<h3>How to Enable the Fallback</h3>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<strong>wavesurfer.js</strong> will automatically
|
||||
fallback to HTML5 Media if Web Audio is not
|
||||
supported. However, you can choose to use audio
|
||||
element manually. Simply set the <code>backend</code>
|
||||
option to <code>"MediaElement"</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<pre><code>var wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#wave'),
|
||||
backend: 'MediaElement'
|
||||
});
|
||||
</code></pre>
|
||||
</p>
|
||||
|
||||
<h3>Pre-rendered Peaks</h3>
|
||||
|
||||
<p>
|
||||
If you have pre-rendered peaks (on your server),
|
||||
you can pass them into the <code>load</code>
|
||||
function. This is optional–if you don't provide
|
||||
any peaks,
|
||||
<strong>wavesurfer.js</strong> will first draw a
|
||||
thin line instead of a waveform, then attempt to
|
||||
download the audio file via Ajax and decode it
|
||||
with Web Audio if available.
|
||||
</p>
|
||||
<p>
|
||||
Check the <a href="https://wavesurfer-js.org/faq/">FAQ</a> for instructions on how to generate peaks.
|
||||
</p>
|
||||
<p>
|
||||
<pre><code>wavesurfer.load('example/media/demo.mp3');</code></pre>
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
Press this button to see the same demo with pre-decoded peaks:
|
||||
<button class="btn btn-warning" data-action="peaks">
|
||||
Load with pre-rendered peaks
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="pull-right">
|
||||
<noindex>
|
||||
The audio file is from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used with permission.
|
||||
</noindex>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
59
public/scripts/wavesurfer/example/audio-element/main.js
Normal file
@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
var wavesurfer;
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Init
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
waveColor: '#A8DBA8',
|
||||
progressColor: '#3B8686',
|
||||
backend: 'MediaElement',
|
||||
mediaControls: false
|
||||
});
|
||||
|
||||
wavesurfer.once('ready', function() {
|
||||
console.log('Using wavesurfer.js ' + WaveSurfer.VERSION);
|
||||
});
|
||||
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
// Load audio from URL
|
||||
wavesurfer.load('../media/demo.wav');
|
||||
|
||||
// toggle play button
|
||||
document
|
||||
.querySelector('[data-action="play"]')
|
||||
.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
|
||||
// peaks button
|
||||
document
|
||||
.querySelector('[data-action="peaks"]')
|
||||
.addEventListener('click', function() {
|
||||
// load peaks from JSON file. See https://wavesurfer-js.org/faq/
|
||||
// for instructions on how to generate peaks
|
||||
fetch('../media/demo-peaks.json')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP error ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(peaks => {
|
||||
console.log(
|
||||
'loaded peaks! sample_rate: ' + peaks.sample_rate
|
||||
);
|
||||
|
||||
// load peaks into wavesurfer.js
|
||||
wavesurfer.load('../media/demo.wav', peaks.data);
|
||||
document.body.scrollTop = 0;
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('error', e);
|
||||
});
|
||||
});
|
||||
});
|
169
public/scripts/wavesurfer/example/bars/index.html
Normal file
@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Waveform using bars</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- Demo -->
|
||||
<script src="main.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">Bars Example</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play /
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<h3>Bars example</h3>
|
||||
<hr />
|
||||
<p>Draws a waveform with bars.</p>
|
||||
|
||||
<p>
|
||||
<pre><code>let wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
barWidth: 2,
|
||||
barHeight: 1, // the height of the wave
|
||||
barGap: null // the optional spacing between bars of the wave, if not provided will be calculated in legacy format
|
||||
});
|
||||
</code></pre>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<h3>Split Channel Options</h3>
|
||||
|
||||
<p>
|
||||
The split channel view can be modified with the <code>splitChannelsOptions</code>. The waveforms can be stacked on top of each other. And colors can be added to each channel.
|
||||
</p>
|
||||
<div id="demo-with-options">
|
||||
<div id="waveform-with-options">
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="play-button" class="btn btn-primary">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<pre><code>var wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#wave'),
|
||||
splitChannels: true,
|
||||
splitChannelsOptions: {
|
||||
overlay: false,
|
||||
channelColors: {
|
||||
0: {
|
||||
progressColor: 'green',
|
||||
waveColor: 'pink'
|
||||
},
|
||||
1: {
|
||||
progressColor: 'orange',
|
||||
waveColor: 'purple'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
</p>
|
||||
</div>
|
||||
<h4>splitChannelOptions</h4>
|
||||
<p>
|
||||
<code>overlay</code> - boolean - This determines whether channels are drawn on top of each other.
|
||||
</p>
|
||||
<p>
|
||||
<code>channelColors</code> - object - Pass this to set colors for each channel. If the channel index is not found on the object, colors will default to the top level color params.
|
||||
</p>
|
||||
<p>
|
||||
<code>filterChannels</code> - array - Array of channel numbers. Channels included in the array will not be drawn.
|
||||
</p>
|
||||
<p>
|
||||
<code>relativeNormalization</code> - boolean - When <code>normalize</code> and <code>splitChannels</code> are both true the channels will be normalized individually or proportionally to each other. Defaults to <code>false</code> (each channel will be normalized in isolation).
|
||||
</p>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="pull-right">
|
||||
<noindex>
|
||||
The audio file is from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used
|
||||
with permission.
|
||||
</noindex>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function (i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
|
||||
(i[r].q = i[r].q || []).push(arguments)
|
||||
}, i[r].l = 1 * new Date(); a = s.createElement(o),
|
||||
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
|
||||
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
64
public/scripts/wavesurfer/example/bars/main.js
Normal file
@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
let wavesurfer = {};
|
||||
let wavesurferWithOptions;
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
barWidth: 2,
|
||||
barHeight: 1,
|
||||
barGap: null
|
||||
});
|
||||
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
// Load audio from URL
|
||||
wavesurfer.load('../media/demo.wav');
|
||||
|
||||
// Play button
|
||||
const button = document.querySelector('[data-action="play"]');
|
||||
|
||||
button.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
|
||||
|
||||
// WaveSurfer with options example
|
||||
wavesurferWithOptions = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform-with-options'),
|
||||
barWidth: 2,
|
||||
barHeight: 1,
|
||||
barGap: null,
|
||||
splitChannels: true,
|
||||
splitChannelsOptions: {
|
||||
overlay: false,
|
||||
channelColors: {
|
||||
0: {
|
||||
progressColor: 'green',
|
||||
waveColor: 'pink'
|
||||
},
|
||||
1: {
|
||||
progressColor: 'orange',
|
||||
waveColor: 'purple'
|
||||
}
|
||||
},
|
||||
filterChannels: [],
|
||||
relativeNormalization: true
|
||||
}
|
||||
});
|
||||
|
||||
wavesurferWithOptions.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
// Load audio from URL
|
||||
wavesurferWithOptions.load('../media/stereo.mp3');
|
||||
|
||||
// Play/pause on button press
|
||||
document
|
||||
.getElementById('play-button')
|
||||
.addEventListener('click', wavesurferWithOptions.playPause.bind(wavesurferWithOptions));
|
||||
});
|
140
public/scripts/wavesurfer/example/css/ribbon.css
Normal file
@ -0,0 +1,140 @@
|
||||
/*!
|
||||
* "Fork me on GitHub" CSS ribbon v0.1.1 | MIT License
|
||||
* https://github.com/simonwhitaker/github-fork-ribbon-css
|
||||
*/
|
||||
|
||||
/* Left will inherit from right (so we don't need to duplicate code) */
|
||||
.github-fork-ribbon {
|
||||
/* The right and left classes determine the side we attach our banner to */
|
||||
position: absolute;
|
||||
|
||||
/* Add a bit of padding to give some substance outside the "stitching" */
|
||||
padding: 2px 0;
|
||||
|
||||
/* Set the base colour */
|
||||
background-color: #a00;
|
||||
|
||||
/* Set a gradient: transparent black at the top to almost-transparent black at the bottom */
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.15)));
|
||||
background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
|
||||
background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
|
||||
background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
|
||||
background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
|
||||
|
||||
/* Add a drop shadow */
|
||||
-webkit-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
-moz-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* Set the font */
|
||||
font: 700 13px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
|
||||
z-index: 9999;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.github-fork-ribbon a,
|
||||
.github-fork-ribbon a:hover {
|
||||
/* Set the text properties */
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 -1px rgba(0, 0, 0, 0.5);
|
||||
text-align: center;
|
||||
|
||||
/* Set the geometry. If you fiddle with these you'll also need
|
||||
to tweak the top and right values in .github-fork-ribbon. */
|
||||
width: 200px;
|
||||
line-height: 20px;
|
||||
|
||||
/* Set the layout properties */
|
||||
display: inline-block;
|
||||
padding: 2px 0;
|
||||
|
||||
/* Add "stitching" effect */
|
||||
border-width: 1px 0;
|
||||
border-style: dotted;
|
||||
border-color: #fff;
|
||||
border-color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.left-bottom {
|
||||
position: fixed;
|
||||
top: inherit;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.right-bottom {
|
||||
position: fixed;
|
||||
top: inherit;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.right .github-fork-ribbon {
|
||||
top: 42px;
|
||||
right: -43px;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.left .github-fork-ribbon {
|
||||
top: 42px;
|
||||
left: -43px;
|
||||
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
|
||||
.github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon {
|
||||
top: 80px;
|
||||
left: -43px;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon {
|
||||
top: 80px;
|
||||
right: -43px;
|
||||
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
110
public/scripts/wavesurfer/example/css/style.css
Normal file
@ -0,0 +1,110 @@
|
||||
/* Space out content a bit */
|
||||
body {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Everything but the jumbotron gets side spacing for mobile first views */
|
||||
.header,
|
||||
.marketing,
|
||||
.footer {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* Custom page header */
|
||||
.header {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
/* Make the masthead heading the same height as the navigation */
|
||||
.header h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
line-height: 40px;
|
||||
padding-bottom: 19px;
|
||||
}
|
||||
|
||||
/* Custom page footer */
|
||||
.footer {
|
||||
padding-top: 19px;
|
||||
color: #777;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
/* Customize container */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
max-width: 900px;
|
||||
}
|
||||
}
|
||||
|
||||
.container-narrow > hr {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
/* Supporting marketing content */
|
||||
.marketing {
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.marketing p + h4 {
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
/* Responsive: Portrait tablets and up */
|
||||
@media screen and (min-width: 768px) {
|
||||
/* Remove the padding we set earlier */
|
||||
.header,
|
||||
.marketing,
|
||||
.footer {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
/* Space out the masthead */
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 30px 0 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.controls .btn {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 990px) {
|
||||
.controls .mark-controls {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.lead {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#waveform {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 50%;
|
||||
margin-top: -10px;
|
||||
left: 5%;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#drop {
|
||||
border: 3px dashed #ddd;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
#drop.wavesurfer-dragover {
|
||||
border-color: #333;
|
||||
}
|
123
public/scripts/wavesurfer/example/cursor/index.html
Normal file
@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Cursor Example</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.cursor.js"></script>
|
||||
|
||||
<!-- Demo -->
|
||||
<script src="main.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">Cursor Plugin Example</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play /
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<h3>Cursor plugin</h3>
|
||||
<hr />
|
||||
<p>Shows a cursor on the waveform.</p>
|
||||
|
||||
<p>
|
||||
<pre><code>let wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
plugins: [
|
||||
WaveSurfer.cursor.create({
|
||||
showTime: true,
|
||||
opacity: 1,
|
||||
customShowTimeStyle: {
|
||||
'background-color': '#000',
|
||||
color: '#fff',
|
||||
padding: '2px',
|
||||
'font-size': '10px'
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
</code></pre>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="pull-right">
|
||||
<noindex>
|
||||
The audio file is from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used
|
||||
with permission.
|
||||
</noindex>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function (i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
|
||||
(i[r].q = i[r].q || []).push(arguments)
|
||||
}, i[r].l = 1 * new Date(); a = s.createElement(o),
|
||||
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
|
||||
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
35
public/scripts/wavesurfer/example/cursor/main.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
let wavesurfer = {};
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
plugins: [
|
||||
WaveSurfer.cursor.create({
|
||||
showTime: true,
|
||||
opacity: 1,
|
||||
customShowTimeStyle: {
|
||||
'background-color': '#000',
|
||||
color: '#fff',
|
||||
padding: '2px',
|
||||
'font-size': '10px'
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
// Load audio from URL
|
||||
wavesurfer.load('../media/demo.wav');
|
||||
|
||||
// Play button
|
||||
const button = document.querySelector('[data-action="play"]');
|
||||
|
||||
button.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
});
|
194
public/scripts/wavesurfer/example/elan-wave-segment/index.html
Normal file
@ -0,0 +1,194 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | ELAN Wave Segment player</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="stylesheet" href="css/elan.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.min.js"></script>
|
||||
|
||||
<!-- regions plugin -->
|
||||
<script src="../../dist/plugin/wavesurfer.regions.min.js"></script>
|
||||
|
||||
<!-- ELAN format renderer -->
|
||||
<script src="../../dist/plugin/wavesurfer.elan.min.js"></script>
|
||||
|
||||
<!-- ELAN wave segment renderer -->
|
||||
<script src="../../plugin/wavesurfer.elan-wave-segment.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="app.js"></script>
|
||||
<script src="../trivia.js"></script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<noindex>
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="?fill">Fill</a></li>
|
||||
<li><a href="?scroll">Scroll</a></li>
|
||||
</ul>
|
||||
</noindex>
|
||||
|
||||
<h1 itemprop="name"><a href="http://wavesurfer-js.org">wavesurfer.js</a><noindex> + <a rel="nofollow" href="http://spokencorpora.ru/showelan.py">ELAN</a> + Wave Segment</noindex></h1>
|
||||
</div>
|
||||
<div><p>The Elan Wave Segment Plugin uses the table and the time values created by the
|
||||
<a href="../elan/index.html">ELAN plugin</a> to insert a wave form column for each row.
|
||||
</p></div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<div class="progress progress-striped active" id="progress-bar">
|
||||
<div class="progress-bar progress-bar-info"></div>
|
||||
</div>
|
||||
|
||||
<!-- Here be waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="annotations" class="table-responsive">
|
||||
<!-- Here be transcript -->
|
||||
</div>
|
||||
|
||||
<h2>How to Enable Elan Wave Segment</h2>
|
||||
|
||||
<h3>Javascript Dependencies</h3>
|
||||
<ul>
|
||||
<li>Wavesufer <code></code></li>
|
||||
<li>Region Plugin <code></code></li>
|
||||
<li>ElAN Plugin <code></code></li>
|
||||
<li>ELAN Wave Segment</li>
|
||||
<pre><code><script src="[path_to]/wavesurfer.min.js"></script>
|
||||
<script src="[path_to]/plugin/wavesurfer.region.min.js"></script>
|
||||
<script src="[path_to]/plugin/wavesurfer.elan.min.js"></script>
|
||||
<script src="[path_to]/plugin/wavesurfer.elan-wave-segment.min.js"></script> </code></pre>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>Javascript Initialization</h2>
|
||||
<pre><code>// Create the wave surfer instance
|
||||
var wavesurfer = Object.create(WaveSurfer);
|
||||
|
||||
// Create elan instance
|
||||
var elan = Object.create(WaveSurfer.ELAN);
|
||||
|
||||
// Create Elan Wave Segment instance
|
||||
var elanWaveSegment = Object.create(WaveSurfer.ELANWaveSegment);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var options = {
|
||||
container : '#waveform',
|
||||
};
|
||||
|
||||
//################## set up some listeners ####################
|
||||
|
||||
//set up listener for when elan is done
|
||||
elan.on('ready', function (data) {
|
||||
wavesurfer.load('../elan/transcripts/001z.mp3');
|
||||
});
|
||||
|
||||
//set up listener for playing when clicked on
|
||||
elan.on('select', function (start, end) {
|
||||
wavesurfer.backend.play(start, end);
|
||||
});
|
||||
//############################## initialize wavesurfer and related plugins###############
|
||||
|
||||
// Init wavesurfer
|
||||
wavesurfer.init(options);
|
||||
|
||||
//init elan
|
||||
elan.init({
|
||||
url: '../elan/transcripts/001z.xml',
|
||||
container: '#annotations',
|
||||
tiers: {
|
||||
Text: true,
|
||||
Comments: true
|
||||
}
|
||||
});
|
||||
|
||||
//int elanWaveSegment when wavesurfer is done loading the sound file
|
||||
wavesurfer.on('ready', function() {
|
||||
options.plotTimeEnd = wavesurfer.backend.getDuration();
|
||||
options.wavesurfer = wavesurfer;
|
||||
options.ELAN = elan;
|
||||
elanWaveSegment.init(options);
|
||||
});
|
||||
|
||||
//update waveSegments when time advances
|
||||
var onProgress = function (time) {
|
||||
elanWaveSegment.onProgress(time);
|
||||
//code for scrolling Elan goes here
|
||||
};
|
||||
wavesurfer.on('audioprocess', onProgress);
|
||||
}); </code></pre>
|
||||
<h2>Options</h2>
|
||||
<ul>
|
||||
<li><code>ELAN:</code> required - The ELAN instance used to parse the elan data</li>
|
||||
<li><code>wafesurver:</code> required - The wavesurfer instance used to draw the original waveform</li>
|
||||
<li><code>waveSegmentWidth:</code> optional - The width of each wave segment (defaults to 200)</li>
|
||||
<li><code>waveSegmentPeaksPerSegment:</code> optional - The number of peaks that should be drawn (defaults to 400)</li>
|
||||
<li><code>waveSegmentHeight:</code> optional - The height of each wave segment (defaults to 30)</li>
|
||||
<li><code>waveSegmentRenderer:</code> optional - The renderer (drawer) to be used for the wave segments</li>
|
||||
<li><code>waveSegmentNormalizeTo:</code> optional - What to normalize each wave segment to [whole, segment,none]</li>
|
||||
<li><code>waveSegmentBorderWidth:</code> optional - The width of the border of the container element</li>
|
||||
<li><code>waveSegmentBarHeight:</code> optional - the height of the peaks/bars (defaults to 1)</li>
|
||||
</ul>
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<p>
|
||||
The ELAN program and format were developed by <a href="http://tla.mpi.nl/tools/tla-tools/elan/">Max Planck Institute</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The sample ELAN file and audio are from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used with permission.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
114
public/scripts/wavesurfer/example/elan/app.js
Normal file
@ -0,0 +1,114 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
var wavesurfer;
|
||||
|
||||
// Init & load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let options = {
|
||||
container: '#waveform',
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple',
|
||||
loaderColor: 'purple',
|
||||
cursorColor: 'navy',
|
||||
selectionColor: '#d0e9c6',
|
||||
loopSelection: false,
|
||||
plugins: [
|
||||
WaveSurfer.elan.create({
|
||||
url: 'transcripts/001z.xml',
|
||||
container: '#annotations',
|
||||
tiers: {
|
||||
Text: true,
|
||||
Comments: true
|
||||
}
|
||||
}),
|
||||
WaveSurfer.regions.create()
|
||||
]
|
||||
};
|
||||
|
||||
if (location.search.match('scroll')) {
|
||||
options.minPxPerSec = 100;
|
||||
options.scrollParent = true;
|
||||
}
|
||||
|
||||
if (location.search.match('normalize')) {
|
||||
options.normalize = true;
|
||||
}
|
||||
|
||||
// Init wavesurfer
|
||||
wavesurfer = WaveSurfer.create(options);
|
||||
|
||||
/* Progress bar */
|
||||
(function() {
|
||||
let progressDiv = document.querySelector('#progress-bar');
|
||||
let progressBar = progressDiv.querySelector('.progress-bar');
|
||||
|
||||
let showProgress = function(percent) {
|
||||
progressDiv.style.display = 'block';
|
||||
progressBar.style.width = percent + '%';
|
||||
};
|
||||
|
||||
let hideProgress = function() {
|
||||
progressDiv.style.display = 'none';
|
||||
};
|
||||
|
||||
wavesurfer.on('loading', showProgress);
|
||||
wavesurfer.on('ready', hideProgress);
|
||||
wavesurfer.on('destroy', hideProgress);
|
||||
wavesurfer.on('error', hideProgress);
|
||||
})();
|
||||
|
||||
wavesurfer.elan.on('ready', function(data) {
|
||||
wavesurfer.load('transcripts/' + data.media.url);
|
||||
});
|
||||
|
||||
wavesurfer.elan.on('select', function(start, end) {
|
||||
wavesurfer.backend.play(start, end);
|
||||
});
|
||||
|
||||
wavesurfer.elan.on('ready', function() {
|
||||
let classList = wavesurfer.elan.container.querySelector('table')
|
||||
.classList;
|
||||
['table', 'table-striped', 'table-hover'].forEach(function(cl) {
|
||||
classList.add(cl);
|
||||
});
|
||||
});
|
||||
|
||||
let prevAnnotation, prevRow, region;
|
||||
let onProgress = function(time) {
|
||||
let annotation = wavesurfer.elan.getRenderedAnnotation(time);
|
||||
|
||||
if (prevAnnotation != annotation) {
|
||||
prevAnnotation = annotation;
|
||||
|
||||
region && region.remove();
|
||||
region = null;
|
||||
|
||||
if (annotation) {
|
||||
// Highlight annotation table row
|
||||
let row = wavesurfer.elan.getAnnotationNode(annotation);
|
||||
prevRow && prevRow.classList.remove('success');
|
||||
prevRow = row;
|
||||
row.classList.add('success');
|
||||
let before = row.previousSibling;
|
||||
if (before) {
|
||||
wavesurfer.elan.container.scrollTop = before.offsetTop;
|
||||
}
|
||||
|
||||
// Region
|
||||
region = wavesurfer.addRegion({
|
||||
start: annotation.start,
|
||||
end: annotation.end,
|
||||
resize: false,
|
||||
color: 'rgba(223, 240, 216, 0.7)'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wavesurfer.on('audioprocess', onProgress);
|
||||
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
});
|
25
public/scripts/wavesurfer/example/elan/css/elan.css
Normal file
@ -0,0 +1,25 @@
|
||||
#annotations {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.wavesurfer-annotations tr.wavesurfer-active td {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.wavesurfer-time {
|
||||
width: 100px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.wavesurfer-tier-Text {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
td.wavesurfer-tier-Comments {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.wavesurfer-handle {
|
||||
background-color: #c9e2b3;
|
||||
}
|
105
public/scripts/wavesurfer/example/elan/index.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | ELAN player</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="stylesheet" href="css/elan.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- regions plugin -->
|
||||
<script src="../../dist/plugin/wavesurfer.regions.js"></script>
|
||||
|
||||
<!-- ELAN format renderer -->
|
||||
<script src="../../dist/plugin/wavesurfer.elan.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="app.js"></script>
|
||||
<script src="../trivia.js"></script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<noindex>
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="?fill">Fill</a></li>
|
||||
<li><a href="?scroll">Scroll</a></li>
|
||||
</ul>
|
||||
</noindex>
|
||||
|
||||
<h1 itemprop="name"><a href="http://wavesurfer-js.org">wavesurfer.js</a><noindex> + <a rel="nofollow" href="http://spokencorpora.ru/showelan.py">ELAN</a></noindex></h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<div class="progress progress-striped active" id="progress-bar">
|
||||
<div class="progress-bar progress-bar-info"></div>
|
||||
</div>
|
||||
|
||||
<!-- Here be waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="annotations" class="table-responsive">
|
||||
<!-- Here be transcript -->
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<p>
|
||||
The ELAN program and format were developed by <a href="http://tla.mpi.nl/tools/tla-tools/elan/">Max Planck Institute</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The sample ELAN file and audio are from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used with permission.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
BIN
public/scripts/wavesurfer/example/elan/transcripts/001z.mp3
Normal file
2866
public/scripts/wavesurfer/example/elan/transcripts/001z.xml
Executable file
92
public/scripts/wavesurfer/example/equalizer/index.html
Normal file
@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Equalizer Example</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- Demo -->
|
||||
<script src="main.js"></script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">Equalizer Example</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<div class="progress progress-striped active" id="progress-bar">
|
||||
<div class="progress-bar progress-bar-info"></div>
|
||||
</div>
|
||||
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
|
||||
<div id="equalizer">
|
||||
<!-- Here be equalizer sliders -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="pull-right">
|
||||
<noindex>
|
||||
Demo music track is <a href="http://www.jamendo.com/en/track/661578/trou" rel="nofollow"><b>Trou</b> <span class="muted">by</span> <b>czskamaarù</b></a>. Thanks!
|
||||
</noindex>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
137
public/scripts/wavesurfer/example/equalizer/main.js
Normal file
@ -0,0 +1,137 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
var wavesurfer;
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Init
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
waveColor: '#A8DBA8',
|
||||
progressColor: '#3B8686'
|
||||
});
|
||||
|
||||
// Load audio from URL
|
||||
wavesurfer.load('../media/demo.wav');
|
||||
|
||||
// Equalizer
|
||||
wavesurfer.on('ready', function() {
|
||||
let EQ = [
|
||||
{
|
||||
f: 32,
|
||||
type: 'lowshelf'
|
||||
},
|
||||
{
|
||||
f: 64,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 125,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 250,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 500,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 1000,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 2000,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 4000,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 8000,
|
||||
type: 'peaking'
|
||||
},
|
||||
{
|
||||
f: 16000,
|
||||
type: 'highshelf'
|
||||
}
|
||||
];
|
||||
|
||||
// Create filters
|
||||
let filters = EQ.map(function(band) {
|
||||
let filter = wavesurfer.backend.ac.createBiquadFilter();
|
||||
filter.type = band.type;
|
||||
filter.gain.value = 0;
|
||||
filter.Q.value = 1;
|
||||
filter.frequency.value = band.f;
|
||||
return filter;
|
||||
});
|
||||
|
||||
// Connect filters to wavesurfer
|
||||
wavesurfer.backend.setFilters(filters);
|
||||
|
||||
// Bind filters to vertical range sliders
|
||||
let container = document.querySelector('#equalizer');
|
||||
filters.forEach(function(filter) {
|
||||
let input = document.createElement('input');
|
||||
Object.assign(input, {
|
||||
type: 'range',
|
||||
min: -40,
|
||||
max: 40,
|
||||
value: 0,
|
||||
title: filter.frequency.value
|
||||
});
|
||||
input.style.display = 'inline-block';
|
||||
input.setAttribute('orient', 'vertical');
|
||||
wavesurfer.util.style(input, {
|
||||
webkitAppearance: 'slider-vertical',
|
||||
width: '50px',
|
||||
height: '150px'
|
||||
});
|
||||
container.appendChild(input);
|
||||
|
||||
let onChange = function(e) {
|
||||
filter.gain.value = ~~e.target.value;
|
||||
};
|
||||
|
||||
input.addEventListener('input', onChange);
|
||||
input.addEventListener('change', onChange);
|
||||
});
|
||||
|
||||
// For debugging
|
||||
wavesurfer.filters = filters;
|
||||
});
|
||||
|
||||
// Log errors
|
||||
wavesurfer.on('error', function(msg) {
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
// Bind play/pause button
|
||||
document
|
||||
.querySelector('[data-action="play"]')
|
||||
.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
|
||||
// Progress bar
|
||||
(function() {
|
||||
const progressDiv = document.querySelector('#progress-bar');
|
||||
const progressBar = progressDiv.querySelector('.progress-bar');
|
||||
|
||||
let showProgress = function(percent) {
|
||||
progressDiv.style.display = 'block';
|
||||
progressBar.style.width = percent + '%';
|
||||
};
|
||||
|
||||
let hideProgress = function() {
|
||||
progressDiv.style.display = 'none';
|
||||
};
|
||||
|
||||
wavesurfer.on('loading', showProgress);
|
||||
wavesurfer.on('ready', hideProgress);
|
||||
wavesurfer.on('destroy', hideProgress);
|
||||
wavesurfer.on('error', hideProgress);
|
||||
})();
|
||||
});
|
122
public/scripts/wavesurfer/example/html-init/index.html
Normal file
@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | HTML initialisation API example</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
<script src="../../dist/wavesurfer-html-init.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.timeline.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.minimap.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.regions.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">HTML initialisation API</h1>
|
||||
</div>
|
||||
|
||||
<p class="marketing">
|
||||
Using the HTML initialisation script and the HTML attribute API is a quick way of creating wavesurfer instances.
|
||||
</p>
|
||||
|
||||
<div id="demo">
|
||||
<section class="marketing">
|
||||
<wavesurfer
|
||||
data-url="../media/demo.wav"
|
||||
data-plugins="regions"
|
||||
data-regions-regions='[{"start": 1,"end": 3,"color": "hsla(400, 100%, 30%, 0.5)"}, {"start": 5,"end": 7,"color": "hsla(200, 50%, 70%, 0.4)"}]'
|
||||
>
|
||||
</wavesurfer>
|
||||
<pre><code><wavesurfer
|
||||
data-url="../media/demo.wav"
|
||||
data-plugins="regions"
|
||||
data-regions-regions='[{"start": 1,"end": 3,"color": "hsla(400, 100%, 30%, 0.5)"}, {"start": 5,"end": 7,"color": "hsla(200, 50%, 70%, 0.4)"}]'
|
||||
>
|
||||
</wavesurfer>
|
||||
</code></pre>
|
||||
</section>
|
||||
<hr />
|
||||
<section class="marketing">
|
||||
<wavesurfer
|
||||
data-url="../media/demo.wav"
|
||||
data-plugins="minimap,timeline"
|
||||
data-minimap-height="30"
|
||||
data-minimap-wave-color="#ddd"
|
||||
data-minimap-progress-color="#999"
|
||||
data-timeline-font-size="13px"
|
||||
data-timeline-container="#timeline"
|
||||
>
|
||||
</wavesurfer>
|
||||
<div id="timeline"></div>
|
||||
<pre><code><wavesurfer
|
||||
data-url="../media/demo.wav"
|
||||
data-plugins="minimap,timeline"
|
||||
data-minimap-height="30"
|
||||
data-minimap-wave-color="#ddd"
|
||||
data-minimap-progress-color="#999"
|
||||
data-timeline-font-size="13px"
|
||||
data-timeline-container="#timeline"
|
||||
>
|
||||
</wavesurfer>
|
||||
<div id="timeline"></div>
|
||||
</code></pre>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="pull-right">
|
||||
<noindex>
|
||||
The audio file is from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used with permission.
|
||||
</noindex>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
107
public/scripts/wavesurfer/example/main.js
Normal file
@ -0,0 +1,107 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
var wavesurfer;
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let options = {
|
||||
container: document.querySelector('#waveform'),
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple',
|
||||
cursorColor: 'navy'
|
||||
};
|
||||
|
||||
if (location.search.match('scroll')) {
|
||||
options.minPxPerSec = 100;
|
||||
options.scrollParent = true;
|
||||
}
|
||||
|
||||
// Init
|
||||
wavesurfer = WaveSurfer.create(options);
|
||||
// Load audio from URL
|
||||
wavesurfer.load('example/media/demo.wav');
|
||||
|
||||
// Regions
|
||||
if (wavesurfer.enableDragSelection) {
|
||||
wavesurfer.enableDragSelection({
|
||||
color: 'rgba(0, 255, 0, 0.1)'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Play at once when ready
|
||||
// Won't work on iOS until you touch the page
|
||||
wavesurfer.on('ready', function() {
|
||||
//wavesurfer.play();
|
||||
});
|
||||
|
||||
// Report errors
|
||||
wavesurfer.on('error', function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
// Do something when the clip is over
|
||||
wavesurfer.on('finish', function() {
|
||||
console.log('Finished playing');
|
||||
});
|
||||
|
||||
/* Progress bar */
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const progressDiv = document.querySelector('#progress-bar');
|
||||
const progressBar = progressDiv.querySelector('.progress-bar');
|
||||
|
||||
let showProgress = function(percent) {
|
||||
progressDiv.style.display = 'block';
|
||||
progressBar.style.width = percent + '%';
|
||||
};
|
||||
|
||||
let hideProgress = function() {
|
||||
progressDiv.style.display = 'none';
|
||||
};
|
||||
|
||||
wavesurfer.on('loading', showProgress);
|
||||
wavesurfer.on('ready', hideProgress);
|
||||
wavesurfer.on('destroy', hideProgress);
|
||||
wavesurfer.on('error', hideProgress);
|
||||
});
|
||||
|
||||
// Drag'n'drop
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let toggleActive = function(e, toggle) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
toggle
|
||||
? e.target.classList.add('wavesurfer-dragover')
|
||||
: e.target.classList.remove('wavesurfer-dragover');
|
||||
};
|
||||
|
||||
let handlers = {
|
||||
// Drop event
|
||||
drop: function(e) {
|
||||
toggleActive(e, false);
|
||||
|
||||
// Load the file into wavesurfer
|
||||
if (e.dataTransfer.files.length) {
|
||||
wavesurfer.loadBlob(e.dataTransfer.files[0]);
|
||||
} else {
|
||||
wavesurfer.fireEvent('error', 'Not a file');
|
||||
}
|
||||
},
|
||||
|
||||
// Drag-over event
|
||||
dragover: function(e) {
|
||||
toggleActive(e, true);
|
||||
},
|
||||
|
||||
// Drag-leave event
|
||||
dragleave: function(e) {
|
||||
toggleActive(e, false);
|
||||
}
|
||||
};
|
||||
|
||||
let dropTarget = document.querySelector('#drop');
|
||||
Object.keys(handlers).forEach(function(event) {
|
||||
dropTarget.addEventListener(event, handlers[event]);
|
||||
});
|
||||
});
|
50
public/scripts/wavesurfer/example/markers/app.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
var wavesurfer; // eslint-disable-line no-var
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Init
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
waveColor: '#A8DBA8',
|
||||
progressColor: '#3B8686',
|
||||
backend: 'MediaElement',
|
||||
plugins: [
|
||||
WaveSurfer.markers.create({
|
||||
markers: [
|
||||
{
|
||||
time: 0,
|
||||
label: "BEGIN",
|
||||
color: '#ff990a'
|
||||
},
|
||||
{
|
||||
time: 5.5,
|
||||
label: "V1",
|
||||
color: '#ff990a'
|
||||
},
|
||||
{
|
||||
time: 10,
|
||||
label: "V2",
|
||||
color: '#00ffcc',
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
time: 24,
|
||||
label: "END",
|
||||
color: '#00ffcc',
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
// Load audio from URL
|
||||
wavesurfer.load('../media/demo.wav');
|
||||
});
|
121
public/scripts/wavesurfer/example/markers/index.html
Normal file
@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Markers</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- plugins -->
|
||||
<script src="../../dist/plugin/wavesurfer.timeline.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.markers.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.minimap.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="../trivia.js"></script>
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">wavesurfer.js Markers</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be waveform -->
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<p>
|
||||
<pre><code>var wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
plugins: [
|
||||
WaveSurfer.markers.create({
|
||||
markers: [
|
||||
{
|
||||
time: 5.5,
|
||||
label: "V1",
|
||||
color: '#ff990a'
|
||||
},
|
||||
{
|
||||
time: 10,
|
||||
label: "V2",
|
||||
color: '#00ffcc',
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
});
|
||||
</code></pre>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<p>
|
||||
The sound file is from <a href="https://librivox.org/librivox-multilingual-short-works-collection-001-by-various/">librivox.org</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
64
public/scripts/wavesurfer/example/media-session/app.js
Normal file
@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
var wavesurfer;
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Init
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
waveColor: 'black',
|
||||
backend: 'MediaElement',
|
||||
plugins: [
|
||||
WaveSurfer.mediasession.create({
|
||||
metadata: {
|
||||
title: 'Wavesurfer.js Example',
|
||||
artist: 'The Wavesurfer.js Project',
|
||||
album: 'Media Session Plugin',
|
||||
artwork: [
|
||||
{
|
||||
src: 'https://dummyimage.com/96x96',
|
||||
sizes: '96x96',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'https://dummyimage.com/128x128',
|
||||
sizes: '128x128',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'https://dummyimage.com/192x192',
|
||||
sizes: '192x192',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'https://dummyimage.com/256x256',
|
||||
sizes: '256x256',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'https://dummyimage.com/384x384',
|
||||
sizes: '384x384',
|
||||
type: 'image/png'
|
||||
},
|
||||
{
|
||||
src: 'https://dummyimage.com/512x512',
|
||||
sizes: '512x512',
|
||||
type: 'image/png'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// controls
|
||||
document
|
||||
.querySelector('[data-action="play"]')
|
||||
.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
|
||||
// load audio from existing media element
|
||||
let mediaElt = document.querySelector('audio');
|
||||
wavesurfer.load(mediaElt);
|
||||
});
|
169
public/scripts/wavesurfer/example/media-session/index.html
Normal file
@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Media Sesssion plugin</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.min.js"></script>
|
||||
|
||||
<!-- media session plugin -->
|
||||
<script src="../../dist/plugin/wavesurfer.mediasession.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1 itemprop="name"><a href="http://wavesurfer-js.org">wavesurfer.js</a><noindex> + Media Session API</noindex></h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform"></div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<audio src="../media/demo.wav" controls />
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<div class="col-lg-4">
|
||||
<h4>wavesurfer.js Media Session Plugin</h4>
|
||||
|
||||
<p itemprop="about">The <a href="https://wicg.github.io/mediasession/" target="blank">Media Session API</a> plugin for <strong>wavesurfer.js</strong> allows you to customize media notifications
|
||||
by providing metadata for the media your web app is playing. It also allows you to handle media related events such as
|
||||
seeking or playback which may come from notifications or media keys.</p>
|
||||
|
||||
<p><strong>Note</strong>: this plugin only works in Chrome 57 and newer (Firefox is <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1112032" target="blank">working on it</a>).</p>
|
||||
|
||||
<h4>Installation</h4>
|
||||
|
||||
<p>
|
||||
<ol>
|
||||
<li>add the MediaSession plugin script tag</li>
|
||||
<li>create a <code>WaveSurfer</code> instance and supply an object for the <code>metadata</code> property</li>
|
||||
<li>create a <code>MediaSession</code> instance</li>
|
||||
<li>control playback from the notification screen on a mobile device</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-large btn-success" href="../../dist/plugin/wavesurfer.mediasession.js" itemprop="downloadUrl">Download <strong>the plugin</strong></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<h4>Quick Start</h4>
|
||||
|
||||
<noindex><p>
|
||||
<pre><code>
|
||||
var wavesurfer = WaveSurfer.create({
|
||||
container : '#waveform',
|
||||
waveColor : 'black',
|
||||
plugins: [
|
||||
WaveSurfer.mediasession.create({
|
||||
metadata: {
|
||||
title: 'Wavesurfer.js Example',
|
||||
artist: 'The Wavesurfer.js Project',
|
||||
album: 'Media Session Example',
|
||||
artwork: [
|
||||
{src: 'https://dummyimage.com/96x96', sizes: '96x96', type: 'image/png'},
|
||||
{src: 'https://dummyimage.com/128x128', sizes: '128x128', type: 'image/png'},
|
||||
{src: 'https://dummyimage.com/192x192', sizes: '192x192', type: 'image/png'},
|
||||
{src: 'https://dummyimage.com/256x256', sizes: '256x256', type: 'image/png'},
|
||||
{src: 'https://dummyimage.com/384x384', sizes: '384x384', type: 'image/png'},
|
||||
{src: 'https://dummyimage.com/512x512', sizes: '512x512', type: 'image/png'},
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// load audio from existing media element
|
||||
var mediaElt = document.querySelector('audio');
|
||||
wavesurfer.load(mediaElt);
|
||||
</code></pre>
|
||||
</p></noindex>
|
||||
|
||||
<br />
|
||||
|
||||
<h4>Options</h4>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Required</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>wavesurfer</code></td>
|
||||
<td>yes</td>
|
||||
<td></td>
|
||||
<td>A WaveSurfer instance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>metadata</code></td>
|
||||
<td>yes</td>
|
||||
<td></td>
|
||||
<td>A <a href="https://wicg.github.io/mediasession/#mediametadata" target="blank">MediaMetadata</a> object: a representation of the metadata associated with a MediaSession that can be used by
|
||||
user agents to provide customized user interface.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
1
public/scripts/wavesurfer/example/media/demo-peaks.json
Normal file
BIN
public/scripts/wavesurfer/example/media/demo.wav
Normal file
BIN
public/scripts/wavesurfer/example/media/demo_video.mp4
Normal file
1
public/scripts/wavesurfer/example/media/nasa.json
Normal file
@ -0,0 +1 @@
|
||||
[{"start": 2.48, "end": 10.64, "data": {}}, {"start": 10.94, "end": 15.6, "data": {}}, {"start": 16.3, "end": 25.28, "data": {}}, {"start": 25.58, "end": 26.84, "data": {}}, {"start": 27.18, "end": 29.72, "data": {}}, {"start": 30.02, "end": 39.06, "data": {}}, {"start": 39.7, "end": 40.28, "data": {}}, {"start": 41.66, "end": 42.42, "data": {}}, {"start": 45.92, "end": 54.92, "data": {}}, {"start": 55.9, "end": 60.18, "data": {}}, {"start": 60.56, "end": 65.84, "data": {}}, {"start": 66.46, "end": 71.06, "data": {}}, {"start": 71.4, "end": 76.22, "data": {}}, {"start": 77.16, "end": 81.94, "data": {}}, {"start": 82.26, "end": 83.96, "data": {}}]
|
BIN
public/scripts/wavesurfer/example/media/nasa.mp4
Normal file
BIN
public/scripts/wavesurfer/example/media/stereo.mp3
Normal file
@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
// Create an instance
|
||||
let wavesurfer = {};
|
||||
|
||||
// Init & load audio file
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Init
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#waveform'),
|
||||
backend: 'MediaElementWebAudio',
|
||||
minPxPerSec: 30,
|
||||
scrollParent: true,
|
||||
waveColor: '#A8DBA8',
|
||||
progressColor: '#3B8686',
|
||||
plugins: [
|
||||
WaveSurfer.timeline.create({
|
||||
container: '#timeline'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// get audio peaks
|
||||
fetch('stereo-peaks.json')
|
||||
.then(response => {
|
||||
return response.json();
|
||||
})
|
||||
.then(peaks => {
|
||||
// normalize audio peaks to be in range [-1, +1]: get the maximum value of data, then divide all data by max
|
||||
let max = peaks.data.reduce((max, el) => (el > max ? el : max));
|
||||
|
||||
return peaks.data.map(el => {
|
||||
return el / max;
|
||||
});
|
||||
})
|
||||
.then(normalizedPeaks => {
|
||||
// You can load audio from HTML5 tag, or passing an url (from same domain or from another server, if it supports CORS headers)
|
||||
let audio = document.createElement('audio');
|
||||
audio.src = '../media/stereo.mp3';
|
||||
// Set crossOrigin to anonymous to avoid CORS restrictions
|
||||
audio.crossOrigin = 'anonymous';
|
||||
wavesurfer.load(audio, normalizedPeaks, 51);
|
||||
});
|
||||
|
||||
// StereoPanner Node
|
||||
wavesurfer.panner = wavesurfer.backend.ac.createStereoPanner();
|
||||
|
||||
let sliderPanner = document.querySelector('[data-action="pan"]');
|
||||
sliderPanner.addEventListener('input', () => {
|
||||
wavesurfer.panner.pan.value = Number(sliderPanner.value);
|
||||
});
|
||||
|
||||
//Control volume of both channels
|
||||
const channelSplitterNode = wavesurfer.backend.ac.createChannelSplitter(2);
|
||||
const channelMergerNode = wavesurfer.backend.ac.createChannelMerger(2);
|
||||
const leftGainNode = wavesurfer.backend.ac.createGain();
|
||||
const rightGainNode = wavesurfer.backend.ac.createGain();
|
||||
|
||||
channelSplitterNode.connect(leftGainNode, 0);
|
||||
leftGainNode.gain.value = 0.8;
|
||||
|
||||
channelSplitterNode.connect(rightGainNode, 1);
|
||||
rightGainNode.gain.value = 0.8;
|
||||
|
||||
leftGainNode.connect(channelMergerNode, 0, 0);
|
||||
rightGainNode.connect(channelMergerNode, 0, 1);
|
||||
wavesurfer.backend.setFilters([
|
||||
channelSplitterNode,
|
||||
leftGainNode,
|
||||
channelMergerNode,
|
||||
wavesurfer.panner
|
||||
]);
|
||||
|
||||
let sliderLeftVolume = document.querySelector('[data-action="leftVolume"]');
|
||||
sliderLeftVolume.addEventListener('input', () => {
|
||||
leftGainNode.gain.value = Number(sliderLeftVolume.value);
|
||||
});
|
||||
|
||||
let sliderRightVolume = document.querySelector(
|
||||
'[data-action="rightVolume"]'
|
||||
);
|
||||
sliderRightVolume.addEventListener('input', () => {
|
||||
rightGainNode.gain.value = Number(sliderRightVolume.value);
|
||||
});
|
||||
|
||||
// Log errors
|
||||
wavesurfer.on('error', function(msg) {
|
||||
console.log(msg);
|
||||
});
|
||||
|
||||
// Bind play/pause button
|
||||
document
|
||||
.querySelector('[data-action="play"]')
|
||||
.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
});
|
@ -0,0 +1,152 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | MediaElementWebAudio backend Example</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.timeline.js"></script>
|
||||
<script src="../../dist/plugin/wavesurfer.regions.js"></script>
|
||||
|
||||
<!-- Demo -->
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<h1 itemprop="name">MediaElementWebAudio Backend Example</h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be the waveform -->
|
||||
</div>
|
||||
|
||||
<div id="timeline"></div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<p style="text-align: left">StereoPanner</p>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
← left
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<!-- Panner -->
|
||||
<input data-action="pan" type="range" min="-1" max="1" value="0" step="0.2" style="width: 100%" />
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
right →
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-sm-3" style="margin-right: 0">
|
||||
<p style="text-align: left">Left Channel Volume</p>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<input data-action="leftVolume" type="range" min="0" max="1" value="0.8" step="0.2" style="width: 100%" />
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<p style="text-align: right">Right Channel Volume</p>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<input data-action="rightVolume" type="range" min="0" max="1" value="0.8" step="0.2" style="width: 100%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<h3>Why use MediaElementWebAudio backend</h3>
|
||||
<p style="text-align: justify">With this backend you can load a big audio file and use it with WebAudio API. In this example it was added a StereoPannerNode and
|
||||
nodes to control independently the volume of both right and left channels, realised with SplitterNode and MergerNode to split and then merge channels with GainNode to control their volume.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<pre><code>
|
||||
var wavesurfer = WaveSurfer.create({
|
||||
container: document.querySelector('#wave'),
|
||||
backend: 'MediaElementWebAudio'
|
||||
});
|
||||
// You have to use the same methods of MediaElement backend to load the audio file, passing peaks
|
||||
wavesurfer.load('big_audio.mp3', normalizedPeaks, 11625);
|
||||
|
||||
// Example for StereoPanner node
|
||||
wavesurfer.panner = wavesurfer.backend.ac.createStereoPanner();
|
||||
let sliderPanner = document.querySelector('[data-action="pan"]');
|
||||
sliderPanner.addEventListener('input', () => {
|
||||
wavesurfer.panner.pan.value = Number(sliderPanner.value);
|
||||
});
|
||||
wavesurfer.backend.setFilter(wavesurfer.panner)
|
||||
|
||||
</code></pre>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
62
public/scripts/wavesurfer/example/microphone/app.js
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
let wavesurfer, context, processor;
|
||||
|
||||
// Init & load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let micBtn = document.querySelector('#micBtn');
|
||||
|
||||
micBtn.onclick = function() {
|
||||
if (wavesurfer === undefined) {
|
||||
if (isSafari) {
|
||||
// Safari 11 or newer automatically suspends new AudioContext's that aren't
|
||||
// created in response to a user-gesture, like a click or tap, so create one
|
||||
// here (inc. the script processor)
|
||||
let AudioContext =
|
||||
window.AudioContext || window.webkitAudioContext;
|
||||
context = new AudioContext();
|
||||
processor = context.createScriptProcessor(1024, 1, 1);
|
||||
}
|
||||
|
||||
// Init wavesurfer
|
||||
wavesurfer = WaveSurfer.create({
|
||||
container: '#waveform',
|
||||
waveColor: 'black',
|
||||
interact: false,
|
||||
cursorWidth: 0,
|
||||
audioContext: context || null,
|
||||
audioScriptProcessor: processor || null,
|
||||
plugins: [
|
||||
WaveSurfer.microphone.create({
|
||||
bufferSize: 4096,
|
||||
numberOfInputChannels: 1,
|
||||
numberOfOutputChannels: 1,
|
||||
constraints: {
|
||||
video: false,
|
||||
audio: true
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
wavesurfer.microphone.on('deviceReady', function() {
|
||||
console.info('Device ready!');
|
||||
});
|
||||
wavesurfer.microphone.on('deviceError', function(code) {
|
||||
console.warn('Device error: ' + code);
|
||||
});
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
wavesurfer.microphone.start();
|
||||
} else {
|
||||
// start/stop mic on button click
|
||||
if (wavesurfer.microphone.active) {
|
||||
wavesurfer.microphone.stop();
|
||||
} else {
|
||||
wavesurfer.microphone.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
209
public/scripts/wavesurfer/example/microphone/index.html
Normal file
@ -0,0 +1,209 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Microphone plugin</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- microphone plugin -->
|
||||
<script src="../../dist/plugin/wavesurfer.microphone.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1 itemprop="name"><a href="http://wavesurfer-js.org">wavesurfer.js</a><noindex> + Microphone</noindex></h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform"></div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="micBtn" class="btn btn-primary" data-action="start">
|
||||
Microphone:
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Start
|
||||
/
|
||||
<i class="glyphicon glyphicon-stop"></i>
|
||||
Stop
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<div class="col-lg-4">
|
||||
<h4>wavesurfer.js Microphone Plugin</h4>
|
||||
|
||||
<p itemprop="about">Visualizes audio input from a microphone in <strong>wavesurfer.js</strong> instances.</p>
|
||||
|
||||
<h4>Installation</h4>
|
||||
|
||||
<p>
|
||||
<ol>
|
||||
<li>add the Microphone plugin to the plugins property of the wavesurfer options</li>
|
||||
<li>create a new instance of wavesurfer by using the create function</li>
|
||||
<li>control the Microphone using the <code>start</code>, <code>stopDevice</code>, <code>play</code>, <code>pause</code>, <code>stop</code> and <code>togglePlay</code> methods</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-large btn-success" href="../../dist/plugin/wavesurfer.microphone.min.js" itemprop="downloadUrl" download>Download the plugin</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<h4>Quick Start</h4>
|
||||
|
||||
<noindex><p>
|
||||
<pre><code>var wavesurfer = WaveSurfer.create({
|
||||
container : '#waveform',
|
||||
waveColor : 'black',
|
||||
interact : false,
|
||||
cursorWidth : 0,
|
||||
plugins: [
|
||||
WaveSurfer.microphone.create()
|
||||
]
|
||||
});
|
||||
|
||||
wavesurfer.microphone.on('deviceReady', function(stream) {
|
||||
console.log('Device ready!', stream);
|
||||
});
|
||||
wavesurfer.microphone.on('deviceError', function(code) {
|
||||
console.warn('Device error: ' + code);
|
||||
});
|
||||
|
||||
// start the microphone
|
||||
wavesurfer.microphone.start();
|
||||
|
||||
// pause rendering
|
||||
//wavesurfer.microphone.pause();
|
||||
|
||||
// resume rendering
|
||||
//wavesurfer.microphone.play();
|
||||
|
||||
// stop visualization and disconnect microphone
|
||||
//wavesurfer.microphone.stopDevice();
|
||||
|
||||
// same as stopDevice() but also clears the wavesurfer canvas
|
||||
//wavesurfer.microphone.stop();
|
||||
|
||||
// destroy the plugin
|
||||
//wavesurfer.microphone.destroy();
|
||||
</code></pre>
|
||||
</p></noindex>
|
||||
|
||||
<br />
|
||||
|
||||
<h4>Options</h4>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Required</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>wavesurfer</code></td>
|
||||
<td>yes</td>
|
||||
<td></td>
|
||||
<td>A WaveSurfer instance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>bufferSize</code></td>
|
||||
<td>no</td>
|
||||
<td>4096</td>
|
||||
<td>The buffer size in units of sample-frames. If specified, the <code>bufferSize</code> must be one of the following values: 256, 512, 1024, 2048, 4096, 8192, 16384.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>constraints</code></td>
|
||||
<td>no</td>
|
||||
<td><code>{audio: true, video: false}</code></td>
|
||||
<td>The constraints parameter is a MediaStreamConstaints object with at least two members: video and audio, describing the media types requested. Either or both must be specified.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>numberOfInputChannels</code></td>
|
||||
<td>no</td>
|
||||
<td>1</td>
|
||||
<td>Integer specifying the number of channels for this node's input. Values of up to 32 are supported.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>numberOfOutputChannels</code></td>
|
||||
<td>no</td>
|
||||
<td>1</td>
|
||||
<td>Integer specifying the number of channels for this node's output. Values of up to 32 are supported.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4>Events</h4>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>deviceReady</code></td>
|
||||
<td>Invoked when the device is ready to use. Callback will receive a <code>MediaStream</code> object that contains the microphone stream.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>deviceError</code></td>
|
||||
<td>Invoked when the user doesn't allow the browser to access the microphone. Callback will receive a (string) <a href="https://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia#errorCallback">error code</a>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
38
public/scripts/wavesurfer/example/minimap/app.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
var wavesurfer;
|
||||
|
||||
function init() {
|
||||
// configure
|
||||
let options = {
|
||||
container: '#waveform',
|
||||
waveColor: 'violet',
|
||||
progressColor: 'purple',
|
||||
loaderColor: 'purple',
|
||||
cursorColor: 'navy',
|
||||
plugins: [
|
||||
WaveSurfer.minimap.create({
|
||||
container: '#wave-minimap',
|
||||
waveColor: '#777',
|
||||
progressColor: '#222',
|
||||
height: 50
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
// create an instance
|
||||
wavesurfer = WaveSurfer.create(options);
|
||||
|
||||
wavesurfer.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector('[data-action="play"]')
|
||||
.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||
|
||||
wavesurfer.load('../media/demo.wav');
|
||||
}
|
||||
|
||||
// Init & load
|
||||
document.addEventListener('DOMContentLoaded', init);
|
110
public/scripts/wavesurfer/example/minimap/index.html
Normal file
@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>wavesurfer.js | Minimap plugin</title>
|
||||
|
||||
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/ribbon.css" />
|
||||
|
||||
<!-- wavesurfer.js -->
|
||||
<script src="../../dist/wavesurfer.js"></script>
|
||||
|
||||
<!-- minimap plugin -->
|
||||
<script src="../../dist/plugin/wavesurfer.minimap.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- highlight.js for syntax highlighting in this example -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="http://schema.org/WebApplication">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1 itemprop="name"><a href="http://wavesurfer-js.org">wavesurfer.js</a><noindex> + Minimap</noindex></h1>
|
||||
</div>
|
||||
|
||||
<div id="demo">
|
||||
<div id="waveform">
|
||||
<!-- Here be waveform -->
|
||||
</div>
|
||||
<div id="wave-minimap"></div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" data-action="play">
|
||||
<i class="glyphicon glyphicon-play"></i>
|
||||
Play
|
||||
/
|
||||
<i class="glyphicon glyphicon-pause"></i>
|
||||
Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row marketing">
|
||||
<div class="col-lg-4">
|
||||
<h4>wavesurfer.js Minimap Plugin</h4>
|
||||
|
||||
<p itemprop="about">Adds a simple minimap to your <strong>wavesurfer.js</strong> instances.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<h4>Quick Start</h4>
|
||||
|
||||
<noindex><p>
|
||||
<pre><code>var wavesurfer = WaveSurfer.create({
|
||||
// your options here
|
||||
plugins: [
|
||||
WaveSurfer.minimap.create({
|
||||
container: '#wave-minimap',
|
||||
waveColor: '#777',
|
||||
progressColor: '#222',
|
||||
height: 50
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
wavesurfer.load('../media/demo.wav');</code></pre>
|
||||
</p></noindex>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer row">
|
||||
<div class="col-sm-12">
|
||||
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a <a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="github-fork-ribbon-wrapper right">
|
||||
<div class="github-fork-ribbon">
|
||||
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-50026819-1', 'wavesurfer.fm');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|