mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
Add a (basic) equalizer
This commit is contained in:
parent
9303145122
commit
47064547f9
6 changed files with 291 additions and 9 deletions
|
@ -132,7 +132,7 @@
|
|||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-webkit-filter: blur(20px);
|
||||
filter: blur(20px);
|
||||
opacity: .07;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
|
|
236
resources/assets/js/components/site-footer/equalizer.vue
Normal file
236
resources/assets/js/components/site-footer/equalizer.vue
Normal file
|
@ -0,0 +1,236 @@
|
|||
<template>
|
||||
<div id="equalizer">
|
||||
<div class="presets">
|
||||
<button v-for="preset in presets" @click.prevent="loadPreset(preset)">{{ preset.name }}</button>
|
||||
</div>
|
||||
<div class="bands">
|
||||
<span class="band" v-for="band in bands">
|
||||
<label>{{ band.label }}</label>
|
||||
<input type="range" min="-20" max="20" step="0.01" :value="band.filter.gain.value" orient="vertical"
|
||||
@input="changeGain(band.filter, $event)">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
|
||||
import preferenceStore from '../../stores/preference';
|
||||
import utils from '../../services/utils';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
bands: [],
|
||||
|
||||
presets: [
|
||||
{
|
||||
name: 'None',
|
||||
gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
},
|
||||
{
|
||||
name: 'Classical',
|
||||
gains: [-1, -1, -1, -1, -1, -1, -7, -7, -7, -9],
|
||||
},
|
||||
{
|
||||
name: 'Club',
|
||||
gains: [-1, -1, 8, 5, 5, 5, 3, -1, -1, -1],
|
||||
},
|
||||
{
|
||||
name: 'Dance',
|
||||
gains: [9, 7, 2, -1, -1, -5, -7, -7, -1, -1],
|
||||
},
|
||||
{
|
||||
name: 'Full Bass',
|
||||
gains: [-8, 9, 9, 5, 1, -4, -8, -10, -11, -11]
|
||||
},
|
||||
{
|
||||
name: 'Full Treble',
|
||||
gains: [-9, -9, -9, -4, 2, 11, 16, 16, 16, 16]
|
||||
},
|
||||
{
|
||||
name: 'Headphone',
|
||||
gains: [4, 11, 5, -3, -2, 1, 4, 9, 12, 14]
|
||||
},
|
||||
{
|
||||
name: 'Large Hall',
|
||||
gains: [10, 10, 5, 5, -1, -4, -4, -4, -1, -1],
|
||||
},
|
||||
{
|
||||
name: 'Live',
|
||||
gains: [-4, -1, 4, 5, 5, 5, 4, 2, 2, 2],
|
||||
},
|
||||
{
|
||||
name: 'Pop',
|
||||
gains: [-1, 4, 7, 8, 5, -1, -2, -2, -1, -1],
|
||||
},
|
||||
{
|
||||
name: 'Reggae',
|
||||
gains: [-1, -1, -1, -5, -1, 6, 6, -1, -1, -1],
|
||||
},
|
||||
{
|
||||
name: 'Rock',
|
||||
gains: [8, 4, -5, -8, -3, 4, 8, 11, 11, 11],
|
||||
},
|
||||
{
|
||||
name: 'Soft Rock',
|
||||
gains: [4, 4, 2, -1, -4, -5, -3, -1, 2, 8],
|
||||
},
|
||||
{
|
||||
name: 'Techno',
|
||||
gains: [8, 5, -1, -5, -4, -1, 8, 9, 9, 8],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Init the equalizer.
|
||||
*
|
||||
* @param {Object} player The audio player DOM.
|
||||
*/
|
||||
init(player) {
|
||||
var AudioContext = window.AudioContext || window.webkitAudioContext || false;
|
||||
var context = new AudioContext();
|
||||
|
||||
var gainNode = context.createGain();
|
||||
gainNode.gain.value = 1;
|
||||
|
||||
var source = context.createMediaElementSource(player);
|
||||
source.connect(gainNode);
|
||||
|
||||
var prevFilter = null;
|
||||
|
||||
var savedGains = preferenceStore.get('equalizerGains');
|
||||
|
||||
// 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();
|
||||
|
||||
filter.type = 'peaking';
|
||||
filter.gain.value = savedGains[i];
|
||||
filter.Q.value = 1;
|
||||
filter.frequency.value = f;
|
||||
|
||||
if (!prevFilter) {
|
||||
gainNode.connect(filter);
|
||||
} else {
|
||||
prevFilter.connect(filter);
|
||||
}
|
||||
|
||||
prevFilter = filter;
|
||||
|
||||
this.bands.push({
|
||||
filter,
|
||||
label: (f + '').replace('000', 'K'),
|
||||
});
|
||||
});
|
||||
|
||||
prevFilter.connect(context.destination);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the gain value for a band/filter on range input's value change.
|
||||
*
|
||||
* @param {Object} filter The filter object.
|
||||
* @param {Object} e The input event
|
||||
*/
|
||||
changeGain(filter, e) {
|
||||
filter.gain.value = e.target.value;
|
||||
this.save();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a preset.
|
||||
*
|
||||
* @param {Object} preset The preset.
|
||||
*/
|
||||
loadPreset(preset) {
|
||||
$('#equalizer input[type=range]').each((i, input) => {
|
||||
this.bands[i].filter.gain.value = preset.gains[i];
|
||||
input.value = preset.gains[i];
|
||||
});
|
||||
|
||||
this.save();
|
||||
},
|
||||
|
||||
/**
|
||||
* Save the current user's equalizer preferences into local storage.
|
||||
*/
|
||||
save() {
|
||||
preferenceStore.set('equalizerGains', _.pluck(this.bands, 'filter.gain.value'));
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
'equalizer:init': function (player) {
|
||||
if (utils.isAudioContextSupported()) {
|
||||
this.init(player);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "resources/assets/sass/partials/_vars.scss";
|
||||
@import "resources/assets/sass/partials/_mixins.scss";
|
||||
|
||||
#equalizer {
|
||||
position: absolute;
|
||||
bottom: $footerHeight;
|
||||
height: 256px;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
left: 0;
|
||||
|
||||
.presets {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
flex: 1;
|
||||
align-content: center;
|
||||
|
||||
button {
|
||||
font-size: 70%;
|
||||
border-radius: 0;
|
||||
text-transform: uppercase;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, .2);
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: $colorRed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bands {
|
||||
display: flex;
|
||||
left: 0;
|
||||
justify-content: space-between;
|
||||
font-size: 70%;
|
||||
align-items: center;
|
||||
|
||||
input[type="range"] {
|
||||
writing-mode: bt-lr; /* IE */
|
||||
-webkit-appearance: slider-vertical; /* WebKit */
|
||||
width: 8px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.band {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -35,7 +35,10 @@
|
|||
</div>
|
||||
|
||||
<span class="other-controls" :class="{ 'with-gradient': prefs.showExtraPanel }">
|
||||
<equalizer v-if="useEqualizer" v-show="showEqualizer"></equalizer>
|
||||
|
||||
<sound-bar v-show="playing"></sound-bar>
|
||||
|
||||
<i class="like control fa fa-heart" :class="{ 'liked': liked }"
|
||||
@click.prevent="like"></i>
|
||||
|
||||
|
@ -43,7 +46,13 @@
|
|||
@click.prevent="toggleExtraPanel"
|
||||
:class="{ active: prefs.showExtraPanel }">Info</span>
|
||||
|
||||
<i class="queue control fa fa-list-ol control"
|
||||
<i class="fa fa-sliders control"
|
||||
v-if="useEqualizer"
|
||||
@click="showEqualizer = !showEqualizer"
|
||||
:class="{ active: showEqualizer }"></i>
|
||||
|
||||
<i v-else
|
||||
class="queue control fa fa-list-ol control"
|
||||
:class="{ 'active': viewingQueue }"
|
||||
@click.prevent="$root.loadMainView('queue')"></i>
|
||||
|
||||
|
@ -62,12 +71,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import soundBar from '../shared/sound-bar.vue';
|
||||
import config from '../../config';
|
||||
import playback from '../../services/playback';
|
||||
import utils from '../../services/utils';
|
||||
|
||||
import songStore from '../../stores/song';
|
||||
import favoriteStore from '../../stores/favorite';
|
||||
import preferenceStore from '../../stores/preference';
|
||||
import config from '../../config';
|
||||
import playback from '../../services/playback';
|
||||
|
||||
import soundBar from '../shared/sound-bar.vue';
|
||||
import equalizer from './equalizer.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -79,10 +92,18 @@
|
|||
liked: false,
|
||||
|
||||
prefs: preferenceStore.state,
|
||||
showEqualizer: false,
|
||||
|
||||
/**
|
||||
* Indicate if we should build and use an equalizer.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
useEqualizer: utils.isAudioContextSupported(),
|
||||
};
|
||||
},
|
||||
|
||||
components: { soundBar },
|
||||
components: { soundBar, equalizer },
|
||||
|
||||
watch: {
|
||||
/**
|
||||
|
@ -133,7 +154,7 @@
|
|||
* Set the volume level.
|
||||
*
|
||||
* @param {integer} volume Min 0, max 10.
|
||||
* @param {boolean=true} persist Whether the volume level should be store into local storage.
|
||||
* @param {boolean=true} persist Whether the volume level should be stored into local storage.
|
||||
*/
|
||||
setVolume(volume, persist = true) {
|
||||
playback.setVolume(volume, persist);
|
||||
|
@ -344,7 +365,7 @@
|
|||
padding: 0 8px;
|
||||
|
||||
&.active {
|
||||
color: $colorMainText;
|
||||
color: $colorHighlight;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
|
|
@ -72,6 +72,9 @@ export default {
|
|||
// On init, set the volume to the value found in the local storage.
|
||||
this.setVolume(preferenceStore.get('volume'));
|
||||
|
||||
// Init the equalizer if supported.
|
||||
this.app.$broadcast('equalizer:init', this.player.media);
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
|
|
|
@ -36,5 +36,26 @@ export default {
|
|||
*/
|
||||
parseValidationError(error) {
|
||||
return Object.keys(error).reduce((messages, field) => messages.concat(error[field]), []);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if AudioContext is supported by the current browser.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isAudioContextSupported() {
|
||||
var AudioContext = window.AudioContext || window.webkitAudioContext || false;
|
||||
|
||||
if (!AudioContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Safari (MacOS & iOS alike) has webkitAudioContext, but is buggy.
|
||||
// @link http://caniuse.com/#search=audiocontext
|
||||
if (!(new AudioContext()).createMediaElementSource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ export default {
|
|||
notify: true,
|
||||
repeatMode: 'NO_REPEAT',
|
||||
showExtraPanel: true,
|
||||
equalizerGains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue