feat(legacy): visual cue point editor (#2947)
A visual cue point editor in the track editor view. This view displays the track as a waveform and allows you to set where the in- and out-cue points are set. These cue points determine the start and end points of the track. --------- Co-authored-by: Thomas Göttgens <tgoettgens@mail.com> Co-authored-by: Kyle Robbertze <paddatrapper@users.noreply.github.com>
This commit is contained in:
parent
71b20ae3c9
commit
da02e74f21
|
@ -26,7 +26,7 @@
|
|||
"css/media_library.css": "e1982d1f673543f7730898fb49450f8b",
|
||||
"css/player-form.css": "e08a4545715fc56b75c845b44a5b2a1c",
|
||||
"css/player.css": "904bc7aede4d5f0372468528d88094f1",
|
||||
"css/playlist_builder.css": "e92ef56ddffca440a7741934edbb7f7f",
|
||||
"css/playlist_builder.css": "9e35f1b7a1e79a7a73e7e9666d5a711f",
|
||||
"css/playouthistory.css": "983cc1bac566b18b745b6e0da9ef3c0c",
|
||||
"css/plupload.queue.css": "0acfb6b54c18654452727d4abf297394",
|
||||
"css/pro_dropdown_3.css": "9848a27dad960c2218751c1656e9206a",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"css/show_analytics.css": "4393c521308277447afabe8791779bf1",
|
||||
"css/showbuilder.css": "4421c01b5c2dfb03f8d06dd6023b4bd7",
|
||||
"css/station_podcast.css": "88e9b38ead71eddc69ef50bfc8cb2d0d",
|
||||
"css/styles.css": "6890a553402f44cefc7c6915f38aa657",
|
||||
"css/styles.css": "29ce2292b2c007e86d192d0deeaf88ad",
|
||||
"css/tipsy/jquery.tipsy.css": "b13517583583f83ed7d5fc067a0c9372",
|
||||
"css/tracktypes.css": "94c94817a8505ff4dfcd090987859a7e",
|
||||
"css/users.css": "94c94817a8505ff4dfcd090987859a7e",
|
||||
|
@ -63,7 +63,7 @@
|
|||
"js/airtime/library/plupload.js": "0f6be5b133650828b9ffc74e7852dc89",
|
||||
"js/airtime/library/podcast.js": "4dedd84cb571cdba2401bfb8ba621e69",
|
||||
"js/airtime/library/publish.js": "ab3a1452dd332cdb0773241a1c17b7e0",
|
||||
"js/airtime/library/spl.js": "c4cbac0c237b548064685a2cb16d3fa2",
|
||||
"js/airtime/library/spl.js": "5bddd886303ff15e8b78e79b30a9e56f",
|
||||
"js/airtime/listenerstat/listenerstat.js": "a3733dae8f9549668125ec9852d356ed",
|
||||
"js/airtime/listenerstat/showlistenerstat.js": "7cf0c375420f1c8471d304bc8758b2cd",
|
||||
"js/airtime/login/login.js": "7278cf49618791d75bacce38dd1b1d46",
|
||||
|
@ -180,5 +180,16 @@
|
|||
"js/waveformplaylist/playout.js": "7dfc5fe760f3c6739e38499df7b61e47",
|
||||
"js/waveformplaylist/time_scale.js": "74e0e17e1c8cd597449220c98de408ba",
|
||||
"js/waveformplaylist/track.js": "5456e6081ffedf55a9e38571bc178781",
|
||||
"js/waveformplaylist/track_render.js": "e371b582b23e4b618e039f096d2f0570"
|
||||
"js/waveformplaylist/track_render.js": "e371b582b23e4b618e039f096d2f0570",
|
||||
"js/wavesurfer/cursor.js": "8ed17a7437f3ec84972d15d0073249b2",
|
||||
"js/wavesurfer/cursor.min.js": "831165862b629e615cf59112fa00d963",
|
||||
"js/wavesurfer/libretime.js": "17133cacf09fc204a572b56c99d44278",
|
||||
"js/wavesurfer/minimap.js": "c17dd315386006bb3cffdabb5f715c7b",
|
||||
"js/wavesurfer/minimap.min.js": "90b2f2d1d1b4eb189d1a9c3c27dcb4f7",
|
||||
"js/wavesurfer/regions.js": "aafe4f696d3da50c976d11e472fd56d1",
|
||||
"js/wavesurfer/regions.min.js": "2ed2f8b5880beee568942000a6139e85",
|
||||
"js/wavesurfer/timeline.js": "0bd70779070513c2a4f34237a0f9f573",
|
||||
"js/wavesurfer/timeline.min.js": "90ea16b23cacebfad10cad42f94403d0",
|
||||
"js/wavesurfer/wavesurfer.js": "9e2ced8a136449f4fd78911b0f01f6ed",
|
||||
"js/wavesurfer/wavesurfer.min.js": "42ebd7fdd574dfe8cae587145751a1f2"
|
||||
}
|
||||
|
|
|
@ -400,6 +400,12 @@ class LibraryController extends Zend_Controller_Action
|
|||
$this->view->artist_name = $file->getPropelOrm()->getDbArtistName();
|
||||
$this->view->filePath = $file->getPropelOrm()->getDbFilepath();
|
||||
$this->view->artwork = $file->getPropelOrm()->getDbArtwork();
|
||||
$this->view->replay_gain = $file->getPropelOrm()->getDbReplayGain();
|
||||
$this->view->cuein = $file->getPropelOrm()->getDbCuein();
|
||||
$this->view->cueout = $file->getPropelOrm()->getDbCueout();
|
||||
$this->view->format = $file->getPropelOrm()->getDbFormat();
|
||||
$this->view->bit_rate = $file->getPropelOrm()->getDbBitRate();
|
||||
$this->view->sample_rate = $file->getPropelOrm()->getDbSampleRate();
|
||||
$this->view->html = $this->view->render('library/edit-file-md.phtml');
|
||||
}
|
||||
|
||||
|
|
|
@ -217,6 +217,13 @@ class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract
|
|||
->appendFile(Assets::url('js/airtime/common/common.js'), 'text/javascript')
|
||||
->appendFile(Assets::url('js/airtime/common/audioplaytest.js'), 'text/javascript');
|
||||
|
||||
// include wavesurfer.js for waveform display
|
||||
$view->headScript()->appendFile(Assets::url('js/wavesurfer/wavesurfer.min.js'), 'text/javascript')
|
||||
->appendFile(Assets::url('js/wavesurfer/timeline.min.js'), 'text/javascript')
|
||||
->appendFile(Assets::url('js/wavesurfer/regions.min.js'), 'text/javascript')
|
||||
->appendFile(Assets::url('js/wavesurfer/cursor.min.js'), 'text/javascript')
|
||||
->appendFile(Assets::url('js/wavesurfer/libretime.js'), 'text/javascript');
|
||||
|
||||
$user = Application_Model_User::getCurrentUser();
|
||||
if (!is_null($user)) {
|
||||
$userType = $user->getType();
|
||||
|
|
|
@ -8,9 +8,11 @@ class Application_Form_EditAudioMD extends Zend_Form
|
|||
{
|
||||
// Set the method for the display form to POST
|
||||
$this->setMethod('post');
|
||||
$this->setAttrib('id', 'track_edit_' . $p_id);
|
||||
|
||||
$file_id = new Zend_Form_Element_Hidden('file_id');
|
||||
$file_id->setValue($p_id);
|
||||
$file_id->setDecorators(['ViewHelper']);
|
||||
$file_id->addDecorator('HtmlTag', ['tag' => 'div', 'style' => 'display:none']);
|
||||
$file_id->removeDecorator('Label');
|
||||
$file_id->setAttrib('class', 'obj_id');
|
||||
|
@ -188,6 +190,14 @@ class Application_Form_EditAudioMD extends Zend_Form
|
|||
]);
|
||||
$this->addElement($mood);
|
||||
|
||||
// Add replay gain field
|
||||
$replay_gain = new Zend_Form_Element_Hidden('replay_gain');
|
||||
$replay_gain->class = 'input_text replay_gain_' . $p_id;
|
||||
$replay_gain->setLabel(_('Replay Gain:'))
|
||||
->addDecorator('HtmlTag', ['tag' => 'div', 'style' => 'display:none'])
|
||||
->removeDecorator('Label');
|
||||
$this->addElement($replay_gain);
|
||||
|
||||
// Add bmp field
|
||||
$bpm = new Zend_Form_Element_Text('bpm');
|
||||
$bpm->class = 'input_text';
|
||||
|
@ -242,7 +252,7 @@ class Application_Form_EditAudioMD extends Zend_Form
|
|||
$validCuePattern = '/^(?:[0-9]{1,2}:)?(?:[0-9]{1,2}:)?[0-9]{1,6}(\.\d{1,6})?$/';
|
||||
|
||||
$cueIn = new Zend_Form_Element_Text('cuein');
|
||||
$cueIn->class = 'input_text';
|
||||
$cueIn->class = 'input_text cuein_' . $p_id;
|
||||
$cueIn->setLabel('Cue In:');
|
||||
$cueInValidator = Application_Form_Helper_ValidationTypes::overrideRegexValidator(
|
||||
$validCuePattern,
|
||||
|
@ -252,7 +262,7 @@ class Application_Form_EditAudioMD extends Zend_Form
|
|||
$this->addElement($cueIn);
|
||||
|
||||
$cueOut = new Zend_Form_Element_Text('cueout');
|
||||
$cueOut->class = 'input_text';
|
||||
$cueOut->class = 'input_text cueout_' . $p_id;
|
||||
$cueOut->setLabel('Cue Out:');
|
||||
$cueOutValidator = Application_Form_Helper_ValidationTypes::overrideRegexValidator(
|
||||
$validCuePattern,
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
<?php $get_artwork = FileDataHelper::getArtworkData($this->artwork, 256); ?>
|
||||
<?php $get_artwork = FileDataHelper::getArtworkData($this->artwork, 256);
|
||||
|
||||
$baseUrl = Config::getBasePath();
|
||||
|
||||
$get_replay_gain = Application_Model_Preference::getReplayGainModifier();
|
||||
if (!Application_Model_Preference::GetEnableReplayGain() ) {
|
||||
$get_replay_gain = 0;
|
||||
}
|
||||
$analogMeter = true;
|
||||
?>
|
||||
<div class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong edit-md-dialog">
|
||||
<div class="track-edit-header" style="top:15px">
|
||||
<?php if ($this->permissionDenied) { ?> <h3><?php echo _("You do not have permission to edit this track.") ?></h3> <?php } ?>
|
||||
|
@ -36,4 +45,142 @@
|
|||
</div>
|
||||
<div style="height: 160px;"></div>
|
||||
<?php echo $this->form; ?>
|
||||
<div class="collapsible-header closed"><span class="arrow-icon"></span><?php echo _("Visual Waveform Editor"); ?></div>
|
||||
<div class="visual-waveform-editor" style="clear:both;padding:18px 24px 0 0;">
|
||||
<div class="controls">
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<div class="btn-toolbar track-toolbar">
|
||||
<div class="btn-group" title="Play Controls">
|
||||
<button class="btn btn-control-player btn-new control-play-btn" id="track-play-<?php echo $this->id; ?>" onClick="wavesurfer['t<?php echo $this->id; ?>'].playPause();" style="background-color:#555555; border-top-left-radius: 5px; border-bottom-left-radius: 5px;">
|
||||
<i class="icon-white icon-play"></i>
|
||||
</button>
|
||||
<button class="btn btn-control-player btn-new control-playedit-btn" id="track-playedit-<?php echo $this->id; ?>" onClick="" style="background-color:#555555; border-top-right-radius: 5px; border-bottom-right-radius: 5px">
|
||||
<i class="icon-white icon-step-backward"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group" title="Playhead">
|
||||
<div class="btn track-timer" style="border-radius: 5px">
|
||||
<input class="track-timer-input" id="tracktimerinput-<?php echo $this->id; ?>" val="0.000" style="font-size:15px; font-weight:500;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" title="Cue Controls">
|
||||
<img src="<?php echo $baseUrl.'css/img/icon_cut_white.png';?>" style="top:6px;margin-right:5px;">
|
||||
<button class="btn btn-control-player btn-new" id="cuein-set-<?php echo $this->id; ?>" style="background-color:#00e640; border-top-left-radius: 5px; border-bottom-left-radius: 5px;">
|
||||
<i class="icon-white icon-chevron-right"></i>
|
||||
</button>
|
||||
<button class="btn btn-control-player btn-new" id="cueout-set-<?php echo $this->id; ?>" style="background-color:#f22613; border-top-right-radius: 5px; border-bottom-right-radius: 5px">
|
||||
<i class="icon-white icon-chevron-left"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group pull-right zoom-container" title="Zoom">
|
||||
<i class="icon-white icon-zoom-out" style="margin-top: -24px;"></i>
|
||||
<input id="zoom-slider-<?php echo $this->id; ?>" class="input-slider" data-height="26" data-width="120" data-action="zoom-<?php echo $this->id; ?>" type="range" min="20" max="290" data-sprites="50" data-src="<?php echo $baseUrl.'css/images/slider.png'; ?>" value="0" />
|
||||
<i class="icon-white icon-zoom-in" style="margin-top: -24px;"></i>
|
||||
</div>
|
||||
<br><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="track-waveform-<?php echo $this->id; ?>"></div>
|
||||
<div id="timeline-<?php echo $this->id; ?>"></div>
|
||||
</div>
|
||||
<input type="hidden" id="volume-<?php echo $this->id; ?>" type="range" min="0" max="1" value="1" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
//waveform
|
||||
var track_id = <?php echo $this->id; ?>;
|
||||
var selector_id = "#track-waveform-"+<?php echo $this->id; ?>;
|
||||
var url = baseUrl + 'api/get-media/file/<?php echo $this->id; ?>';
|
||||
var cuein = '<?php echo $this->cuein; ?>';
|
||||
var cueout = '<?php echo $this->cueout; ?>';
|
||||
var gain_level = deciSteps(<?php echo $this->replay_gain; ?>);
|
||||
var default_gain = <?php echo $get_replay_gain; ?>;
|
||||
var eTrack = renderWaveform(track_id, selector_id, url, cuein, cueout, gain_level, default_gain);
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$(".collapsible-header")
|
||||
.off("click")
|
||||
.on("click", function () {
|
||||
$(this).toggleClass("visible");
|
||||
$(".visual-waveform-editor").toggle();
|
||||
$(".editor_pane_wrapper").animate({
|
||||
scrollTop: $(".collapsible-header").offset().top * 2
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Counter field edit
|
||||
$(document).on('change', '#tracktimerinput-<?php echo $this->id; ?>', 'input', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var val = $(this).val();
|
||||
eTrack.setCurrentTime(val);
|
||||
}).on('keypress keydown', '#tracktimerinput-<?php echo $this->id; ?>', 'input', function(event) {
|
||||
if (event.key === 'Enter' || event.keyCode === 13 || event.keyCode === 10) {
|
||||
var val = $(this).val();
|
||||
eTrack.setCurrentTime(val);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
//Gain Knob
|
||||
$("#volume-<?php echo $this->id; ?>").val(deciSteps(<?php echo $this->replay_gain; ?>));
|
||||
|
||||
$(document).on('change', '.cuein_<?php echo $this->id; ?>', 'input', function(event) {
|
||||
console.log('cuein input');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var val = $(this).val();
|
||||
var a = val.split(':');
|
||||
var startseconds = (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+a[2]);
|
||||
console.log(startseconds);
|
||||
var region = eTrack.regions.list[track_id];
|
||||
region.update({start: startseconds});
|
||||
$('#track-playedit-'+track_id).attr('onClick', 'wavesurfer["t'+ track_id +'"].play('+ region.start +', '+ region.end +');');
|
||||
});
|
||||
|
||||
$(document).on('change', '.cueout_<?php echo $this->id; ?>', 'input', function(event) {
|
||||
console.log('cueout input');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var val = $(this).val();
|
||||
var b = val.split(':');
|
||||
var endseconds = (+b[0]) * 60 * 60 + (+b[1]) * 60 + (+b[2]);
|
||||
var region = eTrack.regions.list[track_id];
|
||||
region.update({end: endseconds});
|
||||
$('#track-playedit-'+track_id).attr('onClick', 'wavesurfer["t'+ track_id +'"].play('+ region.start +', '+ region.end +');');
|
||||
});
|
||||
|
||||
$(document).on('click', '#cuein-set-<?php echo $this->id; ?>', 'button', function(event) {
|
||||
console.log('cuein button');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var val = eTrack.getCurrentTime();
|
||||
var region = eTrack.regions.list[track_id];
|
||||
region.update({start: val});
|
||||
document.getElementsByClassName("cuein_"+track_id)[0].value = toHHMMSS(region.start);
|
||||
$('#track-playedit-'+track_id).attr('onClick', 'wavesurfer["t'+ track_id +'"].play('+ region.start +', '+ region.end +');');
|
||||
});
|
||||
|
||||
$(document).on('click', '#cueout-set-<?php echo $this->id; ?>', 'button', function(event) {
|
||||
console.log('cueout button')
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var val = eTrack.getCurrentTime();
|
||||
var region = eTrack.regions.list[track_id];
|
||||
region.update({end: val});
|
||||
document.getElementsByClassName("cueout_"+track_id)[0].value = toHHMMSS(region.end);
|
||||
$('#track-playedit-'+track_id).attr('onClick', 'wavesurfer["t'+ track_id +'"].play('+ region.start +', '+ region.end +');');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
|
@ -658,7 +658,7 @@ li.spl_empty {
|
|||
.collapsible-header.visible .arrow-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.smart-block-advanced {
|
||||
.smart-block-advanced, .visual-waveform-editor {
|
||||
display: none;
|
||||
}
|
||||
.smart-block-form .smart-block-advanced dt {
|
||||
|
|
|
@ -4350,3 +4350,95 @@ body.droppable .artwork-upload .artwork-preview {
|
|||
border: 2px dashed lightblue;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* Cue editor GUI starts here */
|
||||
|
||||
.track-file-details {
|
||||
font-size: 14px;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.track-window {
|
||||
top: 285px;
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
/* -webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
-ms-appearance: none;
|
||||
appearance: none; */
|
||||
textarea,
|
||||
input {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus,
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navtt {
|
||||
margin-bottom: 18px;
|
||||
margin-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.navtt > li > a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.navtt-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navtt-content.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-control-player {
|
||||
padding: 5px 7px 5px 10px;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.zoom-container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.track-toolbar {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.track-timer {
|
||||
border-radius: 5px;
|
||||
background-image: none;
|
||||
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
-moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
/*background-color: #727272;*/
|
||||
outline: 0;
|
||||
border-top-color: #333333;
|
||||
/*background: #246B86;*/
|
||||
|
||||
width: 65px;
|
||||
/*padding: 5px 7px 5px 10px;*/
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
.track-timer-input {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
width: 75px;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Track Edit - Cue and Gain editor GUI ends here */
|
||||
|
|
|
@ -1160,8 +1160,8 @@ var AIRTIME = (function (AIRTIME) {
|
|||
});
|
||||
|
||||
newTab.wrapper.find(".edit-md-dialog").on("keyup", function (event) {
|
||||
// Don't submit if the user hits enter in a textarea (description)
|
||||
if ($(event.target).is("input") && event.keyCode === 13) {
|
||||
// Don't submit if the user hits enter in a textarea (description) or in the waveform editor
|
||||
if ($(event.target).is("form input") && event.keyCode === 13) {
|
||||
newTab.wrapper.find(".md-save").click();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,404 @@
|
|||
/*!
|
||||
* wavesurfer.js cursor plugin 4.6.0 (2024-02-05)
|
||||
* https://wavesurfer-js.org
|
||||
* @license BSD-3-Clause
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define("WaveSurfer", [], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["WaveSurfer"] = factory();
|
||||
else
|
||||
root["WaveSurfer"] = root["WaveSurfer"] || {}, root["WaveSurfer"]["cursor"] = factory();
|
||||
})(this, () => {
|
||||
return /******/ (() => { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ "./src/plugin/cursor/index.js":
|
||||
/*!************************************!*\
|
||||
!*** ./src/plugin/cursor/index.js ***!
|
||||
\************************************/
|
||||
/***/ ((module, exports) => {
|
||||
|
||||
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({
|
||||
value: true
|
||||
}));
|
||||
exports["default"] = void 0;
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
/**
|
||||
* @typedef {Object} CursorPluginParams
|
||||
* @property {?boolean} deferInit Set to true to stop auto init in `addPlugin()`
|
||||
* @property {boolean} hideOnBlur=true Hide the cursor when the mouse leaves the
|
||||
* waveform
|
||||
* @property {string} width='1px' The width of the cursor
|
||||
* @property {string} color='black' The color of the cursor
|
||||
* @property {string} opacity='0.25' The opacity of the cursor
|
||||
* @property {string} style='solid' The border style of the cursor
|
||||
* @property {number} zIndex=3 The z-index of the cursor element
|
||||
* @property {object} customStyle An object with custom styles which are applied
|
||||
* to the cursor element
|
||||
* @property {boolean} showTime=false Show the time on the cursor.
|
||||
* @property {object} customShowTimeStyle An object with custom styles which are
|
||||
* applied to the cursor time element.
|
||||
* @property {string} followCursorY=false Use `true` to make the time on
|
||||
* the cursor follow the x and the y-position of the mouse. Use `false` to make the
|
||||
* it only follow the x-position of the mouse.
|
||||
* @property {function} formatTimeCallback Formats the timestamp on the cursor.
|
||||
*/
|
||||
/**
|
||||
* Displays a thin line at the position of the cursor on the waveform.
|
||||
*
|
||||
* @implements {PluginClass}
|
||||
* @extends {Observer}
|
||||
* @example
|
||||
* // es6
|
||||
* import CursorPlugin from 'wavesurfer.cursor.js';
|
||||
*
|
||||
* // commonjs
|
||||
* var CursorPlugin = require('wavesurfer.cursor.js');
|
||||
*
|
||||
* // if you are using <script> tags
|
||||
* var CursorPlugin = window.WaveSurfer.cursor;
|
||||
*
|
||||
* // ... initialising wavesurfer with the plugin
|
||||
* var wavesurfer = WaveSurfer.create({
|
||||
* // wavesurfer options ...
|
||||
* plugins: [
|
||||
* CursorPlugin.create({
|
||||
* // plugin options ...
|
||||
* })
|
||||
* ]
|
||||
* });
|
||||
*/
|
||||
var CursorPlugin = exports["default"] = /*#__PURE__*/function () {
|
||||
/**
|
||||
* Construct the plugin class. You probably want to use `CursorPlugin.create`
|
||||
* instead.
|
||||
*
|
||||
* @param {CursorPluginParams} params Plugin parameters
|
||||
* @param {object} ws Wavesurfer instance
|
||||
*/
|
||||
function CursorPlugin(params, ws) {
|
||||
var _this = this;
|
||||
_classCallCheck(this, CursorPlugin);
|
||||
this.defaultParams = {
|
||||
hideOnBlur: true,
|
||||
width: '1px',
|
||||
color: 'black',
|
||||
opacity: '0.25',
|
||||
style: 'solid',
|
||||
zIndex: 4,
|
||||
customStyle: {},
|
||||
customShowTimeStyle: {},
|
||||
showTime: false,
|
||||
followCursorY: false,
|
||||
formatTimeCallback: null
|
||||
};
|
||||
this._onMousemove = function (e) {
|
||||
var bbox = _this.wavesurfer.container.getBoundingClientRect();
|
||||
var y = 0;
|
||||
var x = e.clientX - bbox.left;
|
||||
var flip = bbox.right < e.clientX + _this.outerWidth(_this.displayTime);
|
||||
if (_this.params.showTime && _this.params.followCursorY) {
|
||||
// follow y-position of the mouse
|
||||
y = e.clientY - (bbox.top + bbox.height / 2);
|
||||
}
|
||||
_this.updateCursorPosition(x, y, flip);
|
||||
};
|
||||
this._onMouseenter = function () {
|
||||
return _this.showCursor();
|
||||
};
|
||||
this._onMouseleave = function () {
|
||||
return _this.hideCursor();
|
||||
};
|
||||
this.wavesurfer = ws;
|
||||
this.style = ws.util.style;
|
||||
/**
|
||||
* The cursor HTML element
|
||||
*
|
||||
* @type {?HTMLElement}
|
||||
*/
|
||||
this.cursor = null;
|
||||
/**
|
||||
* displays the time next to the cursor
|
||||
*
|
||||
* @type {?HTMLElement}
|
||||
*/
|
||||
this.showTime = null;
|
||||
/**
|
||||
* The html container that will display the time
|
||||
*
|
||||
* @type {?HTMLElement}
|
||||
*/
|
||||
this.displayTime = null;
|
||||
this.params = Object.assign({}, this.defaultParams, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the plugin (used by the Plugin API)
|
||||
*/
|
||||
_createClass(CursorPlugin, [{
|
||||
key: "init",
|
||||
value: function init() {
|
||||
this.wrapper = this.wavesurfer.container;
|
||||
this.cursor = this.wrapper.appendChild(this.style(document.createElement('cursor'), Object.assign({
|
||||
position: 'absolute',
|
||||
zIndex: this.params.zIndex,
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '0',
|
||||
display: 'flex',
|
||||
borderRightStyle: this.params.style,
|
||||
borderRightWidth: this.params.width,
|
||||
borderRightColor: this.params.color,
|
||||
opacity: this.params.opacity,
|
||||
pointerEvents: 'none'
|
||||
}, this.params.customStyle)));
|
||||
if (this.params.showTime) {
|
||||
this.showTime = this.wrapper.appendChild(this.style(document.createElement('showTitle'), Object.assign({
|
||||
position: 'absolute',
|
||||
zIndex: this.params.zIndex,
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: 'auto',
|
||||
display: 'flex',
|
||||
opacity: this.params.opacity,
|
||||
pointerEvents: 'none',
|
||||
height: '100%'
|
||||
}, this.params.customStyle)));
|
||||
this.displayTime = this.showTime.appendChild(this.style(document.createElement('div'), Object.assign({
|
||||
display: 'inline',
|
||||
pointerEvents: 'none',
|
||||
margin: 'auto',
|
||||
visibility: 'hidden' // initial value will be hidden just for measuring purpose
|
||||
}, this.params.customShowTimeStyle)));
|
||||
// initial value to measure display width
|
||||
this.displayTime.innerHTML = this.formatTime(0);
|
||||
}
|
||||
this.wrapper.addEventListener('mousemove', this._onMousemove);
|
||||
if (this.params.hideOnBlur) {
|
||||
// ensure elements are hidden initially
|
||||
this.hideCursor();
|
||||
this.wrapper.addEventListener('mouseenter', this._onMouseenter);
|
||||
this.wrapper.addEventListener('mouseleave', this._onMouseleave);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the plugin (used by the Plugin API)
|
||||
*/
|
||||
}, {
|
||||
key: "destroy",
|
||||
value: function destroy() {
|
||||
if (this.params.showTime) {
|
||||
this.cursor.parentNode.removeChild(this.showTime);
|
||||
}
|
||||
this.cursor.parentNode.removeChild(this.cursor);
|
||||
this.wrapper.removeEventListener('mousemove', this._onMousemove);
|
||||
if (this.params.hideOnBlur) {
|
||||
this.wrapper.removeEventListener('mouseenter', this._onMouseenter);
|
||||
this.wrapper.removeEventListener('mouseleave', this._onMouseleave);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cursor position
|
||||
*
|
||||
* @param {number} xpos The x offset of the cursor in pixels
|
||||
* @param {number} ypos The y offset of the cursor in pixels
|
||||
* @param {boolean} flip Flag to flip duration text from right to left
|
||||
*/
|
||||
}, {
|
||||
key: "updateCursorPosition",
|
||||
value: function updateCursorPosition(xpos, ypos) {
|
||||
var flip = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
||||
this.style(this.cursor, {
|
||||
left: "".concat(xpos, "px")
|
||||
});
|
||||
if (this.params.showTime) {
|
||||
var duration = this.wavesurfer.getDuration();
|
||||
var elementWidth = this.wavesurfer.drawer.width / this.wavesurfer.params.pixelRatio;
|
||||
var scrollWidth = this.wavesurfer.drawer.getScrollX();
|
||||
var scrollTime = duration / this.wavesurfer.drawer.width * scrollWidth;
|
||||
var timeValue = Math.max(0, xpos / elementWidth * duration) + scrollTime;
|
||||
var formatValue = this.formatTime(timeValue);
|
||||
if (flip) {
|
||||
var textOffset = this.outerWidth(this.displayTime);
|
||||
xpos -= textOffset;
|
||||
}
|
||||
this.style(this.showTime, {
|
||||
left: "".concat(xpos, "px"),
|
||||
top: "".concat(ypos, "px")
|
||||
});
|
||||
this.style(this.displayTime, {
|
||||
visibility: 'visible'
|
||||
});
|
||||
this.displayTime.innerHTML = "".concat(formatValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the cursor
|
||||
*/
|
||||
}, {
|
||||
key: "showCursor",
|
||||
value: function showCursor() {
|
||||
this.style(this.cursor, {
|
||||
display: 'flex'
|
||||
});
|
||||
if (this.params.showTime) {
|
||||
this.style(this.showTime, {
|
||||
display: 'flex'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the cursor
|
||||
*/
|
||||
}, {
|
||||
key: "hideCursor",
|
||||
value: function hideCursor() {
|
||||
this.style(this.cursor, {
|
||||
display: 'none'
|
||||
});
|
||||
if (this.params.showTime) {
|
||||
this.style(this.showTime, {
|
||||
display: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the timestamp for `cursorTime`.
|
||||
*
|
||||
* @param {number} cursorTime Time in seconds
|
||||
* @returns {string} Formatted timestamp
|
||||
*/
|
||||
}, {
|
||||
key: "formatTime",
|
||||
value: function formatTime(cursorTime) {
|
||||
cursorTime = isNaN(cursorTime) ? 0 : cursorTime;
|
||||
if (this.params.formatTimeCallback) {
|
||||
return this.params.formatTimeCallback(cursorTime);
|
||||
}
|
||||
return [cursorTime].map(function (time) {
|
||||
return [Math.floor(time % 3600 / 60),
|
||||
// minutes
|
||||
('00' + Math.floor(time % 60)).slice(-2),
|
||||
// seconds
|
||||
('000' + Math.floor(time % 1 * 1000)).slice(-3) // milliseconds
|
||||
].join(':');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get outer width of given element.
|
||||
*
|
||||
* @param {DOM} element DOM Element
|
||||
* @returns {number} outer width
|
||||
*/
|
||||
}, {
|
||||
key: "outerWidth",
|
||||
value: function outerWidth(element) {
|
||||
if (!element) return 0;
|
||||
var width = element.offsetWidth;
|
||||
var style = getComputedStyle(element);
|
||||
width += parseInt(style.marginLeft + style.marginRight);
|
||||
return width;
|
||||
}
|
||||
}], [{
|
||||
key: "create",
|
||||
value:
|
||||
/**
|
||||
* Cursor plugin definition factory
|
||||
*
|
||||
* This function must be used to create a plugin definition which can be
|
||||
* used by wavesurfer to correctly instantiate the plugin.
|
||||
*
|
||||
* @param {CursorPluginParams} params parameters use to initialise the
|
||||
* plugin
|
||||
* @return {PluginDefinition} an object representing the plugin
|
||||
*/
|
||||
function create(params) {
|
||||
return {
|
||||
name: 'cursor',
|
||||
deferInit: params && params.deferInit ? params.deferInit : false,
|
||||
params: params,
|
||||
staticProps: {},
|
||||
instance: CursorPlugin
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {CursorPluginParams}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {object} e Mouse move event
|
||||
*/
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
}]);
|
||||
return CursorPlugin;
|
||||
}();
|
||||
module.exports = exports.default;
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/
|
||||
/******/ // startup
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ // This entry module is referenced by other modules so it can't be inlined
|
||||
/******/ var __webpack_exports__ = __webpack_require__("./src/plugin/cursor/index.js");
|
||||
/******/
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
//# sourceMappingURL=wavesurfer.cursor.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,627 @@
|
|||
'use strict';
|
||||
|
||||
var wavesurfer = [];
|
||||
|
||||
window.addEventListener("load",()=>{
|
||||
let op=window.inputKnobsOptions||{};
|
||||
op.knobWidth=op.knobWidth||op.knobDiameter||64;
|
||||
op.knobHeight=op.knobHeight||op.knobDiameter||64;
|
||||
op.sliderWidth=op.sliderWidth||op.sliderDiameter||128;
|
||||
op.sliderHeight=op.sliderHeight||op.sliderDiameter||20;
|
||||
op.switchWidth=op.switchWidth||op.switchDiameter||24;
|
||||
op.switchHeight=op.switchHeight||op.switchDiameter||24;
|
||||
op.fgcolor=op.fgcolor||"#f00";
|
||||
op.bgcolor=op.bgcolor||"#000";
|
||||
op.knobMode=op.knobMode||"linear";
|
||||
op.sliderMode=op.sliderMode||"relative";
|
||||
let styles=document.createElement("style");
|
||||
styles.innerHTML=
|
||||
`input[type=range].input-knob,input[type=range].input-slider{
|
||||
-webkit-appearance:none;
|
||||
-moz-appearance:none;
|
||||
border:none;
|
||||
box-sizing:border-box;
|
||||
overflow:hidden;
|
||||
background-repeat:no-repeat;
|
||||
background-size:100% 100%;
|
||||
background-position:0px 0%;
|
||||
background-color:transparent;
|
||||
touch-action:none;
|
||||
}
|
||||
input[type=range].input-knob{
|
||||
width:${op.knobWidth}px; height:${op.knobHeight}px;
|
||||
}
|
||||
input[type=range].input-slider{
|
||||
width:${op.sliderWidth}px; height:${op.sliderHeight}px;
|
||||
}
|
||||
input[type=range].input-knob::-webkit-slider-thumb,input[type=range].input-slider::-webkit-slider-thumb{
|
||||
-webkit-appearance:none;
|
||||
opacity:0;
|
||||
}
|
||||
input[type=range].input-knob::-moz-range-thumb,input[type=range].input-slider::-moz-range-thumb{
|
||||
-moz-appearance:none;
|
||||
height:0;
|
||||
border:none;
|
||||
}
|
||||
input[type=range].input-knob::-moz-range-track,input[type=range].input-slider::-moz-range-track{
|
||||
-moz-appearance:none;
|
||||
height:0;
|
||||
border:none;
|
||||
}
|
||||
input[type=checkbox].input-switch,input[type=radio].input-switch {
|
||||
width:${op.switchWidth}px;
|
||||
height:${op.switchHeight}px;
|
||||
-webkit-appearance:none;
|
||||
-moz-appearance:none;
|
||||
background-size:100% 200%;
|
||||
background-position:0% 0%;
|
||||
background-repeat:no-repeat;
|
||||
border:none;
|
||||
border-radius:0;
|
||||
background-color:transparent;
|
||||
}
|
||||
input[type=checkbox].input-switch:checked,input[type=radio].input-switch:checked {
|
||||
background-position:0% 100%;
|
||||
}`;
|
||||
document.head.appendChild(styles);
|
||||
let makeKnobFrames=(fr,fg,bg)=>{
|
||||
let r=
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="${fr*64}" viewBox="0 0 64 ${fr*64}" preserveAspectRatio="none">
|
||||
<defs><g id="K"><circle cx="32" cy="32" r="30" fill="${bg}"/>
|
||||
<line x1="32" y1="28" x2="32" y2="7" stroke-linecap="round" stroke-width="6" stroke="${fg}"/></g></defs>
|
||||
<use xlink:href="#K" transform="rotate(-135,32,32)"/>`;
|
||||
for(let i=1;i<fr;++i)
|
||||
r+=`<use xlink:href="#K" transform="translate(0,${64*i}) rotate(${-135+270*i/fr},32,32)"/>`;
|
||||
return r+"</svg>";
|
||||
}
|
||||
let makeHSliderFrames=(fr,fg,bg,w,h)=>{
|
||||
let r=
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${w}" height="${fr*h}" viewBox="0 0 ${w} ${fr*h}" preserveAspectRatio="none">
|
||||
<defs><g id="B"><rect x="0" y="0" width="${w}" height="${h}" rx="${h/2}" ry="${h/2}" fill="${bg}"/></g>
|
||||
<g id="K"><circle x="${w/2}" y="0" r="${h/2*0.9}" fill="${fg}"/></g></defs>`;
|
||||
for(let i=0;i<fr;++i){
|
||||
r+=`<use xlink:href="#B" transform="translate(0,${h*i})"/>`;
|
||||
r+=`<use xlink:href="#K" transform="translate(${h/2+(w-h)*i/100},${h/2+h*i})"/>`;
|
||||
}
|
||||
return r+"</svg>";
|
||||
}
|
||||
let makeVSliderFrames=(fr,fg,bg,w,h)=>{
|
||||
let r=
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${w}" height="${fr*h}" viewBox="0 0 ${w} ${fr*h}" preserveAspectRatio="none">
|
||||
<defs><rect id="B" x="0" y="0" width="${w}" height="${h}" rx="${w/2}" ry="${w/2}" fill="${bg}"/>
|
||||
<circle id="K" x="0" y="0" r="${w/2*0.9}" fill="${fg}"/></defs>`;
|
||||
for(let i=0;i<fr;++i){
|
||||
r+=`<use xlink:href="#B" transform="translate(0,${h*i})"/>`;
|
||||
r+=`<use xlink:href="#K" transform="translate(${w/2} ${h*(i+1)-w/2-i*(h-w)/100})"/>`;
|
||||
}
|
||||
return r+"</svg>";
|
||||
}
|
||||
let initSwitches=(el)=>{
|
||||
let w,h,d,fg,bg;
|
||||
if(el.inputKnobs)
|
||||
return;
|
||||
el.inputKnobs={};
|
||||
el.refresh=()=>{
|
||||
let src=el.getAttribute("data-src");
|
||||
d=+el.getAttribute("data-diameter");
|
||||
let st=document.defaultView.getComputedStyle(el,null);
|
||||
w=parseFloat(el.getAttribute("data-width")||d||st.width);
|
||||
h=parseFloat(el.getAttribute("data-height")||d||st.height);
|
||||
bg=el.getAttribute("data-bgcolor")||op.bgcolor;
|
||||
fg=el.getAttribute("data-fgcolor")||op.fgcolor;
|
||||
el.style.width=w+"px";
|
||||
el.style.height=h+"px";
|
||||
if(src)
|
||||
el.style.backgroundImage="url("+src+")";
|
||||
else {
|
||||
let minwh=Math.min(w,h);
|
||||
let svg=
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h*2}" viewBox="0 0 ${w} ${h*2}" preserveAspectRatio="none">
|
||||
<g><rect fill="${bg}" x="1" y="1" width="${w-2}" height="${h-2}" rx="${minwh*0.25}" ry="${minwh*0.25}"/>
|
||||
<rect fill="${bg}" x="1" y="${h+1}" width="${w-2}" height="${h-2}" rx="${minwh*0.25}" ry="${minwh*0.25}"/>
|
||||
<circle fill="${fg}" cx="${w*0.5}" cy="${h*1.5}" r="${minwh*0.25}"/></g></svg>`;
|
||||
el.style.backgroundImage="url(data:image/svg+xml;base64,"+btoa(svg)+")";
|
||||
}
|
||||
};
|
||||
el.refresh();
|
||||
};
|
||||
let initKnobs=(el)=>{
|
||||
let w,h,d,fg,bg;
|
||||
if(el.inputKnobs){
|
||||
el.redraw();
|
||||
return;
|
||||
}
|
||||
let ik=el.inputKnobs={};
|
||||
el.refresh=()=>{
|
||||
d=+el.getAttribute("data-diameter");
|
||||
let st=document.defaultView.getComputedStyle(el,null);
|
||||
w=parseFloat(el.getAttribute("data-width")||d||st.width);
|
||||
h=parseFloat(el.getAttribute("data-height")||d||st.height);
|
||||
bg=el.getAttribute("data-bgcolor")||op.bgcolor;
|
||||
fg=el.getAttribute("data-fgcolor")||op.fgcolor;
|
||||
ik.sensex=ik.sensey=200;
|
||||
if(el.className.indexOf("input-knob")>=0)
|
||||
ik.itype="k";
|
||||
else{
|
||||
if(w>=h){
|
||||
ik.itype="h";
|
||||
ik.sensex=w-h;
|
||||
ik.sensey=Infinity;
|
||||
el.style.backgroundSize="auto 100%";
|
||||
}
|
||||
else{
|
||||
ik.itype="v";
|
||||
ik.sensex="Infinity";
|
||||
ik.sensey=h-w;
|
||||
el.style.backgroundSize="100% auto";
|
||||
}
|
||||
}
|
||||
el.style.width=w+"px";
|
||||
el.style.height=h+"px";
|
||||
ik.frameheight=h;
|
||||
let src=el.getAttribute("data-src");
|
||||
if(src){
|
||||
el.style.backgroundImage=`url(${src})`;
|
||||
let sp=+el.getAttribute("data-sprites");
|
||||
if(sp)
|
||||
ik.sprites=sp;
|
||||
else
|
||||
ik.sprites=0;
|
||||
if(ik.sprites>=1)
|
||||
el.style.backgroundSize=`100% ${(ik.sprites+1)*100}%`;
|
||||
else if(ik.itype!="k"){
|
||||
el.style.backgroundColor=bg;
|
||||
el.style.borderRadius=Math.min(w,h)*0.25+"px";
|
||||
}
|
||||
}
|
||||
else{
|
||||
let svg;
|
||||
switch(ik.itype){
|
||||
case "k": svg=makeKnobFrames(101,fg,bg); break;
|
||||
case "h": svg=makeHSliderFrames(101,fg,bg,w,h); break;
|
||||
case "v": svg=makeVSliderFrames(101,fg,bg,w,h); break;
|
||||
}
|
||||
ik.sprites=100;
|
||||
el.style.backgroundImage="url(data:image/svg+xml;base64,"+btoa(svg)+")";
|
||||
el.style.backgroundSize=`100% ${(ik.sprites+1)*100}%`;
|
||||
}
|
||||
ik.valrange={min:+el.min, max:(el.max=="")?100:+el.max, step:(el.step=="")?1:+el.step};
|
||||
el.redraw(true);
|
||||
};
|
||||
el.setValue=(v)=>{
|
||||
v=(Math.round((v-ik.valrange.min)/ik.valrange.step))*ik.valrange.step+ik.valrange.min;
|
||||
if(v<ik.valrange.min) v=ik.valrange.min;
|
||||
if(v>ik.valrange.max) v=ik.valrange.max;
|
||||
el.value=v;
|
||||
if(el.value!=ik.oldvalue){
|
||||
el.setAttribute("value",el.value);
|
||||
el.redraw();
|
||||
let event=document.createEvent("HTMLEvents");
|
||||
event.initEvent("input",false,true);
|
||||
el.dispatchEvent(event);
|
||||
ik.oldvalue=el.value;
|
||||
}
|
||||
};
|
||||
ik.pointerdown=(ev)=>{
|
||||
el.focus();
|
||||
if(ev.touches)
|
||||
ev = ev.touches[0];
|
||||
let rc=el.getBoundingClientRect();
|
||||
let cx=(rc.left+rc.right)*0.5,cy=(rc.top+rc.bottom)*0.5;
|
||||
let dx=ev.clientX,dy=ev.clientY;
|
||||
let da=Math.atan2(ev.clientX-cx,cy-ev.clientY);
|
||||
if(ik.itype=="k"&&op.knobMode=="circularabs"){
|
||||
dv=ik.valrange.min+(da/Math.PI*0.75+0.5)*(ik.valrange.max-ik.valrange.min);
|
||||
el.setValue(dv);
|
||||
}
|
||||
if(ik.itype!="k"&&op.sliderMode=="abs"){
|
||||
dv=(ik.valrange.min+ik.valrange.max)*0.5+((dx-cx)/ik.sensex-(dy-cy)/ik.sensey)*(ik.valrange.max-ik.valrange.min);
|
||||
el.setValue(dv);
|
||||
}
|
||||
ik.dragfrom={x:ev.clientX,y:ev.clientY,a:Math.atan2(ev.clientX-cx,cy-ev.clientY),v:+el.value};
|
||||
document.addEventListener("mousemove",ik.pointermove);
|
||||
document.addEventListener("mouseup",ik.pointerup);
|
||||
document.addEventListener("touchmove",ik.pointermove);
|
||||
document.addEventListener("touchend",ik.pointerup);
|
||||
document.addEventListener("touchcancel",ik.pointerup);
|
||||
document.addEventListener("touchstart",ik.preventScroll);
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
};
|
||||
ik.pointermove=(ev)=>{
|
||||
let dv;
|
||||
let rc=el.getBoundingClientRect();
|
||||
let cx=(rc.left+rc.right)*0.5,cy=(rc.top+rc.bottom)*0.5;
|
||||
if(ev.touches)
|
||||
ev = ev.touches[0];
|
||||
let dx=ev.clientX-ik.dragfrom.x,dy=ev.clientY-ik.dragfrom.y;
|
||||
let da=Math.atan2(ev.clientX-cx,cy-ev.clientY);
|
||||
switch(ik.itype){
|
||||
case "k":
|
||||
switch(op.knobMode){
|
||||
case "linear":
|
||||
dv=(dx/ik.sensex-dy/ik.sensey)*(ik.valrange.max-ik.valrange.min);
|
||||
if(ev.shiftKey)
|
||||
dv*=0.2;
|
||||
el.setValue(ik.dragfrom.v+dv);
|
||||
break;
|
||||
case "circularabs":
|
||||
if(!ev.shiftKey){
|
||||
dv=ik.valrange.min+(da/Math.PI*0.75+0.5)*(ik.valrange.max-ik.valrange.min);
|
||||
el.setValue(dv);
|
||||
break;
|
||||
}
|
||||
case "circularrel":
|
||||
if(da>ik.dragfrom.a+Math.PI) da-=Math.PI*2;
|
||||
if(da<ik.dragfrom.a-Math.PI) da+=Math.PI*2;
|
||||
da-=ik.dragfrom.a;
|
||||
dv=da/Math.PI/1.5*(ik.valrange.max-ik.valrange.min);
|
||||
if(ev.shiftKey)
|
||||
dv*=0.2;
|
||||
el.setValue(ik.dragfrom.v+dv);
|
||||
}
|
||||
break;
|
||||
case "h":
|
||||
case "v":
|
||||
dv=(dx/ik.sensex-dy/ik.sensey)*(ik.valrange.max-ik.valrange.min);
|
||||
if(ev.shiftKey)
|
||||
dv*=0.2;
|
||||
el.setValue(ik.dragfrom.v+dv);
|
||||
break;
|
||||
}
|
||||
};
|
||||
ik.pointerup=()=>{
|
||||
document.removeEventListener("mousemove",ik.pointermove);
|
||||
document.removeEventListener("touchmove",ik.pointermove);
|
||||
document.removeEventListener("mouseup",ik.pointerup);
|
||||
document.removeEventListener("touchend",ik.pointerup);
|
||||
document.removeEventListener("touchcancel",ik.pointerup);
|
||||
document.removeEventListener("touchstart",ik.preventScroll);
|
||||
let event=document.createEvent("HTMLEvents");
|
||||
event.initEvent("change",false,true);
|
||||
el.dispatchEvent(event);
|
||||
};
|
||||
ik.preventScroll=(ev)=>{
|
||||
ev.preventDefault();
|
||||
};
|
||||
ik.keydown=()=>{
|
||||
el.redraw();
|
||||
};
|
||||
ik.wheel=(ev)=>{
|
||||
let delta=ev.deltaY>0?-ik.valrange.step:ik.valrange.step;
|
||||
if(!ev.shiftKey)
|
||||
delta*=5;
|
||||
el.setValue(+el.value+delta);
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
};
|
||||
el.redraw=(f)=>{
|
||||
if(f||ik.valueold!=el.value){
|
||||
let v=(el.value-ik.valrange.min)/(ik.valrange.max-ik.valrange.min);
|
||||
if(ik.sprites>=1)
|
||||
el.style.backgroundPosition="0px "+(-((v*ik.sprites)|0)*ik.frameheight)+"px";
|
||||
else{
|
||||
switch(ik.itype){
|
||||
case "k":
|
||||
el.style.transform="rotate("+(270*v-135)+"deg)";
|
||||
break;
|
||||
case "h":
|
||||
el.style.backgroundPosition=((w-h)*v)+"px 0px";
|
||||
break;
|
||||
case "v":
|
||||
el.style.backgroundPosition="0px "+(h-w)*(1-v)+"px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
ik.valueold=el.value;
|
||||
}
|
||||
};
|
||||
el.refresh();
|
||||
el.redraw(true);
|
||||
el.addEventListener("keydown",ik.keydown);
|
||||
el.addEventListener("mousedown",ik.pointerdown);
|
||||
el.addEventListener("touchstart",ik.pointerdown);
|
||||
el.addEventListener("wheel",function (event){ event.stopPropagation(); }); //previous: el.addEventListener("wheel",ik.wheel); Maybe optional, can accidently move knob if allowed
|
||||
el.addEventListener("click",function (event){ event.stopPropagation(); });
|
||||
}
|
||||
let refreshque=()=>{
|
||||
let elem=document.querySelectorAll("input.input-knob,input.input-slider");
|
||||
for(let i=0;i<elem.length;++i)
|
||||
procque.push([initKnobs,elem[i]]);
|
||||
elem=document.querySelectorAll("input[type=checkbox].input-switch,input[type=radio].input-switch");
|
||||
for(let i=0;i<elem.length;++i){
|
||||
procque.push([initSwitches,elem[i]]);
|
||||
}
|
||||
}
|
||||
let procque=[];
|
||||
refreshque();
|
||||
setInterval(()=>{
|
||||
for(let i=0;procque.length>0&&i<8;++i){
|
||||
let q=procque.shift();
|
||||
q[0](q[1]);
|
||||
}
|
||||
if(procque.length<=0)
|
||||
refreshque();
|
||||
},50);
|
||||
});
|
||||
|
||||
// Used for converting back to HH-MM-SS, Cuein and Cueout
|
||||
function toHHMMSS(secs) {
|
||||
let totalSeconds = secs;
|
||||
let hours = Math.floor(totalSeconds / 3600);
|
||||
totalSeconds %= 3600;
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
let seconds = totalSeconds % 60;
|
||||
|
||||
// If you want strings with leading zeroes:
|
||||
minutes = String(minutes).padStart(2, "0");
|
||||
hours = String(hours).padStart(2, "0");
|
||||
seconds = String(seconds).padStart(2, "0");
|
||||
return(hours + ":" + minutes + ":" + seconds.slice(0, 6));
|
||||
}
|
||||
|
||||
//outputs gain percentage from decibels
|
||||
function deciNum(value){
|
||||
const decibels = Math.exp(value / 8.6858);
|
||||
return 50 * decibels;
|
||||
}
|
||||
|
||||
//outputs decibel from gain
|
||||
function gainNum(perc, minDb, maxDb, minP, maxP){
|
||||
const getPerc = (db) => (Math.round(1000000000 * Math.pow(10, db / 20)) / 10000000);
|
||||
const maxPerc = maxP || (_.isNumber(maxDb) && getPerc(maxDb)) || 100;
|
||||
const minPerc = minP || (_.isNumber(minDb) && getPerc(minDb)) || 0;
|
||||
const range = maxPerc - minPerc;
|
||||
const newPerc = (range / 50) * perc;
|
||||
const total = Math.round(1000000000 * 20 * (Math.log(newPerc * 0.01) / Math.log(10))) / 1000000000;
|
||||
return total.toFixed(2);
|
||||
}
|
||||
|
||||
//outputs steps from decibels
|
||||
function deciSteps(value){
|
||||
//convert decibel to gain/percent
|
||||
const percent = deciNum(value);
|
||||
var result = percent / 100;
|
||||
return result;
|
||||
}
|
||||
|
||||
function formatTimeCallback(seconds, pxPerSec) {
|
||||
|
||||
seconds = Number(seconds);
|
||||
|
||||
//alert(seconds);
|
||||
var minutes = Math.floor(seconds / 60);
|
||||
seconds = seconds % 60;
|
||||
|
||||
// fill up seconds with zeroes
|
||||
var secondsStr = Math.round(seconds).toString();
|
||||
if (pxPerSec >= 25 * 10) {
|
||||
secondsStr = seconds.toFixed(2);
|
||||
} else if (pxPerSec >= 25 * 1) {
|
||||
secondsStr = seconds.toFixed(1);
|
||||
}
|
||||
|
||||
if (minutes > 0) {
|
||||
if (seconds < 10) {
|
||||
secondsStr = '0' + secondsStr;
|
||||
}
|
||||
return `${minutes}:${secondsStr}`;
|
||||
}
|
||||
|
||||
return secondsStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use timeInterval to set the period between notches, in seconds,
|
||||
* adding notches as the number of pixels per second increases.
|
||||
*
|
||||
* Note that if you override the default function, you'll almost
|
||||
* certainly want to override formatTimeCallback, primaryLabelInterval
|
||||
* and/or secondaryLabelInterval so they all work together.
|
||||
*
|
||||
* @param: pxPerSec
|
||||
*/
|
||||
function timeInterval(pxPerSec) {
|
||||
var retval = 1;
|
||||
if (pxPerSec >= 25 * 100) {
|
||||
retval = 0.01;
|
||||
} else if (pxPerSec >= 25 * 40) {
|
||||
retval = 0.025;
|
||||
} else if (pxPerSec >= 25 * 10) {
|
||||
retval = 0.1;
|
||||
} else if (pxPerSec >= 25 * 4) {
|
||||
retval = 0.25;
|
||||
} else if (pxPerSec >= 25) {
|
||||
retval = 1;
|
||||
} else if (pxPerSec * 5 >= 25) {
|
||||
retval = 5;
|
||||
} else if (pxPerSec * 15 >= 25) {
|
||||
retval = 15;
|
||||
} else {
|
||||
retval = Math.ceil(0.5 / pxPerSec) * 60;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cadence of notches that get labels in the primary color.
|
||||
* EG, return 2 if every 2nd notch should be labeled,
|
||||
* return 10 if every 10th notch should be labeled, etc.
|
||||
*
|
||||
* Note that if you override the default function, you'll almost
|
||||
* certainly want to override formatTimeCallback, primaryLabelInterval
|
||||
* and/or secondaryLabelInterval so they all work together.
|
||||
*
|
||||
* @param pxPerSec
|
||||
*/
|
||||
function primaryLabelInterval(pxPerSec) {
|
||||
var retval = 1;
|
||||
if (pxPerSec >= 25 * 100) {
|
||||
retval = 10;
|
||||
} else if (pxPerSec >= 25 * 40) {
|
||||
retval = 4;
|
||||
} else if (pxPerSec >= 25 * 10) {
|
||||
retval = 10;
|
||||
} else if (pxPerSec >= 25 * 4) {
|
||||
retval = 4;
|
||||
} else if (pxPerSec >= 25) {
|
||||
retval = 1;
|
||||
} else if (pxPerSec * 5 >= 25) {
|
||||
retval = 5;
|
||||
} else if (pxPerSec * 15 >= 25) {
|
||||
retval = 15;
|
||||
} else {
|
||||
retval = Math.ceil(0.5 / pxPerSec) * 60;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cadence of notches to get labels in the secondary color.
|
||||
* EG, return 2 if every 2nd notch should be labeled,
|
||||
* return 10 if every 10th notch should be labeled, etc.
|
||||
*
|
||||
* Secondary labels are drawn after primary labels, so if
|
||||
* you want to have labels every 10 seconds and another color labels
|
||||
* every 60 seconds, the 60 second labels should be the secondaries.
|
||||
*
|
||||
* Note that if you override the default function, you'll almost
|
||||
* certainly want to override formatTimeCallback, primaryLabelInterval
|
||||
* and/or secondaryLabelInterval so they all work together.
|
||||
*
|
||||
* @param pxPerSec
|
||||
*/
|
||||
function secondaryLabelInterval(pxPerSec) {
|
||||
// draw one every 10s as an example
|
||||
return Math.floor(10 / timeInterval(pxPerSec));
|
||||
}
|
||||
|
||||
|
||||
function renderWaveform(track_id, selector_id, url, cuein, cueout, gain_level, default_gain) {
|
||||
|
||||
var trackid = "t"+track_id;
|
||||
var a = cuein.split(':'); // split it at the colons
|
||||
var b = cueout.split(':'); // split it at the colons
|
||||
var startseconds = (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+a[2]);
|
||||
var endseconds = (+b[0]) * 60 * 60 + (+b[1]) * 60 + (+b[2]);
|
||||
|
||||
$('#track-playedit-'+track_id).attr('onClick', 'wavesurfer["'+ trackid +'"].play('+ startseconds +', '+endseconds+');');
|
||||
|
||||
wavesurfer[trackid] = WaveSurfer.create({
|
||||
container: document.querySelector(selector_id),
|
||||
waveColor: 'hsla(0, 0%, 50%, 0.8)',
|
||||
backgroundColor: '#333',
|
||||
progressColor: 'hsla(0, 0%, 0%, 0)',
|
||||
backend: 'MediaElementWebAudio',
|
||||
height: 70,
|
||||
cursorColor: '#ccc',
|
||||
cursorWidth: 1,
|
||||
responsive: true,
|
||||
scrollParent: true,
|
||||
minimap: true,
|
||||
autoCenter: true,
|
||||
splitChannels: true,
|
||||
plugins: [
|
||||
WaveSurfer.regions.create({
|
||||
regions: [
|
||||
{
|
||||
id: track_id,
|
||||
start: startseconds,
|
||||
end: endseconds,
|
||||
loop: false,
|
||||
drag: false,
|
||||
color: 'hsla(232, 64%, 50%, 0.2)',
|
||||
handleStyle: {
|
||||
left: {
|
||||
backgroundColor: '#00e640',
|
||||
width: '2px',
|
||||
},
|
||||
right: {
|
||||
backgroundColor: '#f22613',
|
||||
width: '2px',
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
}),
|
||||
WaveSurfer.timeline.create({
|
||||
container: "#timeline-"+ track_id,
|
||||
formatTimeCallback: formatTimeCallback,
|
||||
timeInterval: timeInterval,
|
||||
primaryLabelInterval: primaryLabelInterval,
|
||||
secondaryLabelInterval: secondaryLabelInterval,
|
||||
primaryColor: '#ff611f',
|
||||
secondaryColor: '#cccccc',
|
||||
primaryFontColor: '#ff611f',
|
||||
secondaryFontColor: '#cccccc'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
var eTrack = wavesurfer[trackid];
|
||||
eTrack.load(url);
|
||||
|
||||
// Zoom slider
|
||||
var slider = document.querySelector('[data-action="zoom-'+track_id+'"]');
|
||||
slider.value = eTrack.params.minPxPerSec;
|
||||
slider.min = eTrack.params.minPxPerSec;
|
||||
slider.addEventListener('input', function() {
|
||||
eTrack.zoom(Number(this.value));
|
||||
});
|
||||
|
||||
document.getElementById('tracktimerinput-'+track_id).value='0.000';
|
||||
|
||||
eTrack.on('audioprocess', function (e) {
|
||||
document.getElementById('tracktimerinput-'+track_id).value=(eTrack.getCurrentTime().toFixed(3));
|
||||
});
|
||||
|
||||
var playPause = document.getElementById('track-play-'+track_id);
|
||||
eTrack.on('play', function () {
|
||||
//var playButton = document.getElementById('track-play-'+track_id);
|
||||
playPause.style.backgroundColor = "#32CD32";
|
||||
});
|
||||
eTrack.on('pause', function () {
|
||||
//var pauseButton = document.getElementById('track-play-'+track_id);
|
||||
playPause.style.backgroundColor = "#555555";
|
||||
});
|
||||
eTrack.on('error', function(e) {
|
||||
console.warn(e);
|
||||
});
|
||||
|
||||
function saveCue() {
|
||||
var region = eTrack.regions.list[track_id];
|
||||
document.getElementsByClassName("cuein_"+track_id)[0].value = toHHMMSS(region.start);
|
||||
document.getElementsByClassName("cueout_"+track_id)[0].value = toHHMMSS(region.end);
|
||||
$('#track-playedit-'+track_id).attr('onClick', 'wavesurfer["t'+ track_id +'"].play('+ region.start +', '+ region.end +');');
|
||||
|
||||
return {
|
||||
start: region.start,
|
||||
end: region.end
|
||||
};
|
||||
}
|
||||
eTrack.on('ready', () => {
|
||||
eTrack.on('region-update-end', saveCue);
|
||||
});
|
||||
|
||||
eTrack.on('region-in', function(e) {
|
||||
document.getElementById('tracktimerinput-'+track_id).style.color = "rgb(56, 232, 56)";
|
||||
});
|
||||
eTrack.on('region-out', function(e) {
|
||||
document.getElementById('tracktimerinput-'+track_id).style.color = "#ffffff";
|
||||
});
|
||||
|
||||
//Volume to Gain deciSteps(gainNum(gain_level))
|
||||
eTrack.setVolume(0.6);
|
||||
|
||||
document.querySelector("#volume-"+ track_id).value = eTrack.getVolume();
|
||||
|
||||
var volumeInput = document.querySelector("#volume-"+ track_id);
|
||||
|
||||
var onChangeVolume = function (e) {
|
||||
//eTrack.setVolume(e.target.value);
|
||||
};
|
||||
volumeInput.addEventListener('input', onChangeVolume);
|
||||
volumeInput.addEventListener('change', onChangeVolume);
|
||||
|
||||
return eTrack;
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
/*!
|
||||
* wavesurfer.js minimap plugin 4.6.0 (2024-02-05)
|
||||
* https://wavesurfer-js.org
|
||||
* @license BSD-3-Clause
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define("WaveSurfer", [], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["WaveSurfer"] = factory();
|
||||
else
|
||||
root["WaveSurfer"] = root["WaveSurfer"] || {}, root["WaveSurfer"]["minimap"] = factory();
|
||||
})(this, () => {
|
||||
return /******/ (() => { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ "./src/plugin/minimap/index.js":
|
||||
/*!*************************************!*\
|
||||
!*** ./src/plugin/minimap/index.js ***!
|
||||
\*************************************/
|
||||
/***/ ((module, exports) => {
|
||||
|
||||
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({
|
||||
value: true
|
||||
}));
|
||||
exports["default"] = void 0;
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
/*eslint no-console: ["error", { allow: ["warn"] }] */
|
||||
/**
|
||||
* @typedef {Object} MinimapPluginParams
|
||||
* @desc Extends the `WavesurferParams` wavesurfer was initialised with
|
||||
* @property {?string|HTMLElement} container CSS selector or HTML element where
|
||||
* the map should be rendered. By default it is simply appended
|
||||
* after the waveform.
|
||||
* @property {?boolean} deferInit Set to true to manually call
|
||||
* `initPlugin('minimap')`
|
||||
*/
|
||||
/**
|
||||
* Renders a smaller version waveform as a minimap of the main waveform.
|
||||
*
|
||||
* @implements {PluginClass}
|
||||
* @extends {Observer}
|
||||
* @example
|
||||
* // es6
|
||||
* import MinimapPlugin from 'wavesurfer.minimap.js';
|
||||
*
|
||||
* // commonjs
|
||||
* var MinimapPlugin = require('wavesurfer.minimap.js');
|
||||
*
|
||||
* // if you are using <script> tags
|
||||
* var MinimapPlugin = window.WaveSurfer.minimap;
|
||||
*
|
||||
* // ... initialising wavesurfer with the plugin
|
||||
* var wavesurfer = WaveSurfer.create({
|
||||
* // wavesurfer options ...
|
||||
* plugins: [
|
||||
* MinimapPlugin.create({
|
||||
* // plugin options ...
|
||||
* })
|
||||
* ]
|
||||
* });
|
||||
*/
|
||||
var MinimapPlugin = exports["default"] = /*#__PURE__*/function () {
|
||||
function MinimapPlugin(params, ws) {
|
||||
var _this = this;
|
||||
_classCallCheck(this, MinimapPlugin);
|
||||
this.params = Object.assign({}, ws.params, {
|
||||
showRegions: false,
|
||||
regionsPluginName: params.regionsPluginName || 'regions',
|
||||
showOverview: false,
|
||||
overviewBorderColor: 'green',
|
||||
overviewBorderSize: 2,
|
||||
// the container should be different
|
||||
container: false,
|
||||
height: Math.max(Math.round(ws.params.height / 4), 20)
|
||||
}, params, {
|
||||
scrollParent: false,
|
||||
fillParent: true
|
||||
});
|
||||
// if container is a selector, get the element
|
||||
if (typeof params.container === 'string') {
|
||||
var el = document.querySelector(params.container);
|
||||
if (!el) {
|
||||
console.warn("Wavesurfer minimap container ".concat(params.container, " was not found! The minimap will be automatically appended below the waveform."));
|
||||
}
|
||||
this.params.container = el;
|
||||
}
|
||||
// if no container is specified add a new element and insert it
|
||||
if (!params.container) {
|
||||
this.params.container = ws.util.style(document.createElement('minimap'), {
|
||||
display: 'block'
|
||||
});
|
||||
}
|
||||
this.drawer = new ws.Drawer(this.params.container, this.params);
|
||||
this.wavesurfer = ws;
|
||||
this.util = ws.util;
|
||||
/**
|
||||
* Minimap needs to listen for the `ready` and `waveform-ready` events
|
||||
* to work with the `MediaElement` backend. The moment the `ready` event
|
||||
* is called is different (and peaks would not load).
|
||||
*
|
||||
* @type {string}
|
||||
* @see https://github.com/katspaugh/wavesurfer.js/issues/736
|
||||
*/
|
||||
this.renderEvent = ws.params.backend === 'MediaElement' ? 'waveform-ready' : 'ready';
|
||||
this.overviewRegion = null;
|
||||
this.regionsPlugin = this.wavesurfer[this.params.regionsPluginName];
|
||||
this.drawer.createWrapper();
|
||||
this.createElements();
|
||||
var isInitialised = false;
|
||||
|
||||
// ws ready event listener
|
||||
this._onShouldRender = function () {
|
||||
// only bind the events in the first run
|
||||
if (!isInitialised) {
|
||||
_this.bindWavesurferEvents();
|
||||
_this.bindMinimapEvents();
|
||||
isInitialised = true;
|
||||
}
|
||||
// if there is no such element, append it to the container (below
|
||||
// the waveform)
|
||||
if (!document.body.contains(_this.params.container)) {
|
||||
ws.container.insertBefore(_this.params.container, null);
|
||||
}
|
||||
if (_this.regionsPlugin && _this.params.showRegions) {
|
||||
_this.regions();
|
||||
}
|
||||
_this.render();
|
||||
};
|
||||
this._onAudioprocess = function (currentTime) {
|
||||
_this.drawer.progress(_this.wavesurfer.backend.getPlayedPercents());
|
||||
};
|
||||
|
||||
// ws seek event listener
|
||||
this._onSeek = function () {
|
||||
return _this.drawer.progress(ws.backend.getPlayedPercents());
|
||||
};
|
||||
|
||||
// event listeners for the overview region
|
||||
this._onScroll = function (e) {
|
||||
if (!_this.draggingOverview) {
|
||||
_this.moveOverviewRegion(e.target.scrollLeft / _this.ratio);
|
||||
}
|
||||
};
|
||||
this._onMouseover = function (e) {
|
||||
if (_this.draggingOverview) {
|
||||
_this.draggingOverview = false;
|
||||
}
|
||||
};
|
||||
var prevWidth = 0;
|
||||
this._onResize = ws.util.debounce(function () {
|
||||
if (prevWidth != _this.drawer.wrapper.clientWidth) {
|
||||
prevWidth = _this.drawer.wrapper.clientWidth;
|
||||
_this.render();
|
||||
_this.drawer.progress(_this.wavesurfer.backend.getPlayedPercents());
|
||||
}
|
||||
});
|
||||
this._onZoom = function (e) {
|
||||
_this.render();
|
||||
};
|
||||
this.wavesurfer.on('zoom', this._onZoom);
|
||||
}
|
||||
_createClass(MinimapPlugin, [{
|
||||
key: "init",
|
||||
value: function init() {
|
||||
if (this.wavesurfer.isReady) {
|
||||
this._onShouldRender();
|
||||
}
|
||||
this.wavesurfer.on(this.renderEvent, this._onShouldRender);
|
||||
}
|
||||
}, {
|
||||
key: "destroy",
|
||||
value: function destroy() {
|
||||
window.removeEventListener('resize', this._onResize, true);
|
||||
window.removeEventListener('orientationchange', this._onResize, true);
|
||||
this.wavesurfer.drawer.wrapper.removeEventListener('mouseover', this._onMouseover);
|
||||
this.wavesurfer.un(this.renderEvent, this._onShouldRender);
|
||||
this.wavesurfer.un('seek', this._onSeek);
|
||||
this.wavesurfer.un('scroll', this._onScroll);
|
||||
this.wavesurfer.un('audioprocess', this._onAudioprocess);
|
||||
this.wavesurfer.un('zoom', this._onZoom);
|
||||
this.drawer.destroy();
|
||||
this.overviewRegion = null;
|
||||
this.unAll();
|
||||
}
|
||||
}, {
|
||||
key: "regions",
|
||||
value: function regions() {
|
||||
var _this2 = this;
|
||||
this.regions = {};
|
||||
this.wavesurfer.on('region-created', function (region) {
|
||||
_this2.regions[region.id] = region;
|
||||
_this2.drawer.wrapper && _this2.renderRegions();
|
||||
});
|
||||
this.wavesurfer.on('region-updated', function (region) {
|
||||
_this2.regions[region.id] = region;
|
||||
_this2.drawer.wrapper && _this2.renderRegions();
|
||||
});
|
||||
this.wavesurfer.on('region-removed', function (region) {
|
||||
delete _this2.regions[region.id];
|
||||
_this2.drawer.wrapper && _this2.renderRegions();
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "renderRegions",
|
||||
value: function renderRegions() {
|
||||
var _this3 = this;
|
||||
var regionElements = this.drawer.wrapper.querySelectorAll('region');
|
||||
var i;
|
||||
for (i = 0; i < regionElements.length; ++i) {
|
||||
this.drawer.wrapper.removeChild(regionElements[i]);
|
||||
}
|
||||
Object.keys(this.regions).forEach(function (id) {
|
||||
var region = _this3.regions[id];
|
||||
var width = _this3.getWidth() * ((region.end - region.start) / _this3.wavesurfer.getDuration());
|
||||
var left = _this3.getWidth() * (region.start / _this3.wavesurfer.getDuration());
|
||||
var regionElement = _this3.util.style(document.createElement('region'), {
|
||||
height: 'inherit',
|
||||
backgroundColor: region.color,
|
||||
width: width + 'px',
|
||||
left: left + 'px',
|
||||
display: 'block',
|
||||
position: 'absolute'
|
||||
});
|
||||
regionElement.classList.add(id);
|
||||
_this3.drawer.wrapper.appendChild(regionElement);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: "createElements",
|
||||
value: function createElements() {
|
||||
this.drawer.createElements();
|
||||
if (this.params.showOverview) {
|
||||
this.overviewRegion = this.util.style(document.createElement('overview'), {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '0px',
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
cursor: 'move',
|
||||
border: this.params.overviewBorderSize + 'px solid ' + this.params.overviewBorderColor,
|
||||
zIndex: 2,
|
||||
opacity: this.params.overviewOpacity
|
||||
});
|
||||
this.drawer.wrapper.appendChild(this.overviewRegion);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "bindWavesurferEvents",
|
||||
value: function bindWavesurferEvents() {
|
||||
window.addEventListener('resize', this._onResize, true);
|
||||
window.addEventListener('orientationchange', this._onResize, true);
|
||||
this.wavesurfer.on('audioprocess', this._onAudioprocess);
|
||||
this.wavesurfer.on('seek', this._onSeek);
|
||||
if (this.params.showOverview) {
|
||||
this.wavesurfer.on('scroll', this._onScroll);
|
||||
this.wavesurfer.drawer.wrapper.addEventListener('mouseover', this._onMouseover);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "bindMinimapEvents",
|
||||
value: function bindMinimapEvents() {
|
||||
var _this4 = this;
|
||||
var positionMouseDown = {
|
||||
clientX: 0,
|
||||
clientY: 0
|
||||
};
|
||||
var relativePositionX = 0;
|
||||
var seek = true;
|
||||
|
||||
// the following event listeners will be destroyed by using
|
||||
// this.unAll() and nullifying the DOM node references after
|
||||
// removing them
|
||||
if (this.params.interact) {
|
||||
this.drawer.wrapper.addEventListener('click', function (event) {
|
||||
_this4.fireEvent('click', event, _this4.drawer.handleEvent(event));
|
||||
});
|
||||
this.on('click', function (event, position) {
|
||||
if (seek) {
|
||||
_this4.drawer.progress(position);
|
||||
_this4.wavesurfer.seekAndCenter(position);
|
||||
} else {
|
||||
seek = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.params.showOverview) {
|
||||
this.overviewRegion.addEventListener('mousedown', function (event) {
|
||||
_this4.draggingOverview = true;
|
||||
relativePositionX = event.layerX;
|
||||
positionMouseDown.clientX = event.clientX;
|
||||
positionMouseDown.clientY = event.clientY;
|
||||
});
|
||||
this.drawer.wrapper.addEventListener('mousemove', function (event) {
|
||||
if (_this4.draggingOverview) {
|
||||
_this4.moveOverviewRegion(event.clientX - _this4.drawer.container.getBoundingClientRect().left - relativePositionX);
|
||||
}
|
||||
});
|
||||
this.drawer.wrapper.addEventListener('mouseup', function (event) {
|
||||
if (positionMouseDown.clientX - event.clientX === 0 && positionMouseDown.clientX - event.clientX === 0) {
|
||||
seek = true;
|
||||
_this4.draggingOverview = false;
|
||||
} else if (_this4.draggingOverview) {
|
||||
seek = false;
|
||||
_this4.draggingOverview = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "render",
|
||||
value: function render() {
|
||||
var len = this.drawer.getWidth();
|
||||
var peaks = this.wavesurfer.backend.getPeaks(len, 0, len);
|
||||
this.drawer.drawPeaks(peaks, len, 0, len);
|
||||
this.drawer.progress(this.wavesurfer.backend.getPlayedPercents());
|
||||
if (this.params.showOverview) {
|
||||
//get proportional width of overview region considering the respective
|
||||
//width of the drawers
|
||||
this.ratio = this.wavesurfer.drawer.width / this.drawer.width;
|
||||
this.waveShowedWidth = this.wavesurfer.drawer.width / this.ratio;
|
||||
this.waveWidth = this.wavesurfer.drawer.width;
|
||||
this.overviewWidth = this.drawer.container.offsetWidth / this.ratio;
|
||||
this.overviewPosition = 0;
|
||||
this.moveOverviewRegion(this.wavesurfer.drawer.wrapper.scrollLeft / this.ratio);
|
||||
this.overviewRegion.style.width = this.overviewWidth + 'px';
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "moveOverviewRegion",
|
||||
value: function moveOverviewRegion(pixels) {
|
||||
if (pixels < 0) {
|
||||
this.overviewPosition = 0;
|
||||
} else if (pixels + this.overviewWidth < this.drawer.container.offsetWidth) {
|
||||
this.overviewPosition = pixels;
|
||||
} else {
|
||||
this.overviewPosition = this.drawer.container.offsetWidth - this.overviewWidth;
|
||||
}
|
||||
this.overviewRegion.style.left = this.overviewPosition + 'px';
|
||||
if (this.draggingOverview) {
|
||||
this.wavesurfer.drawer.wrapper.scrollLeft = this.overviewPosition * this.ratio;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: "getWidth",
|
||||
value: function getWidth() {
|
||||
return this.drawer.width / this.params.pixelRatio;
|
||||
}
|
||||
}], [{
|
||||
key: "create",
|
||||
value:
|
||||
/**
|
||||
* Minimap plugin definition factory
|
||||
*
|
||||
* This function must be used to create a plugin definition which can be
|
||||
* used by wavesurfer to correctly instantiate the plugin.
|
||||
*
|
||||
* @param {MinimapPluginParams} params parameters use to initialise the plugin
|
||||
* @return {PluginDefinition} an object representing the plugin
|
||||
*/
|
||||
function create(params) {
|
||||
return {
|
||||
name: 'minimap',
|
||||
deferInit: params && params.deferInit ? params.deferInit : false,
|
||||
params: params,
|
||||
staticProps: {},
|
||||
instance: MinimapPlugin
|
||||
};
|
||||
}
|
||||
}]);
|
||||
return MinimapPlugin;
|
||||
}();
|
||||
module.exports = exports.default;
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/
|
||||
/******/ // startup
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ // This entry module is referenced by other modules so it can't be inlined
|
||||
/******/ var __webpack_exports__ = __webpack_require__("./src/plugin/minimap/index.js");
|
||||
/******/
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
//# sourceMappingURL=wavesurfer.minimap.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,640 @@
|
|||
/*!
|
||||
* wavesurfer.js timeline plugin 4.6.0 (2024-02-05)
|
||||
* https://wavesurfer-js.org
|
||||
* @license BSD-3-Clause
|
||||
*/
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define("WaveSurfer", [], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["WaveSurfer"] = factory();
|
||||
else
|
||||
root["WaveSurfer"] = root["WaveSurfer"] || {}, root["WaveSurfer"]["timeline"] = factory();
|
||||
})(this, () => {
|
||||
return /******/ (() => { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ "./src/plugin/timeline/index.js":
|
||||
/*!**************************************!*\
|
||||
!*** ./src/plugin/timeline/index.js ***!
|
||||
\**************************************/
|
||||
/***/ ((module, exports) => {
|
||||
|
||||
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({
|
||||
value: true
|
||||
}));
|
||||
exports["default"] = void 0;
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
/**
|
||||
* @typedef {Object} TimelinePluginParams
|
||||
* @desc Extends the `WavesurferParams` wavesurfer was initialised with
|
||||
* @property {!string|HTMLElement} container CSS selector or HTML element where
|
||||
* the timeline should be drawn. This is the only required parameter.
|
||||
* @property {number} notchPercentHeight=90 Height of notches in percent
|
||||
* @property {string} unlabeledNotchColor='#c0c0c0' The colour of the notches
|
||||
* that do not have labels
|
||||
* @property {string} primaryColor='#000' The colour of the main notches
|
||||
* @property {string} secondaryColor='#c0c0c0' The colour of the secondary
|
||||
* notches
|
||||
* @property {string} primaryFontColor='#000' The colour of the labels next to
|
||||
* the main notches
|
||||
* @property {string} secondaryFontColor='#000' The colour of the labels next to
|
||||
* the secondary notches
|
||||
* @property {number} labelPadding=5 The padding between the label and the notch
|
||||
* @property {?number} zoomDebounce A debounce timeout to increase rendering
|
||||
* performance for large files
|
||||
* @property {string} fontFamily='Arial'
|
||||
* @property {number} fontSize=10 Font size of labels in pixels
|
||||
* @property {?number} duration Length of the track in seconds. Overrides
|
||||
* getDuration() for setting length of timeline
|
||||
* @property {function} formatTimeCallback (sec, pxPerSec) -> label
|
||||
* @property {function} timeInterval (pxPerSec) -> seconds between notches
|
||||
* @property {function} primaryLabelInterval (pxPerSec) -> cadence between
|
||||
* labels in primary color
|
||||
* @property {function} secondaryLabelInterval (pxPerSec) -> cadence between
|
||||
* labels in secondary color
|
||||
* @property {?number} offset Offset for the timeline start in seconds. May also be
|
||||
* negative.
|
||||
* @property {?boolean} deferInit Set to true to manually call
|
||||
* `initPlugin('timeline')`
|
||||
*/
|
||||
/**
|
||||
* Adds a timeline to the waveform.
|
||||
*
|
||||
* @implements {PluginClass}
|
||||
* @extends {Observer}
|
||||
* @example
|
||||
* // es6
|
||||
* import TimelinePlugin from 'wavesurfer.timeline.js';
|
||||
*
|
||||
* // commonjs
|
||||
* var TimelinePlugin = require('wavesurfer.timeline.js');
|
||||
*
|
||||
* // if you are using <script> tags
|
||||
* var TimelinePlugin = window.WaveSurfer.timeline;
|
||||
*
|
||||
* // ... initialising wavesurfer with the plugin
|
||||
* var wavesurfer = WaveSurfer.create({
|
||||
* // wavesurfer options ...
|
||||
* plugins: [
|
||||
* TimelinePlugin.create({
|
||||
* // plugin options ...
|
||||
* })
|
||||
* ]
|
||||
* });
|
||||
*/
|
||||
var TimelinePlugin = exports["default"] = /*#__PURE__*/function () {
|
||||
/**
|
||||
* Creates an instance of TimelinePlugin.
|
||||
*
|
||||
* You probably want to use TimelinePlugin.create()
|
||||
*
|
||||
* @param {TimelinePluginParams} params Plugin parameters
|
||||
* @param {object} ws Wavesurfer instance
|
||||
*/
|
||||
function TimelinePlugin(params, ws) {
|
||||
var _this = this;
|
||||
_classCallCheck(this, TimelinePlugin);
|
||||
_initialiseProps.call(this);
|
||||
this.container = 'string' == typeof params.container ? document.querySelector(params.container) : params.container;
|
||||
if (!this.container) {
|
||||
throw new Error('No container for wavesurfer timeline');
|
||||
}
|
||||
this.wavesurfer = ws;
|
||||
this.util = ws.util;
|
||||
this.params = Object.assign({}, {
|
||||
height: 20,
|
||||
notchPercentHeight: 90,
|
||||
labelPadding: 5,
|
||||
unlabeledNotchColor: '#c0c0c0',
|
||||
primaryColor: '#000',
|
||||
secondaryColor: '#c0c0c0',
|
||||
primaryFontColor: '#000',
|
||||
secondaryFontColor: '#000',
|
||||
fontFamily: 'Arial',
|
||||
fontSize: 10,
|
||||
duration: null,
|
||||
zoomDebounce: false,
|
||||
formatTimeCallback: this.defaultFormatTimeCallback,
|
||||
timeInterval: this.defaultTimeInterval,
|
||||
primaryLabelInterval: this.defaultPrimaryLabelInterval,
|
||||
secondaryLabelInterval: this.defaultSecondaryLabelInterval,
|
||||
offset: 0
|
||||
}, params);
|
||||
this.canvases = [];
|
||||
this.wrapper = null;
|
||||
this.drawer = null;
|
||||
this.pixelRatio = null;
|
||||
this.maxCanvasWidth = null;
|
||||
this.maxCanvasElementWidth = null;
|
||||
/**
|
||||
* This event handler has to be in the constructor function because it
|
||||
* relies on the debounce function which is only available after
|
||||
* instantiation
|
||||
*
|
||||
* Use a debounced function if `params.zoomDebounce` is defined
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
this._onZoom = this.params.zoomDebounce ? this.wavesurfer.util.debounce(function () {
|
||||
return _this.render();
|
||||
}, this.params.zoomDebounce) : function () {
|
||||
return _this.render();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialisation function used by the plugin API
|
||||
*/
|
||||
_createClass(TimelinePlugin, [{
|
||||
key: "init",
|
||||
value: function init() {
|
||||
// Check if ws is ready
|
||||
if (this.wavesurfer.isReady) {
|
||||
this._onReady();
|
||||
} else {
|
||||
this.wavesurfer.once('ready', this._onReady);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy function used by the plugin API
|
||||
*/
|
||||
}, {
|
||||
key: "destroy",
|
||||
value: function destroy() {
|
||||
this.unAll();
|
||||
this.wavesurfer.un('redraw', this._onRedraw);
|
||||
this.wavesurfer.un('zoom', this._onZoom);
|
||||
this.wavesurfer.un('ready', this._onReady);
|
||||
this.wavesurfer.drawer.wrapper.removeEventListener('scroll', this._onScroll);
|
||||
if (this.wrapper && this.wrapper.parentNode) {
|
||||
this.wrapper.removeEventListener('click', this._onWrapperClick);
|
||||
this.wrapper.parentNode.removeChild(this.wrapper);
|
||||
this.wrapper = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a timeline element to wrap the canvases drawn by this plugin
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "createWrapper",
|
||||
value: function createWrapper() {
|
||||
var wsParams = this.wavesurfer.params;
|
||||
this.container.innerHTML = '';
|
||||
this.wrapper = this.container.appendChild(document.createElement('timeline'));
|
||||
this.util.style(this.wrapper, {
|
||||
display: 'block',
|
||||
position: 'relative',
|
||||
userSelect: 'none',
|
||||
webkitUserSelect: 'none',
|
||||
height: "".concat(this.params.height, "px")
|
||||
});
|
||||
if (wsParams.fillParent || wsParams.scrollParent) {
|
||||
this.util.style(this.wrapper, {
|
||||
width: '100%',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'hidden'
|
||||
});
|
||||
}
|
||||
this.wrapper.addEventListener('click', this._onWrapperClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the timeline (also updates the already rendered timeline)
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "render",
|
||||
value: function render() {
|
||||
if (!this.wrapper) {
|
||||
this.createWrapper();
|
||||
}
|
||||
this.updateCanvases();
|
||||
this.updateCanvasesPositioning();
|
||||
this.renderCanvases();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new timeline canvas
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "addCanvas",
|
||||
value: function addCanvas() {
|
||||
var canvas = this.wrapper.appendChild(document.createElement('canvas'));
|
||||
this.canvases.push(canvas);
|
||||
this.util.style(canvas, {
|
||||
position: 'absolute',
|
||||
zIndex: 4
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove timeline canvas
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "removeCanvas",
|
||||
value: function removeCanvas() {
|
||||
var canvas = this.canvases.pop();
|
||||
canvas.parentElement.removeChild(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the correct of timeline canvas elements exist and are cached in
|
||||
* this.canvases
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "updateCanvases",
|
||||
value: function updateCanvases() {
|
||||
var totalWidth = Math.round(this.drawer.wrapper.scrollWidth);
|
||||
var requiredCanvases = Math.ceil(totalWidth / this.maxCanvasElementWidth);
|
||||
while (this.canvases.length < requiredCanvases) {
|
||||
this.addCanvas();
|
||||
}
|
||||
while (this.canvases.length > requiredCanvases) {
|
||||
this.removeCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the dimensions and positioning style for all the timeline canvases
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "updateCanvasesPositioning",
|
||||
value: function updateCanvasesPositioning() {
|
||||
var _this2 = this;
|
||||
// cache length for performance
|
||||
var canvasesLength = this.canvases.length;
|
||||
this.canvases.forEach(function (canvas, i) {
|
||||
// canvas width is the max element width, or if it is the last the
|
||||
// required width
|
||||
var canvasWidth = i === canvasesLength - 1 ? _this2.drawer.wrapper.scrollWidth - _this2.maxCanvasElementWidth * (canvasesLength - 1) : _this2.maxCanvasElementWidth;
|
||||
// set dimensions and style
|
||||
canvas.width = canvasWidth * _this2.pixelRatio;
|
||||
// on certain pixel ratios the canvas appears cut off at the bottom,
|
||||
// therefore leave 1px extra
|
||||
canvas.height = (_this2.params.height + 1) * _this2.pixelRatio;
|
||||
_this2.util.style(canvas, {
|
||||
width: "".concat(canvasWidth, "px"),
|
||||
height: "".concat(_this2.params.height, "px"),
|
||||
left: "".concat(i * _this2.maxCanvasElementWidth, "px")
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the timeline labels and notches
|
||||
*
|
||||
*/
|
||||
}, {
|
||||
key: "renderCanvases",
|
||||
value: function renderCanvases() {
|
||||
var _this3 = this;
|
||||
var duration = this.params.duration || this.wavesurfer.backend.getDuration();
|
||||
if (duration <= 0) {
|
||||
return;
|
||||
}
|
||||
var wsParams = this.wavesurfer.params;
|
||||
var fontSize = this.params.fontSize * wsParams.pixelRatio;
|
||||
var totalSeconds = parseInt(duration, 10) + 1;
|
||||
var width = wsParams.fillParent && !wsParams.scrollParent ? this.drawer.getWidth() : this.drawer.wrapper.scrollWidth * wsParams.pixelRatio;
|
||||
var height1 = this.params.height * this.pixelRatio;
|
||||
var height2 = this.params.height * (this.params.notchPercentHeight / 100) * this.pixelRatio;
|
||||
var pixelsPerSecond = width / duration;
|
||||
var formatTime = this.params.formatTimeCallback;
|
||||
// if parameter is function, call the function with
|
||||
// pixelsPerSecond, otherwise simply take the value as-is
|
||||
var intervalFnOrVal = function intervalFnOrVal(option) {
|
||||
return typeof option === 'function' ? option(pixelsPerSecond) : option;
|
||||
};
|
||||
var timeInterval = intervalFnOrVal(this.params.timeInterval);
|
||||
var primaryLabelInterval = intervalFnOrVal(this.params.primaryLabelInterval);
|
||||
var secondaryLabelInterval = intervalFnOrVal(this.params.secondaryLabelInterval);
|
||||
var curPixel = pixelsPerSecond * this.params.offset;
|
||||
var curSeconds = 0;
|
||||
var i;
|
||||
// build an array of position data with index, second and pixel data,
|
||||
// this is then used multiple times below
|
||||
var positioning = [];
|
||||
for (i = 0; i < totalSeconds / timeInterval; i++) {
|
||||
positioning.push([i, curSeconds, curPixel]);
|
||||
curSeconds += timeInterval;
|
||||
curPixel += pixelsPerSecond * timeInterval;
|
||||
}
|
||||
|
||||
// iterate over each position
|
||||
var renderPositions = function renderPositions(cb) {
|
||||
positioning.forEach(function (pos) {
|
||||
cb(pos[0], pos[1], pos[2]);
|
||||
});
|
||||
};
|
||||
|
||||
// render primary labels
|
||||
this.setFillStyles(this.params.primaryColor);
|
||||
this.setFonts("".concat(fontSize, "px ").concat(this.params.fontFamily));
|
||||
this.setFillStyles(this.params.primaryFontColor);
|
||||
renderPositions(function (i, curSeconds, curPixel) {
|
||||
if (i % primaryLabelInterval === 0) {
|
||||
_this3.fillRect(curPixel, 0, 1, height1);
|
||||
_this3.fillText(formatTime(curSeconds, pixelsPerSecond), curPixel + _this3.params.labelPadding * _this3.pixelRatio, height1);
|
||||
}
|
||||
});
|
||||
|
||||
// render secondary labels
|
||||
this.setFillStyles(this.params.secondaryColor);
|
||||
this.setFonts("".concat(fontSize, "px ").concat(this.params.fontFamily));
|
||||
this.setFillStyles(this.params.secondaryFontColor);
|
||||
renderPositions(function (i, curSeconds, curPixel) {
|
||||
if (i % secondaryLabelInterval === 0) {
|
||||
_this3.fillRect(curPixel, 0, 1, height1);
|
||||
_this3.fillText(formatTime(curSeconds, pixelsPerSecond), curPixel + _this3.params.labelPadding * _this3.pixelRatio, height1);
|
||||
}
|
||||
});
|
||||
|
||||
// render the actual notches (when no labels are used)
|
||||
this.setFillStyles(this.params.unlabeledNotchColor);
|
||||
renderPositions(function (i, curSeconds, curPixel) {
|
||||
if (i % secondaryLabelInterval !== 0 && i % primaryLabelInterval !== 0) {
|
||||
_this3.fillRect(curPixel, 0, 1, height2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the canvas fill style
|
||||
*
|
||||
* @param {DOMString|CanvasGradient|CanvasPattern} fillStyle Fill style to
|
||||
* use
|
||||
*/
|
||||
}, {
|
||||
key: "setFillStyles",
|
||||
value: function setFillStyles(fillStyle) {
|
||||
this.canvases.forEach(function (canvas) {
|
||||
canvas.getContext('2d').fillStyle = fillStyle;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the canvas font
|
||||
*
|
||||
* @param {DOMString} font Font to use
|
||||
*/
|
||||
}, {
|
||||
key: "setFonts",
|
||||
value: function setFonts(font) {
|
||||
this.canvases.forEach(function (canvas) {
|
||||
canvas.getContext('2d').font = font;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a rectangle on the canvases
|
||||
*
|
||||
* (it figures out the offset for each canvas)
|
||||
*
|
||||
* @param {number} x X-position
|
||||
* @param {number} y Y-position
|
||||
* @param {number} width Width
|
||||
* @param {number} height Height
|
||||
*/
|
||||
}, {
|
||||
key: "fillRect",
|
||||
value: function fillRect(x, y, width, height) {
|
||||
var _this4 = this;
|
||||
this.canvases.forEach(function (canvas, i) {
|
||||
var leftOffset = i * _this4.maxCanvasWidth;
|
||||
var intersection = {
|
||||
x1: Math.max(x, i * _this4.maxCanvasWidth),
|
||||
y1: y,
|
||||
x2: Math.min(x + width, i * _this4.maxCanvasWidth + canvas.width),
|
||||
y2: y + height
|
||||
};
|
||||
if (intersection.x1 < intersection.x2) {
|
||||
canvas.getContext('2d').fillRect(intersection.x1 - leftOffset, intersection.y1, intersection.x2 - intersection.x1, intersection.y2 - intersection.y1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a given text on the canvases
|
||||
*
|
||||
* @param {string} text Text to render
|
||||
* @param {number} x X-position
|
||||
* @param {number} y Y-position
|
||||
*/
|
||||
}, {
|
||||
key: "fillText",
|
||||
value: function fillText(text, x, y) {
|
||||
var textWidth;
|
||||
var xOffset = 0;
|
||||
this.canvases.forEach(function (canvas) {
|
||||
var context = canvas.getContext('2d');
|
||||
var canvasWidth = context.canvas.width;
|
||||
if (xOffset > x + textWidth) {
|
||||
return;
|
||||
}
|
||||
if (xOffset + canvasWidth > x) {
|
||||
textWidth = context.measureText(text).width;
|
||||
context.fillText(text, x - xOffset, y);
|
||||
}
|
||||
xOffset += canvasWidth;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the time into a suitable label for the time.
|
||||
*
|
||||
* @param {number} seconds Seconds to format
|
||||
* @param {number} pxPerSec Pixels per second
|
||||
* @returns {number} Time
|
||||
*/
|
||||
}, {
|
||||
key: "defaultFormatTimeCallback",
|
||||
value: function defaultFormatTimeCallback(seconds, pxPerSec) {
|
||||
if (seconds / 60 > 1) {
|
||||
// calculate minutes and seconds from seconds count
|
||||
var minutes = parseInt(seconds / 60, 10);
|
||||
seconds = parseInt(seconds % 60, 10);
|
||||
// fill up seconds with zeroes
|
||||
seconds = seconds < 10 ? '0' + seconds : seconds;
|
||||
return "".concat(minutes, ":").concat(seconds);
|
||||
}
|
||||
return Math.round(seconds * 1000) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how many seconds should be between each notch
|
||||
*
|
||||
* @param {number} pxPerSec Pixels per second
|
||||
* @returns {number} Time
|
||||
*/
|
||||
}, {
|
||||
key: "defaultTimeInterval",
|
||||
value: function defaultTimeInterval(pxPerSec) {
|
||||
if (pxPerSec >= 25) {
|
||||
return 1;
|
||||
} else if (pxPerSec * 5 >= 25) {
|
||||
return 5;
|
||||
} else if (pxPerSec * 15 >= 25) {
|
||||
return 15;
|
||||
}
|
||||
return Math.ceil(0.5 / pxPerSec) * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cadence of notches that get labels in the primary color.
|
||||
*
|
||||
* @param {number} pxPerSec Pixels per second
|
||||
* @returns {number} Cadence
|
||||
*/
|
||||
}, {
|
||||
key: "defaultPrimaryLabelInterval",
|
||||
value: function defaultPrimaryLabelInterval(pxPerSec) {
|
||||
if (pxPerSec >= 25) {
|
||||
return 10;
|
||||
} else if (pxPerSec * 5 >= 25) {
|
||||
return 6;
|
||||
} else if (pxPerSec * 15 >= 25) {
|
||||
return 4;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cadence of notches that get labels in the secondary color.
|
||||
*
|
||||
* @param {number} pxPerSec Pixels per second
|
||||
* @returns {number} Cadence
|
||||
*/
|
||||
}, {
|
||||
key: "defaultSecondaryLabelInterval",
|
||||
value: function defaultSecondaryLabelInterval(pxPerSec) {
|
||||
if (pxPerSec >= 25) {
|
||||
return 5;
|
||||
} else if (pxPerSec * 5 >= 25) {
|
||||
return 2;
|
||||
} else if (pxPerSec * 15 >= 25) {
|
||||
return 2;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
}], [{
|
||||
key: "create",
|
||||
value:
|
||||
/**
|
||||
* Timeline plugin definition factory
|
||||
*
|
||||
* This function must be used to create a plugin definition which can be
|
||||
* used by wavesurfer to correctly instantiate the plugin.
|
||||
*
|
||||
* @param {TimelinePluginParams} params parameters use to initialise the plugin
|
||||
* @return {PluginDefinition} an object representing the plugin
|
||||
*/
|
||||
function create(params) {
|
||||
return {
|
||||
name: 'timeline',
|
||||
deferInit: params && params.deferInit ? params.deferInit : false,
|
||||
params: params,
|
||||
instance: TimelinePlugin
|
||||
};
|
||||
}
|
||||
|
||||
// event handlers
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {object} e Click event
|
||||
*/
|
||||
}]);
|
||||
return TimelinePlugin;
|
||||
}();
|
||||
var _initialiseProps = function _initialiseProps() {
|
||||
var _this5 = this;
|
||||
this._onScroll = function () {
|
||||
if (_this5.wrapper && _this5.drawer.wrapper) {
|
||||
_this5.wrapper.scrollLeft = _this5.drawer.wrapper.scrollLeft;
|
||||
}
|
||||
};
|
||||
this._onRedraw = function () {
|
||||
return _this5.render();
|
||||
};
|
||||
this._onReady = function () {
|
||||
var ws = _this5.wavesurfer;
|
||||
_this5.drawer = ws.drawer;
|
||||
_this5.pixelRatio = ws.drawer.params.pixelRatio;
|
||||
_this5.maxCanvasWidth = ws.drawer.maxCanvasWidth || ws.drawer.width;
|
||||
_this5.maxCanvasElementWidth = ws.drawer.maxCanvasElementWidth || Math.round(_this5.maxCanvasWidth / _this5.pixelRatio);
|
||||
|
||||
// add listeners
|
||||
ws.drawer.wrapper.addEventListener('scroll', _this5._onScroll);
|
||||
ws.on('redraw', _this5._onRedraw);
|
||||
ws.on('zoom', _this5._onZoom);
|
||||
_this5.render();
|
||||
};
|
||||
this._onWrapperClick = function (e) {
|
||||
e.preventDefault();
|
||||
var relX = 'offsetX' in e ? e.offsetX : e.layerX;
|
||||
_this5.fireEvent('click', relX / _this5.wrapper.scrollWidth || 0);
|
||||
};
|
||||
};
|
||||
module.exports = exports.default;
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/
|
||||
/******/ // startup
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ // This entry module is referenced by other modules so it can't be inlined
|
||||
/******/ var __webpack_exports__ = __webpack_require__("./src/plugin/timeline/index.js");
|
||||
/******/
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
//# sourceMappingURL=wavesurfer.timeline.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue