Waveform

by PhilQ

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/wavesurfer.js/1.4.0/wavesurfer.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<div id="waveform"></div>
<div id="markers">
	<a href="#" class="segment start" data-label="A" style="left: 15%;"></a>
	<a href="#" class="marker" style="left: 18%;"></a>
	<a href="#" class="marker" style="left: 25%;"></a>
	<a href="#" class="segment end" data-label="A" style="left: 27%;"></a>
	<a href="#" class="segment start" data-label="B" style="left: 55%;"></a>
	<a href="#" class="marker" style="left: 58%;"></a>
	<a href="#" class="marker" style="left: 62%;"></a>
	<a href="#" class="marker" style="left: 75%;"></a>
	<a href="#" class="segment end" data-label="B" style="left: 77%;"></a>
</div>

<div id="player_status">Hoi</div>

<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
<button onclick="$('.segment').slice(0,2).attr('data-label', 'XX');">Change data</button>

SCSS

*,:before,:after{margin:0;padding:0;box-sizing:border-box;}

// @import url('https://fonts.googleapis.com/css?family=Fredoka+One|Inconsolata:400,700');
@import url('https://fonts.googleapis.com/css?family=Alegreya+Sans:400,400i,700,700i,900,900i|Alegreya:400,400i,700,700i,900,900i');
/*
font-family: 'Alegreya Sans', sans-serif;
font-family: 'Alegreya', serif;
font-family: 'Fredoka One', cursive;
font-family: 'Inconsolata', monospace;
*/
body {
	font-family: sans-serif;
	background-color: #212121; //#263238;
}

#player_status {
	position: fixed;
	top: 25%;
	left: 50%;
	transform-origin: 50% 50%;
	transform: translate(-50%,-50%) scale(1); //0.2
	color: #fff;
	font-family: 'Alegreya Sans', sans-serif;
	font-family: 'Alegreya', serif;
	font-weight: bold;
	font-size: 32px;
	padding: 15px 25px;
	background: rgba(0,0,0, 0.3);
	border-radius: 6px;
	opacity: 0;
	transition: all 0.15s ease 0s;
	pointer-events: none;
	
	&.blink {
		transform: translate(-50%,-50%) scale(1);
		opacity: 1;
		transition: all 0s linear 0s;
	}
}

@keyframes spin {
    from {transform:rotate(0deg);}
    to {transform:rotate(359deg);}
}

#waveform {
	display: block;
	width: 100%;
	height: 128px;
	position: fixed;
	top: 10px;
	left: 0px;
	
	wave {
		opacity: 0;
		transition: opacity 0.35s ease;
	}

	&:before {
		content: '';
		position: absolute;
		top: 50%;
		left: 50%;
		width: 40px;
		height: 40px;
		margin-top: -20px;
		margin-left: -20px;
		border-radius: 50%;
		border: 4px solid rgba(#fff, 0.3);
		border-top-color: transparent;
		opacity: 1;
		transition: opacity 0.35s ease;
		animation: spin 1s infinite linear;
	}

	&:after {
		content: '';
		position: absolute;
		top: 100%;
		margin-top: 20px;
		left: 0px;
		width: 100%;
		border-top: 1px solid rgba(0,0,0, 0.3);
		border-bottom: 1px solid rgba(255,255,255, 0.05);
	}

	&.ready {
		wave {
			opacity: 1;
		}

		&:before {
			opacity: 0;
		}

		wave:nth-child(1) {
			position: absolute !important;
			z-index: 1;
			top: 0px;
			height:...

JavaScript

/*
speaker = {
	'id': 1,
	'name': ,
	'gender': ,
	'color': ,
};

marker = {
	'stamp': ,
	'speaker': null/1,
	'message': ,
};
*/

/**
 * Takes a positive integer and returns the corresponding column name.
 * @param {number} num  The positive integer to convert to a column name.
 * @return {string}  The column name.
 */
function num2alp(num) {
	for (var ret = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) {
		ret = String.fromCharCode(parseInt((num % b) / a) + 65) + ret;
	}
	return ret;
}
function alp2num(s) {
	for (var ret = 0, p=0, c=s.length; p<c; p++ ) {
		ret += ( (s.charCodeAt(c-(p+1)) - 64) * Math.pow(26, p) );
	}
	return ret;
}

$.fn.blink = function (count) {
    var $this = $(this);

    count = count - 1 || 0;

    $this
		.stop( true )
		.css({opacity: 1})
		.delay(100)
		.animate({opacity: 0}, 350, function () {
			if (count > 0) {
				$this.blink(count);
			}
		});

};


function bindMarkerEvents( objs ) {
	objs.on('mousedown', function(e){
		var $this = $(this);
		marker_x = e.pageX- ($this.offset()).left;
		$(window).on('mousemove.drag', function(ev){
			ev.preventDefault();
			$this.css('left', Math.max(0, Math.min(wave_width, (ev.pageX - marker_x))) +'px');
		}).on('mouseup.drag blur.drag', function(ev){
			$(window).off('mousemove.drag mouseup.drag blur.drag');
		});
	}).on('dblclick', function(e){
		wavesurfer.seekTo( ($(this).offset()).left / wave_width );
	});
}



var wavesurfer,
	skip_amount = 1,
	skip_amount_count = 0,
	skip_amount_timer,
	segment_count = 1,
	wave_width = 0,
	marker_x = 0;


$(window).on('keydown.volume', function(e){
	if ( true ) {
		if ( e.which == 38 || e.which == 40 ) {
			e.preventDefault();
			var v = wavesurfer.getVolume();
			v = (e.which == 38) ? v+0.1 : v-0.1;
			v = Math.min(1, Math.max(0, v)).toFixed(1);
			$('#player_status').html( 'Volume ' + (v*100) + '%' ).blink();
			wavesurfer.setVolume( v );
			/*
			if ( 0 < v ) {
				wavesurfer.setMute(false);
			} else...