koel/resources/assets/js/components/site-footer/equalizer.vue

378 lines
11 KiB
Vue
Raw Normal View History

2016-01-11 15:25:58 +00:00
<template>
<div id="equalizer">
<div class="presets">
2016-01-17 05:14:12 +00:00
<label class="select-wrapper" @change="loadPreset">
<select v-model="selectedPresetIndex">
<option value="-1">Preset</option>
<option v-for="preset in presets" :value="$index">{{* preset.name }}</option>
</select>
</label>
2016-01-11 15:25:58 +00:00
</div>
<div class="bands">
2016-01-19 11:00:23 +00:00
<span class="band preamp">
<input
2016-02-09 04:57:08 +00:00
type="range"
min="-20"
max="20"
2016-01-19 11:00:23 +00:00
step="0.01"
data-orientation="vertical"
v-model="preampGainValue">
<label>Preamp</label>
</span>
<span class="indicators">
<span>+20</span>
<span>0</span>
<span>-20</span>
</span>
<span class="band amp" v-for="band in bands">
2016-02-09 04:57:08 +00:00
<input
type="range"
min="-20"
max="20"
2016-01-16 18:11:24 +00:00
step="0.01"
2016-01-17 05:14:12 +00:00
data-orientation="vertical"
:value="band.filter.gain.value">
2016-01-19 11:00:23 +00:00
<label>{{* band.label }}</label>
2016-01-11 15:25:58 +00:00
</span>
</div>
</div>
</template>
<script>
import _ from 'lodash';
import $ from 'jquery';
2016-01-17 05:14:12 +00:00
import rangeslider from 'rangeslider.js';
2016-01-11 15:25:58 +00:00
2016-01-19 11:00:23 +00:00
import equalizerStore from '../../stores/equalizer';
2016-01-11 15:25:58 +00:00
import utils from '../../services/utils';
export default {
data() {
return {
bands: [],
2016-01-17 05:14:12 +00:00
selectedPresetIndex: -1,
2016-01-19 11:00:23 +00:00
preampGainValue: 0,
2016-01-11 15:25:58 +00:00
2016-01-19 11:00:23 +00:00
presets: equalizerStore.presets,
2016-01-11 15:25:58 +00:00
};
},
methods: {
/**
* Init the equalizer.
2016-02-09 04:57:08 +00:00
*
2016-01-17 14:26:24 +00:00
* @param {Element} player The audio player's DOM.
2016-01-11 15:25:58 +00:00
*/
init(player) {
2016-01-19 11:00:23 +00:00
var settings = equalizerStore.get();
2016-02-09 04:57:08 +00:00
var AudioContext = window.AudioContext || window.webkitAudioContext || false;
2016-01-11 15:25:58 +00:00
var context = new AudioContext();
2016-01-19 11:00:23 +00:00
this.preampGainNode = context.createGain();
this.changePreampGain(settings.preamp);
2016-01-11 15:25:58 +00:00
var source = context.createMediaElementSource(player);
2016-01-19 11:00:23 +00:00
source.connect(this.preampGainNode);
2016-01-11 15:25:58 +00:00
var prevFilter = null;
// Create 10 bands with the frequencies similar to those of Winamp and connect them together.
[60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000].forEach((f, i) => {
var filter = context.createBiquadFilter();
2016-01-19 11:00:23 +00:00
if (i === 0) {
filter.type = 'lowshelf';
} else if (i === 9) {
filter.type = 'highshelf'
} else {
2016-02-09 04:57:08 +00:00
filter.type = 'peaking';
2016-01-19 11:00:23 +00:00
}
2016-02-09 04:57:08 +00:00
2016-01-19 11:00:23 +00:00
filter.gain.value = settings.gains[i] ? settings.gains[i] : 0;
2016-01-11 15:25:58 +00:00
filter.Q.value = 1;
filter.frequency.value = f;
if (!prevFilter) {
2016-01-19 11:00:23 +00:00
this.preampGainNode.connect(filter);
2016-01-11 15:25:58 +00:00
} else {
prevFilter.connect(filter);
}
prevFilter = filter;
this.bands.push({
filter,
label: (f + '').replace('000', 'K'),
});
});
2016-02-09 04:57:08 +00:00
2016-01-11 15:25:58 +00:00
prevFilter.connect(context.destination);
2016-01-17 05:14:12 +00:00
2016-02-09 04:57:08 +00:00
this.$nextTick(this.createRangeSliders);
2016-01-19 11:00:23 +00:00
},
/**
* Create the UI slider for both the preamp and the normal bands using rangeslider.js.
*/
createRangeSliders() {
$('#equalizer input[type="range"]').each((i, el) => {
$(el).rangeslider({
/**
* Force the polyfill and its styles on all browsers.
2016-02-09 04:57:08 +00:00
*
2016-01-19 11:00:23 +00:00
* @type {Boolean}
*/
polyfill: false,
/**
* Change the gain/preamp value when the user drags the sliders.
2016-02-09 04:57:08 +00:00
*
2016-01-19 11:00:23 +00:00
* @param {Float} position
* @param {Float} value
*/
onSlide: (position, value) => {
if ($(el).parents('.band').is('.preamp')) {
this.changePreampGain(value);
} else {
this.changeFilterGain(this.bands[i-1].filter, value);
2016-01-17 05:14:12 +00:00
}
2016-01-19 11:00:23 +00:00
},
/**
* Save the settings and set the preset index to -1 (which is None) on slideEnd.
*/
onSlideEnd: () => {
this.selectedPresetIndex = -1;
this.save();
}
2016-02-09 04:57:08 +00:00
});
2016-01-17 05:14:12 +00:00
});
2016-01-11 15:25:58 +00:00
},
2016-01-19 11:00:23 +00:00
/**
* Change the gain value for the preamp.
2016-02-09 04:57:08 +00:00
*
2016-01-19 11:00:23 +00:00
* @param {Number} dbValue The value of the gain, in dB.
*/
changePreampGain(dbValue) {
this.preampGainValue = dbValue;
this.preampGainNode.gain.value = Math.pow(10, dbValue/20);
},
2016-01-11 15:25:58 +00:00
/**
2016-01-17 05:14:12 +00:00
* Change the gain value for a band/filter.
2016-02-09 04:57:08 +00:00
*
2016-01-17 05:14:12 +00:00
* @param {Object} filter The filter object
2016-01-19 11:00:23 +00:00
* @param {Object} value Value of the gain, in dB.
2016-01-11 15:25:58 +00:00
*/
2016-01-19 11:00:23 +00:00
changeFilterGain(filter, value) {
2016-01-17 05:14:12 +00:00
filter.gain.value = value;
2016-01-11 15:25:58 +00:00
},
/**
2016-01-17 05:14:12 +00:00
* Load a preset when the user select it from the dropdown.
2016-01-11 15:25:58 +00:00
*/
2016-01-17 05:14:12 +00:00
loadPreset() {
2016-01-19 11:00:23 +00:00
if (Number.parseInt(this.selectedPresetIndex) === -1) {
2016-01-17 05:14:12 +00:00
return;
}
var preset = this.presets[this.selectedPresetIndex];
2016-01-11 15:25:58 +00:00
$('#equalizer input[type=range]').each((i, input) => {
2016-01-19 11:00:23 +00:00
// We treat our preamp slider differently.
if ($(input).parents('.band').is('.preamp')) {
this.changePreampGain(preset.preamp);
} else {
this.changeFilterGain(this.bands[i-1].filter, preset.gains[i-1]);
2016-02-09 04:57:08 +00:00
input.value = preset.gains[i-1];
2016-01-19 11:00:23 +00:00
}
2016-01-11 15:25:58 +00:00
});
2016-02-09 04:57:08 +00:00
this.$nextTick(() => {
2016-01-19 11:00:23 +00:00
// Update the slider values into GUI.
2016-02-09 04:57:08 +00:00
$('#equalizer input[type="range"]').rangeslider('update', true);
2016-01-19 11:00:23 +00:00
});
2016-01-17 05:14:12 +00:00
2016-01-11 15:25:58 +00:00
this.save();
},
/**
* Save the current user's equalizer preferences into local storage.
*/
save() {
2016-01-19 11:00:23 +00:00
equalizerStore.set(this.preampGainValue, _.pluck(this.bands, 'filter.gain.value'));
2016-01-11 15:25:58 +00:00
},
},
events: {
'equalizer:init': function (player) {
if (utils.isAudioContextSupported()) {
this.init(player);
}
},
},
};
</script>
2016-01-17 05:14:12 +00:00
<style lang="sass">
2016-01-11 15:25:58 +00:00
@import "resources/assets/sass/partials/_vars.scss";
@import "resources/assets/sass/partials/_mixins.scss";
#equalizer {
position: absolute;
bottom: $footerHeight;
width: 100%;
background: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
left: 0;
2016-01-19 11:00:23 +00:00
label {
margin-top: 8px;
margin-bottom: 0;
2016-01-20 13:49:06 +00:00
text-align: left;
2016-01-19 11:00:23 +00:00
}
2016-01-11 15:25:58 +00:00
.presets {
2016-01-17 05:14:12 +00:00
padding: 8px 16px;
2016-01-11 15:25:58 +00:00
display: flex;
flex-wrap: wrap;
justify-content: space-between;
flex: 1;
align-content: center;
2016-01-16 05:15:33 +00:00
z-index: 1;
2016-01-17 05:14:12 +00:00
border-bottom: 1px solid rgba(255, 255, 255, .1);
2016-01-19 11:00:23 +00:00
2016-01-17 05:14:12 +00:00
.select-wrapper {
position: relative;
margin-bottom: 0;
&::after {
content: '\f107';
font-family: FontAwesome;
color: $colorHighlight;
display: inline-block;
position: absolute;
right: 8px;
top: 3px;
pointer-events: none;
2016-01-11 15:25:58 +00:00
}
}
2016-01-17 05:14:12 +00:00
select {
background: none;
color: $colorMainText;
padding-left: 0;
width: 100px;
text-transform: none;
}
2016-01-11 15:25:58 +00:00
}
.bands {
2016-01-17 05:14:12 +00:00
padding: 16px;
2016-01-19 11:00:23 +00:00
z-index: 1;
2016-01-11 15:25:58 +00:00
left: 0;
2016-01-19 11:00:23 +00:00
display: flex;
2016-01-11 15:25:58 +00:00
justify-content: space-between;
font-size: 70%;
2016-01-19 11:00:23 +00:00
align-items: flex-start;
2016-01-11 15:25:58 +00:00
.band {
display: flex;
flex-direction: column;
align-items: center;
}
2016-01-19 11:00:23 +00:00
.indicators {
height: 100px;
width: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
margin-left: -16px;
opacity: 0;
transition: .4s;
span:first-child {
line-height: 8px;
}
span:last-child {
line-height: 8px;
}
}
&:hover .indicators {
opacity: 1;
}
2016-01-11 15:25:58 +00:00
}
2016-01-12 14:53:15 +00:00
2016-01-17 05:14:12 +00:00
/**
* The range slider styles
*/
.rangeslider {
background: transparent;
2016-01-17 05:39:12 +00:00
box-shadow: none;
2016-01-17 05:14:12 +00:00
&--vertical {
min-height: 100px;
2016-01-19 11:00:23 +00:00
width: 16px;
2016-01-17 05:14:12 +00:00
&::before {
content: " ";
position: absolute;
2016-01-19 11:00:23 +00:00
left: 7px;
2016-01-17 05:14:12 +00:00
width: 2px;
background: rgba(255, 255, 255, 0.2);
z-index: 1;
height: 100%;
pointer-events: none;
}
.rangeslider__fill {
width: 2px;
background: #fff;
position: absolute;
2016-01-19 11:00:23 +00:00
left: 7px;
2016-01-17 05:14:12 +00:00
box-shadow: none;
border-radius: 0;
}
.rangeslider__handle {
left: 0;
background: #fff;
border: 0;
height: 2px;
width: 100%;
border-radius: 0;
box-shadow: none;
&::after {
display: none;
}
}
}
}
@media only screen and (max-device-width : 768px) {
2016-01-12 14:53:15 +00:00
position: fixed;
max-width: 414px;
left: auto;
right: 0;
bottom: $footerHeight + 5px;
display: block;
height: auto;
label {
line-height: 20px;
}
}
2016-01-11 15:25:58 +00:00
}
</style>