From d2e3aa088017da83f7c28c219e2e3f71397c3889 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 30 Jan 2019 23:36:33 +0300 Subject: [PATCH] slider and volume ctrl --- libjs/wController/player.js | 71 ++++++++++++------ lorgar/views/CMakeLists.txt | 1 + lorgar/views/player.js | 6 +- lorgar/views/slider.js | 137 +++++++++++++++++++++++++++++++++++ lorgar/views/songProgress.js | 95 ++++-------------------- 5 files changed, 206 insertions(+), 104 deletions(-) create mode 100644 lorgar/views/slider.js diff --git a/libjs/wController/player.js b/libjs/wController/player.js index 4a9d42d..aa376be 100644 --- a/libjs/wController/player.js +++ b/libjs/wController/player.js @@ -22,9 +22,12 @@ var Player = Controller.inherit({ this.views = Object.create(null); this.mode = PlayerMode.straight.playback; this.progress = new ProgressModel(); + this.volume = new Slider(); + this.volume.setValue(1); this.progress.on("seekingStart", this._onSeekingStart, this); this.progress.on("seekingEnd", this._onSeekingEnd, this); - this.progress.on("seek", this._onSeek, this); + this.progress.enable(false); + this.volume.on("value", this._onVolume, this); this._audio = null; this._createStateMachine(); this._createPlayingInfrastructure(); @@ -123,6 +126,8 @@ var Player = Controller.inherit({ _createPlayingInfrastructure: function() { this._ctx = new AudioContext(); this._decoder = new Mp3Decoder(); + this._gainNode = this._ctx.createGain(); + this._gainNode.connect(this._ctx.destination); this._currentTime = 0; this._seekingTime = 0; this._buffers = []; @@ -212,14 +217,14 @@ var Player = Controller.inherit({ if (offset > 0) { var src = this._ctx.createBufferSource(); src.buffer = sb; - src.connect(this._ctx.destination); + src.connect(this._gainNode); src.start(0, Math.abs(startTime - this._ctx.currentTime)); this._sources.push(src); } } else { var src = this._ctx.createBufferSource(); src.buffer = sb; - src.connect(this._ctx.destination); + src.connect(this._gainNode); src.start(startTime); this._sources.push(src); } @@ -243,10 +248,10 @@ var Player = Controller.inherit({ _onInterval: function() { if (this._audio && this._audio.initialized && seekingStates.indexOf(this._fsm.state()) === -1) { var duration = this._audio.getDuration(); - this.progress.setPlayback(this.getCurrentPlaybackTime() / duration); + this.progress.setValue(this.getCurrentPlaybackTime() / duration); this._checkIfEnough(); - if (this.progress.playback >= 0.9999) { + if (this.progress.value >= 0.9999) { var next = this.controls[ItemType.straight.next]; if (next && next.enabled) { next.activate(); @@ -289,11 +294,6 @@ var Player = Controller.inherit({ this._audio = null; } }, - _onSeek: function(progress) { - if (seekingStates.indexOf(this._fsm.state()) !== -1) { - this.progress.setPlayback(progress); - } - }, _onSeekingStart: function() { this._fsm.manipulation("startSeeking"); }, @@ -319,14 +319,14 @@ var Player = Controller.inherit({ if (offset > 0) { var src = this._ctx.createBufferSource(); src.buffer = buffer; - src.connect(this._ctx.destination); + src.connect(this._gainNode); src.start(0, Math.abs(startTime)); this._sources.push(src); } } else { var src = this._ctx.createBufferSource(); src.buffer = buffer; - src.connect(this._ctx.destination); + src.connect(this._gainNode); src.start(this._ctx.currentTime + startTime); this._sources.push(src); } @@ -340,7 +340,7 @@ var Player = Controller.inherit({ switch (e.newState) { case "initial": if (e.manipulation === "noController") { - + this.progress.enable(false); this.removeForeignController(this._audio); this._audio.destructor(); this._audio = null; @@ -350,6 +350,7 @@ var Player = Controller.inherit({ break; case "initialPlaying": if (e.manipulation === "noController") { + this.progress.enable(false); this._ctx.suspend(); this.removeForeignController(this._audio); @@ -373,10 +374,15 @@ var Player = Controller.inherit({ } break; case "buffering": + if (e.oldState === "hasController") { + this.progress.enable(true); + } break; case "bufferingPlaying": if (e.oldState === "playing") { this._ctx.suspend(); + } else if (e.oldState === "hasControllerPlaying") { + this.progress.enable(true); } break; case "seeking": @@ -421,6 +427,10 @@ var Player = Controller.inherit({ break; } }, + _onVolume: function(volume) { + this._gainNode.gain.cancelScheduledValues(this._ctx.currentTime); + this._gainNode.gain.exponentialRampToValueAtTime(volume, this._ctx.currentTime + 0.01); + }, _removeControl: function(type) { var ctrl = this.controls[type]; if (ctrl !== undefined) { @@ -553,26 +563,41 @@ var audioPortion = 1024 * 50; //bytes to download for each portion var threshold = 2; //seconds to buffer before playing var intervalPrecision = 100; //millisecond of how often to check the playback -var ProgressModel = Model.inherit({ - className: "ProgressModel", +var Slider = Model.inherit({ + className: "Slider", constructor: function(properties) { Model.fn.constructor.call(this, properties); - this.load = 0; - this.playback = 0; + this.enabled = true; + this.value = 0; this.initialized = true; }, + enable: function(en) { + if (en !== this.enabled) { + this.enabled = en; + this.trigger("enabled", en); + } + }, + setValue: function(p) { + if (p !== this.value) { + this.value = p; + this.trigger("value", p); + } + } +}); + +var ProgressModel = Slider.inherit({ + className: "ProgressModel", + constructor: function(properties) { + Slider.fn.constructor.call(this, properties); + + this.value = 0; + }, setLoad: function(l) { if (l !== this.load) { this.load = l; this.trigger("load", l); } - }, - setPlayback: function(p) { - if (p !== this.playback) { - this.playback = p; - this.trigger("playback", p); - } } }); diff --git a/lorgar/views/CMakeLists.txt b/lorgar/views/CMakeLists.txt index 7e9ad6f..aedfc2c 100644 --- a/lorgar/views/CMakeLists.txt +++ b/lorgar/views/CMakeLists.txt @@ -14,6 +14,7 @@ configure_file(image.js image.js) configure_file(button.js button.js) configure_file(enumeration.js enumeration.js) configure_file(player.js player.js) +configure_file(slider.js slider.js) configure_file(songProgress.js songProgress.js) add_subdirectory(helpers) diff --git a/lorgar/views/player.js b/lorgar/views/player.js index 9013cac..4b42cb9 100644 --- a/lorgar/views/player.js +++ b/lorgar/views/player.js @@ -9,6 +9,7 @@ deps.push("views/view"); deps.push("views/image"); deps.push("views/songProgress"); + deps.push("views/slider"); deps.push("lib/wController/localModel"); @@ -21,6 +22,7 @@ var View = require("views/view"); var Image = require("views/image"); var SongProgress = require("views/songProgress"); + var Slider = require("views/slider"); var Model = require("lib/wController/localModel"); @@ -49,14 +51,16 @@ var artist = new Label(this._infoModels.artist); var album = new Label(this._infoModels.album); var song = new Label(this._infoModels.song); - var spacer = new View(helper, {maxWidth: 50}); + var spacer = new View(helper, {maxWidth: 10}); var progress = new SongProgress(ctrl.progress); + var volume = new Slider(ctrl.volume, {maxWidth: 100}); this.append(artist, 0, 4, 1, 1, GridLayout.Aligment.LeftCenter); this.append(song, 1, 4, 1, 1, GridLayout.Aligment.LeftCenter); this.append(album, 2, 4, 1, 1, GridLayout.Aligment.LeftCenter); this.append(spacer, 0, 6, 3, 1, GridLayout.Aligment.LeftCenter); this.append(progress, 1, 5, 1, 1, GridLayout.Aligment.CenterCenter); + this.append(volume, 1, 7, 1, 1, GridLayout.Aligment.CenterCenter); this._uncyclic.push(this._infoModels.artist.destructor.bind(this._infoModels.artist)); this._uncyclic.push(this._infoModels.song.destructor.bind(this._infoModels.song)); diff --git a/lorgar/views/slider.js b/lorgar/views/slider.js new file mode 100644 index 0000000..328947f --- /dev/null +++ b/lorgar/views/slider.js @@ -0,0 +1,137 @@ +"use strict"; +(function() { + var moduleName = "views/slider"; + + var deps = []; + deps.push("views/view"); + + define(moduleName, deps, function() { + var View = require("views/view"); + + var Slider = View.inherit({ + className: "Slider", + constructor: function(controller, options) { + var base = { + minHeight: 10, + maxHeight: 10 + }; + W.extend(base, options) + var el = document.createElement("div"); + this._createBars(); + View.fn.constructor.call(this, controller, base, el); + + this._seeking = false; + this._createProxy(); + + this._f.on("value", this._onValue, this); + this._f.on("enabled", this._onEnabled, this); + + this._e.style.backgroundColor = "#eeeeee"; + this._e.appendChild(this._value); + + if (this._f.enabled) { + this._e.addEventListener("mousedown", this._proxy.onMouseDown, false); + this._e.addEventListener("touchstart", W.touchToMouse, false); + this._e.addEventListener("touchmove", W.touchToMouse, false); + this._e.addEventListener("touchend", W.touchToMouse, false); + } + }, + destructor: function() { + if (this._seeking) { + window.removeEventListener("mouseup", this._proxy.onMouseUp); + window.removeEventListener("mousemove", this._proxy.onMouseMove); + } + if (this._f.enabled) { + this._e.removeEventListener("mousedown", this._proxy.onMouseDown); + this._e.removeEventListener("touchstart", W.touchToMouse); + this._e.removeEventListener("touchmove", W.touchToMouse); + this._e.removeEventListener("touchend", W.touchToMouse); + } + + this._f.off("value", this._value, this); + this._f.off("enabled", this._onEnabled, this); + + View.fn.destructor.call(this); + }, + _createBars: function() { + this._value = document.createElement("div"); + + this._value.style.backgroundColor = View.theme.primaryColor || "#ff0000"; + this._value.style.height = "100%"; + this._value.style.width = "0"; + this._value.style.position = "absolute"; + this._value.style.top = "0"; + this._value.style.left = "0"; + }, + _createProxy: function () { + this._proxy = { + onMouseDown: this._onMouseDown.bind(this), + onMouseUp: this._onMouseUp.bind(this), + onMouseMove: this._onMouseMove.bind(this) + } + }, + _onData: function() { + this._onValue(this._f.value); + }, + _onEnabled: function(enabled) { + if (enabled) { + this._e.addEventListener("mousedown", this._proxy.onMouseDown, false); + this._e.addEventListener("touchstart", W.touchToMouse, false); + this._e.addEventListener("touchmove", W.touchToMouse, false); + this._e.addEventListener("touchend", W.touchToMouse, false); + } else { + if (this._seeking) { + this._onMouseUp(); + } + + this._e.removeEventListener("mousedown", this._proxy.onMouseDown); + this._e.removeEventListener("touchstart", W.touchToMouse); + this._e.removeEventListener("touchmove", W.touchToMouse); + this._e.removeEventListener("touchend", W.touchToMouse); + } + }, + _onMouseDown: function(e) { + if (e.which === 1) { + window.addEventListener("mouseup", this._proxy.onMouseUp); + window.addEventListener("mousemove", this._proxy.onMouseMove); + this._seeking = true; + + this._ap = this.getAbsolutePosition(); + var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0); + var nSeek = seek / this._w; + if (this._seek !== nSeek) { + this._seek = nSeek; + this._f.setValue(this._seek); + } + } + }, + _onMouseMove: function(e) { + var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0); + var nSeek = seek / this._w; + if (this._seek !== nSeek) { + this._seek = nSeek; + this._f.setValue(this._seek); + } + }, + _onMouseUp: function() { + delete this._ap; + delete this._seek; + + this._seeking = false; + window.removeEventListener("mouseup", this._proxy.onMouseUp); + window.removeEventListener("mousemove", this._proxy.onMouseMove); + }, + _onValue: function(pb) { + this._value.style.width = pb * 100 + "%"; + }, + _resetTheme: function() { + View.fn._resetTheme.call(this); + + this._value.style.backgroundColor = View.theme.primaryColor || "#ff0000"; + } + }); + + return Slider; + }) +})(); + diff --git a/lorgar/views/songProgress.js b/lorgar/views/songProgress.js index 2f4fc44..0639b07 100644 --- a/lorgar/views/songProgress.js +++ b/lorgar/views/songProgress.js @@ -3,12 +3,12 @@ var moduleName = "views/songProgress"; var deps = []; - deps.push("views/view"); + deps.push("views/slider"); define(moduleName, deps, function() { - var View = require("views/view"); + var Slider = require("views/slider"); - var SongProgress = View.inherit({ + var SongProgress = Slider.inherit({ className: "SongProgress", constructor: function(controller, options) { var base = { @@ -16,116 +16,51 @@ maxHeight: 10 }; W.extend(base, options) - var el = document.createElement("div"); - this._createBars(); - View.fn.constructor.call(this, controller, base, el); - - this._seeking = false; - this._createProxy(); + Slider.fn.constructor.call(this, controller, base); this._f.on("load", this._onLoad, this); - this._f.on("playback", this._onPlayback, this); - this._e.style.backgroundColor = "#eeeeee"; - this._e.appendChild(this._loadProgress); - this._e.appendChild(this._playbackProgress); - - this._e.addEventListener("mousedown", this._proxy.onMouseDown, false); - this._e.addEventListener("touchstart", W.touchToMouse, false); - this._e.addEventListener("touchmove", W.touchToMouse, false); - this._e.addEventListener("touchend", W.touchToMouse, false); + this._e.insertBefore(this._loadProgress, this._value); }, destructor: function() { - if (this._seeking) { - window.removeEventListener("mouseup", this._proxy.onMouseUp); - window.removeEventListener("mousemove", this._proxy.onMouseMove); - } - - this._e.removeEventListener("mousedown", this._proxy.onMouseDown); - this._e.removeEventListener("touchstart", W.touchToMouse); - this._e.removeEventListener("touchmove", W.touchToMouse); - this._e.removeEventListener("touchend", W.touchToMouse); - this._f.off("load", this._onLoad, this); - this._f.off("playback", this._onPlayback, this); - View.fn.destructor.call(this); + Slider.fn.destructor.call(this); }, _createBars: function() { + Slider.fn._createBars.call(this); this._loadProgress = document.createElement("div"); - this._playbackProgress = document.createElement("div"); - this._loadProgress.style.backgroundColor = View.theme.secondaryColor || "#ffff00"; + this._loadProgress.style.backgroundColor = Slider.theme.secondaryColor || "#ffff00"; this._loadProgress.style.height = "100%"; this._loadProgress.style.width = "0"; this._loadProgress.style.position = "absolute"; this._loadProgress.style.top = "0"; this._loadProgress.style.left = "0"; - - this._playbackProgress.style.backgroundColor = View.theme.primaryColor || "#ff0000"; - this._playbackProgress.style.height = "100%"; - this._playbackProgress.style.width = "0"; - this._playbackProgress.style.position = "absolute"; - this._playbackProgress.style.top = "0"; - this._playbackProgress.style.left = "0"; - }, - _createProxy: function () { - this._proxy = { - onMouseDown: this._onMouseDown.bind(this), - onMouseUp: this._onMouseUp.bind(this), - onMouseMove: this._onMouseMove.bind(this) - } }, _onData: function() { + Slider.fn._onData.call(this); this._onLoad(this._f.load); - this._onPlayback(this._f.playback); }, _onLoad: function(load) { this._loadProgress.style.width = load * 100 + "%"; }, _onMouseDown: function(e) { if (e.which === 1) { - window.addEventListener("mouseup", this._proxy.onMouseUp); - window.addEventListener("mousemove", this._proxy.onMouseMove); - this._seeking = true; - this._f.trigger("seekingStart"); - - this._ap = this.getAbsolutePosition(); - var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0); - var nSeek = seek / this._w; - if (this._seek !== nSeek) { - this._seek = nSeek; - this._f.trigger("seek", this._seek); - } } - }, - _onMouseMove: function(e) { - var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0); - var nSeek = seek / this._w; - if (this._seek !== nSeek) { - this._seek = nSeek; - this._f.trigger("seek", this._seek); - } - }, - _onMouseUp: function() { - delete this._ap; - delete this._seek; - this._f.trigger("seekingEnd", this._f.playback); - this._seeking = false; - window.removeEventListener("mouseup", this._proxy.onMouseUp); - window.removeEventListener("mousemove", this._proxy.onMouseMove); + Slider.fn._onMouseDown.call(this, e); }, - _onPlayback: function(pb) { - this._playbackProgress.style.width = pb * 100 + "%"; + _onMouseUp: function(e) { + Slider.fn._onMouseUp.call(this, e); + this._f.trigger("seekingEnd", this._f.value); }, _resetTheme: function() { - View.fn._resetTheme.call(this); + Slider.fn._resetTheme.call(this); - this._loadProgress.style.backgroundColor = View.theme.secondaryColor || "#ffff00" - this._playbackProgress.style.backgroundColor = View.theme.primaryColor || "#ff0000"; + this._loadProgress.style.backgroundColor = Slider.theme.secondaryColor || "#ffff00" } });