From 762e6e15a61d4b889107641cd0e960969bf2f225 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Fri, 30 Sep 2016 18:43:11 +0200 Subject: [PATCH 01/13] Ignore idea project files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 5cfed66b..6c1e44f1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ /lib .sass-cache /styles + +#Ide files +.idea/* +.idea From cab16de983e568c8dbf6e39fc339213ea0c79084 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Fri, 30 Sep 2016 20:49:33 +0200 Subject: [PATCH 02/13] Play rate can be changed Audio get desynchronised due to play time scheduling when the play button is clicked --- src/Playlist.js | 15 ++++++++++++++- src/Playout.js | 29 +++++++++++++++++------------ src/Track.js | 8 ++++++++ src/app.js | 1 + 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/Playlist.js b/src/Playlist.js index b76b6e1f..796e2872 100644 --- a/src/Playlist.js +++ b/src/Playlist.js @@ -134,6 +134,10 @@ export default class { setUpEventEmitter() { let ee = this.ee; + ee.on('speedchange', (speed) => { + this.setSpeed(speed); + }); + ee.on('select', (start, end, track) => { if (this.isPlaying()) { this.lastSeeked = start; @@ -477,6 +481,13 @@ export default class { }); } + setSpeed(speed){ + this.speed = (speed>=0.5 && speed <= 2)? speed : 1; + this.tracks.forEach((track) => { + track.setSpeed(this.speed); + }) + } + muteTrack(track) { let mutedList = this.mutedTracks; let index = mutedList.indexOf(track); @@ -700,9 +711,11 @@ export default class { cursorPos = cursorPos || this.cursor; elapsed = currentTime - this.lastDraw; + //console.log(elapsed); if (this.isPlaying()) { - playbackSeconds = cursorPos + elapsed; + //console.log("speed " + this.speed); + playbackSeconds = cursorPos + elapsed*this.speed; this.ee.emit('timeupdate', playbackSeconds); this.animationRequest = window.requestAnimationFrame( this.updateEditor.bind(this, playbackSeconds) diff --git a/src/Playout.js b/src/Playout.js index c89b7211..e10c2328 100644 --- a/src/Playout.js +++ b/src/Playout.js @@ -1,6 +1,6 @@ 'use strict'; -import {createFadeIn, createFadeOut} from 'fade-maker'; +import {createFadeIn, createFadeOut} from "fade-maker"; const FADEIN = "FadeIn"; const FADEOUT = "FadeOut"; @@ -14,7 +14,7 @@ export default class { this.destination = this.ac.destination; } - applyFade(type, start, duration, shape="logarithmic") { + applyFade(type, start, duration, shape = "logarithmic") { if (type === FADEIN) { createFadeIn(this.fadeGain.gain, shape, start, duration); } @@ -26,11 +26,11 @@ export default class { } } - applyFadeIn(start, duration, shape="logarithmic") { + applyFadeIn(start, duration, shape = "logarithmic") { this.applyFade(FADEIN, start, duration, shape); } - applyFadeOut(start, duration, shape="logarithmic") { + applyFadeOut(start, duration, shape = "logarithmic") { this.applyFade(FADEOUT, start, duration, shape); } @@ -42,7 +42,7 @@ export default class { return this.buffer.duration; } - setAudioContext(audioContext){ + setAudioContext(audioContext) { this.ac = audioContext; this.destination = this.ac.destination; } @@ -73,6 +73,7 @@ export default class { } }); + this.setSpeed(1); this.fadeGain = this.ac.createGain(); //used for track volume slider this.volumeGain = this.ac.createGain(); @@ -93,7 +94,7 @@ export default class { this.volumeGain && (this.volumeGain.gain.value = level); } - setShouldPlay(bool){ + setShouldPlay(bool) { this.shouldPlayGain && (this.shouldPlayGain.gain.value = bool ? 1 : 0); } @@ -101,17 +102,21 @@ export default class { this.masterGain && (this.masterGain.gain.value = level); } + setSpeed(speed) { + this.source && (this.source.playbackRate.value = speed); + } + /* - source.start is picky when passing the end time. - If rounding error causes a number to make the source think - it is playing slightly more samples than it has it won't play at all. - Unfortunately it doesn't seem to work if you just give it a start time. - */ + source.start is picky when passing the end time. + If rounding error causes a number to make the source think + it is playing slightly more samples than it has it won't play at all. + Unfortunately it doesn't seem to work if you just give it a start time. + */ play(when, start, duration) { this.source.start(when, start, duration); } - stop(when=0) { + stop(when = 0) { this.source && this.source.stop(when); } } diff --git a/src/Track.js b/src/Track.js index 93cc56ec..61c363b5 100644 --- a/src/Track.js +++ b/src/Track.js @@ -209,6 +209,11 @@ export default class { this.playout.setMasterGainLevel(level); } + setSpeed(speed){ + this.speed = speed; + this.playout.setSpeed(speed); + } + /* startTime, endTime in seconds (float). segment is for a highlighted section in the UI. @@ -271,6 +276,9 @@ export default class { start = start + this.cueIn; relPos = startTime - this.startTime; + console.log("now "+ now); + console.log("start " + start); + sourcePromise = playoutSystem.setUpSource(); //param relPos: cursor position in seconds relative to this track. diff --git a/src/app.js b/src/app.js index aaf2160c..f11f12d0 100644 --- a/src/app.js +++ b/src/app.js @@ -42,6 +42,7 @@ export function init(options={}, ee=EventEmitter()) { let playlist = new Playlist(); playlist.setSampleRate(config.sampleRate); + playlist.setSpeed(1); playlist.setSamplesPerPixel(config.samplesPerPixel); playlist.setAudioContext(config.ac); playlist.setEventEmitter(ee); From c102743625b8ced92ccad810883bd0a1805f8806 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Fri, 30 Sep 2016 23:48:11 +0200 Subject: [PATCH 03/13] Now able to change de audio speed #19 The pitch is altered for now ... --- src/Playlist.js | 2 ++ src/Playout.js | 5 +++-- src/Track.js | 20 +++++++++++--------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Playlist.js b/src/Playlist.js index 796e2872..4ccb06f5 100644 --- a/src/Playlist.js +++ b/src/Playlist.js @@ -324,6 +324,8 @@ export default class { track.setState(this.getState()); track.setStartTime(start); track.setPlayout(playout); + track.setSpeed(1); + track.setGainLevel(gain); diff --git a/src/Playout.js b/src/Playout.js index e10c2328..ef9ce6b8 100644 --- a/src/Playout.js +++ b/src/Playout.js @@ -73,7 +73,6 @@ export default class { } }); - this.setSpeed(1); this.fadeGain = this.ac.createGain(); //used for track volume slider this.volumeGain = this.ac.createGain(); @@ -103,7 +102,8 @@ export default class { } setSpeed(speed) { - this.source && (this.source.playbackRate.value = speed); + console.log("callaed ! " + speed); + this.speed = speed; } /* @@ -114,6 +114,7 @@ export default class { */ play(when, start, duration) { this.source.start(when, start, duration); + this.source.playbackRate.value = this.speed; } stop(when = 0) { diff --git a/src/Track.js b/src/Track.js index 61c363b5..9f2f231a 100644 --- a/src/Track.js +++ b/src/Track.js @@ -222,6 +222,8 @@ export default class { is either stopped or plays out naturally. */ schedulePlay(now, startTime, endTime, options) { + //console.log("now" + now + " startTime" + startTime + " endTime " + endTime + ' duration '+ this.duration); + var start, duration, relPos, @@ -234,7 +236,7 @@ export default class { shouldPlay: true, masterGain: 1, isOffline: false - } + }; options = _assign(defaultOptions, options); @@ -252,17 +254,19 @@ export default class { //the track starts in the future or on the cursor position if (this.startTime >= startTime) { start = 0; - when = when + this.startTime - startTime; //schedule additional delay for this audio node. + when = when + (this.startTime - startTime)/this.speed; //schedule additional delay for this audio node. if (endTime) { segment = segment - (this.startTime - startTime); duration = Math.min(segment, this.duration); + } else { duration = this.duration; } } else { + console.log('HEre !! '); start = startTime - this.startTime; if (endTime) { @@ -276,9 +280,6 @@ export default class { start = start + this.cueIn; relPos = startTime - this.startTime; - console.log("now "+ now); - console.log("start " + start); - sourcePromise = playoutSystem.setUpSource(); //param relPos: cursor position in seconds relative to this track. @@ -290,20 +291,20 @@ export default class { //only apply fade if it's ahead of the cursor. if (relPos < fade.end) { if (relPos <= fade.start) { - startTime = now + (fade.start - relPos); + startTime = now + (fade.start - relPos)/this.speed; duration = fade.end - fade.start; } else if (relPos > fade.start && relPos < fade.end) { - startTime = now - (relPos - fade.start); + startTime = now - (relPos - fade.start)/this.speed; duration = fade.end - fade.start; } switch (fade.type) { case FADEIN: - playoutSystem.applyFadeIn(startTime, duration, fade.shape); + playoutSystem.applyFadeIn(startTime, duration/this.speed, fade.shape); break; case FADEOUT: - playoutSystem.applyFadeOut(startTime, duration, fade.shape); + playoutSystem.applyFadeOut(startTime, duration/this.speed, fade.shape); break; default: throw new Error("Invalid fade type saved on track."); @@ -311,6 +312,7 @@ export default class { } }); + playoutSystem.setSpeed(this.speed); playoutSystem.setVolumeGainLevel(this.gain); playoutSystem.setShouldPlay(options.shouldPlay); playoutSystem.setMasterGainLevel(options.masterGain); From d2cedaaaa0f106206195d585e367eef7f5c99df8 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sat, 1 Oct 2016 19:20:07 +0200 Subject: [PATCH 04/13] Remove console log --- src/Track.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Track.js b/src/Track.js index 9f2f231a..49b12ae9 100644 --- a/src/Track.js +++ b/src/Track.js @@ -266,7 +266,6 @@ export default class { } } else { - console.log('HEre !! '); start = startTime - this.startTime; if (endTime) { From 73df1a9c473c1e45f2044a89640181c9bb5aa3a5 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 09:50:19 +0200 Subject: [PATCH 05/13] Updated Readme for #19 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9c3de3fd..22a2d768 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,7 @@ An example of using the event emitter to control the playlist can be found in [/ | `mastervolumechange` | `volume` | Set a new master volume `volume` (0-100) | | `select` | `start, end, track:optional` | Seek to the start time or start/end selection optionally with active track `track`. | | `startaudiorendering` | (`wav | buffer`) | Request for a downloadable file or web Audio buffer that represent the current work | +| `speedchange` | `speed` | Change de playback speed. In memory until the next play action. If currently playing doesn't do anything | #### Events to Listen to From d987d1ddfa05b36ae6ffc54c5c10006e9c18b0ab Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 11:10:17 +0200 Subject: [PATCH 06/13] Playback speed change improvements. --- src/Playlist.js | 36 +++++++++++++++++------------------- src/app.js | 10 +++++----- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/Playlist.js b/src/Playlist.js index 4ccb06f5..a07fe592 100644 --- a/src/Playlist.js +++ b/src/Playlist.js @@ -1,23 +1,17 @@ 'use strict'; -import _defaults from 'lodash.defaults'; - -import h from 'virtual-dom/h'; -import diff from 'virtual-dom/diff'; -import patch from 'virtual-dom/patch'; - -import {pixelsToSeconds} from './utils/conversions'; - -import LoaderFactory from './track/loader/LoaderFactory'; - -import ScrollHook from './render/ScrollHook'; - -import TimeScale from './TimeScale'; -import Track from './Track'; -import Playout from './Playout'; - -import RecorderWorker from 'worker!./track/recorderWorker.js'; -import ExportWavWorker from 'worker!./utils/exportWavWorker.js'; +import _defaults from "lodash.defaults"; +import h from "virtual-dom/h"; +import diff from "virtual-dom/diff"; +import patch from "virtual-dom/patch"; +import {pixelsToSeconds} from "./utils/conversions"; +import LoaderFactory from "./track/loader/LoaderFactory"; +import ScrollHook from "./render/ScrollHook"; +import TimeScale from "./TimeScale"; +import Track from "./Track"; +import Playout from "./Playout"; +import RecorderWorker from "worker!./track/recorderWorker.js"; +import ExportWavWorker from "worker!./utils/exportWavWorker.js"; export default class { @@ -487,7 +481,11 @@ export default class { this.speed = (speed>=0.5 && speed <= 2)? speed : 1; this.tracks.forEach((track) => { track.setSpeed(this.speed); - }) + }); + + if (this.isPlaying()) + this.restartPlayFrom(this.playbackSeconds); + this.ee.emit('speedchanged', this.speed); } muteTrack(track) { diff --git a/src/app.js b/src/app.js index f11f12d0..daac889f 100644 --- a/src/app.js +++ b/src/app.js @@ -1,7 +1,7 @@ -import _assign from 'lodash.assign'; -import createElement from 'virtual-dom/create-element'; -import EventEmitter from 'event-emitter'; -import Playlist from './Playlist'; +import _assign from "lodash.assign"; +import createElement from "virtual-dom/create-element"; +import EventEmitter from "event-emitter"; +import Playlist from "./Playlist"; export function init(options={}, ee=EventEmitter()) { @@ -42,7 +42,6 @@ export function init(options={}, ee=EventEmitter()) { let playlist = new Playlist(); playlist.setSampleRate(config.sampleRate); - playlist.setSpeed(1); playlist.setSamplesPerPixel(config.samplesPerPixel); playlist.setAudioContext(config.ac); playlist.setEventEmitter(ee); @@ -57,6 +56,7 @@ export function init(options={}, ee=EventEmitter()) { playlist.setMono(config.mono); playlist.setShowTimeScale(config.timescale); playlist.setSeekStyle(config.seekStyle); + playlist.setSpeed(1); //take care of initial virtual dom rendering. let tree = playlist.render(); From a7466fdb5fb8210afbc3492e75316ba19257eed1 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 11:14:54 +0200 Subject: [PATCH 07/13] Updated dist and readme --- README.md | 3 +- dist/waveform-playlist/js/emitter.js | 389 ++++++++++--------- dist/waveform-playlist/web-audio-editor.html | 122 +++--- ghpages/_examples/99webaudioeditor.html | 122 +++--- ghpages/js/emitter.js | 389 ++++++++++--------- 5 files changed, 542 insertions(+), 483 deletions(-) diff --git a/README.md b/README.md index 22a2d768..ef007263 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ An example of using the event emitter to control the playlist can be found in [/ | `mastervolumechange` | `volume` | Set a new master volume `volume` (0-100) | | `select` | `start, end, track:optional` | Seek to the start time or start/end selection optionally with active track `track`. | | `startaudiorendering` | (`wav | buffer`) | Request for a downloadable file or web Audio buffer that represent the current work | -| `speedchange` | `speed` | Change de playback speed. In memory until the next play action. If currently playing doesn't do anything | +| `speedchange` | `speed` | Change de playback speed.| #### Events to Listen to @@ -282,6 +282,7 @@ An example of using the event emitter to control the playlist can be found in [/ | `loadprogress` | `percent, src` | Loading audio `src` has loaded percent `percent` (0-100) | | `finished` | _none_ | Event fired when cursor ( while playing ) reaches the end (maximum duration) | | `audiorenderingfinished` | `type, data` | Return the result of the rendering in the desired format. `type` can be `buffer` or `wav` and can be used to dertermine the `data` type. When `type` is `wav`, data is a `blob` object that represent the wav file. | +| `speedchanged` | `speed` | When speed is changed, return the value applied to tracks. | ## Tests diff --git a/dist/waveform-playlist/js/emitter.js b/dist/waveform-playlist/js/emitter.js index 3450604b..27e065ee 100644 --- a/dist/waveform-playlist/js/emitter.js +++ b/dist/waveform-playlist/js/emitter.js @@ -1,7 +1,7 @@ /* * This script is provided to give an example how the playlist can be controlled using the event emitter. * This enables projects to create/control the useability of the project. -*/ + */ var ee = playlist.getEventEmitter(); var $container = $("body"); var $timeFormat = $container.find('.time-format'); @@ -16,319 +16,336 @@ var audioPos = 0; var downloadUrl = undefined; function toggleActive(node) { - var active = node.parentNode.querySelectorAll('.active'); - var i = 0, len = active.length; + var active = node.parentNode.querySelectorAll('.active'); + var i = 0, len = active.length; - for (; i < len; i++) { - active[i].classList.remove('active'); - } + for (; i < len; i++) { + active[i].classList.remove('active'); + } - node.classList.toggle('active'); + node.classList.toggle('active'); } function cueFormatters(format) { - function clockFormat(seconds, decimals) { - var hours, - minutes, - secs, - result; - - hours = parseInt(seconds / 3600, 10) % 24; - minutes = parseInt(seconds / 60, 10) % 60; - secs = seconds % 60; - secs = secs.toFixed(decimals); - - result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (secs < 10 ? "0" + secs : secs); - - return result; - } - - var formats = { - "seconds": function (seconds) { - return seconds.toFixed(0); - }, - "thousandths": function (seconds) { - return seconds.toFixed(3); - }, - "hh:mm:ss": function (seconds) { - return clockFormat(seconds, 0); - }, - "hh:mm:ss.u": function (seconds) { - return clockFormat(seconds, 1); - }, - "hh:mm:ss.uu": function (seconds) { - return clockFormat(seconds, 2); - }, - "hh:mm:ss.uuu": function (seconds) { - return clockFormat(seconds, 3); + function clockFormat(seconds, decimals) { + var hours, + minutes, + secs, + result; + + hours = parseInt(seconds / 3600, 10) % 24; + minutes = parseInt(seconds / 60, 10) % 60; + secs = seconds % 60; + secs = secs.toFixed(decimals); + + result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (secs < 10 ? "0" + secs : secs); + + return result; } - }; - return formats[format]; + var formats = { + "seconds": function (seconds) { + return seconds.toFixed(0); + }, + "thousandths": function (seconds) { + return seconds.toFixed(3); + }, + "hh:mm:ss": function (seconds) { + return clockFormat(seconds, 0); + }, + "hh:mm:ss.u": function (seconds) { + return clockFormat(seconds, 1); + }, + "hh:mm:ss.uu": function (seconds) { + return clockFormat(seconds, 2); + }, + "hh:mm:ss.uuu": function (seconds) { + return clockFormat(seconds, 3); + } + }; + + return formats[format]; } function updateSelect(start, end) { - if (start < end) { - $('.btn-trim-audio').removeClass('disabled'); - } - else { - $('.btn-trim-audio').addClass('disabled'); - } - - $audioStart.val(cueFormatters(format)(start)); - $audioEnd.val(cueFormatters(format)(end)); - - startTime = start; - endTime = end; + if (start < end) { + $('.btn-trim-audio').removeClass('disabled'); + } + else { + $('.btn-trim-audio').addClass('disabled'); + } + + $audioStart.val(cueFormatters(format)(start)); + $audioEnd.val(cueFormatters(format)(end)); + + startTime = start; + endTime = end; } function updateTime(time) { - $time.html(cueFormatters(format)(time)); + $time.html(cueFormatters(format)(time)); - audioPos = time; + audioPos = time; } updateSelect(startTime, endTime); updateTime(audioPos); - /* -* Code below sets up events to send messages to the playlist. -*/ -$container.on("click", ".btn-playlist-state-group", function() { - //reset these for now. - $('.btn-fade-state-group').addClass('hidden'); - $('.btn-select-state-group').addClass('hidden'); - - if ($('.btn-select').hasClass('active')) { - $('.btn-select-state-group').removeClass('hidden'); - } + * Code below sets up events to send messages to the playlist. + */ +$container.on("click", ".btn-playlist-state-group", function () { + //reset these for now. + $('.btn-fade-state-group').addClass('hidden'); + $('.btn-select-state-group').addClass('hidden'); + + if ($('.btn-select').hasClass('active')) { + $('.btn-select-state-group').removeClass('hidden'); + } - if ($('.btn-fadein').hasClass('active') || $('.btn-fadeout').hasClass('active')) { - $('.btn-fade-state-group').removeClass('hidden'); - } + if ($('.btn-fadein').hasClass('active') || $('.btn-fadeout').hasClass('active')) { + $('.btn-fade-state-group').removeClass('hidden'); + } }); -$container.on("click", ".btn-play", function() { - ee.emit("play"); +$container.on("click", ".btn-play", function () { + ee.emit("play"); }); -$container.on("click", ".btn-pause", function() { - ee.emit("pause"); +$container.on("click", ".btn-pause", function () { + ee.emit("pause"); }); -$container.on("click", ".btn-stop", function() { - ee.emit("stop"); +$container.on("click", ".btn-stop", function () { + ee.emit("stop"); }); -$container.on("click", ".btn-rewind", function() { - ee.emit("rewind"); +$container.on("click", ".btn-rewind", function () { + ee.emit("rewind"); }); -$container.on("click", ".btn-fast-forward", function() { - ee.emit("fastforward"); +$container.on("click", ".btn-fast-forward", function () { + ee.emit("fastforward"); }); -$container.on("click", ".btn-record", function() { - ee.emit("record"); +$container.on("click", ".btn-record", function () { + ee.emit("record"); }); //track interaction states -$container.on("click", ".btn-cursor", function() { - ee.emit("statechange", "cursor"); - toggleActive(this); +$container.on("click", ".btn-cursor", function () { + ee.emit("statechange", "cursor"); + toggleActive(this); }); -$container.on("click", ".btn-select", function() { - ee.emit("statechange", "select"); - toggleActive(this); +$container.on("click", ".btn-select", function () { + ee.emit("statechange", "select"); + toggleActive(this); }); -$container.on("click", ".btn-shift", function() { - ee.emit("statechange", "shift"); - toggleActive(this); +$container.on("click", ".btn-shift", function () { + ee.emit("statechange", "shift"); + toggleActive(this); }); -$container.on("click", ".btn-fadein", function() { - ee.emit("statechange", "fadein"); - toggleActive(this); +$container.on("click", ".btn-fadein", function () { + ee.emit("statechange", "fadein"); + toggleActive(this); }); -$container.on("click", ".btn-fadeout", function() { - ee.emit("statechange", "fadeout"); - toggleActive(this); +$container.on("click", ".btn-fadeout", function () { + ee.emit("statechange", "fadeout"); + toggleActive(this); }); //fade types -$container.on("click", ".btn-logarithmic", function() { - ee.emit("fadetype", "logarithmic"); - toggleActive(this); +$container.on("click", ".btn-logarithmic", function () { + ee.emit("fadetype", "logarithmic"); + toggleActive(this); }); -$container.on("click", ".btn-linear", function() { - ee.emit("fadetype", "linear"); - toggleActive(this); +$container.on("click", ".btn-linear", function () { + ee.emit("fadetype", "linear"); + toggleActive(this); }); -$container.on("click", ".btn-scurve", function() { - ee.emit("fadetype", "sCurve"); - toggleActive(this); +$container.on("click", ".btn-scurve", function () { + ee.emit("fadetype", "sCurve"); + toggleActive(this); }); -$container.on("click", ".btn-exponential", function() { - ee.emit("fadetype", "exponential"); - toggleActive(this); +$container.on("click", ".btn-exponential", function () { + ee.emit("fadetype", "exponential"); + toggleActive(this); }); //zoom buttons -$container.on("click", ".btn-zoom-in", function() { - ee.emit("zoomin"); +$container.on("click", ".btn-zoom-in", function () { + ee.emit("zoomin"); }); -$container.on("click", ".btn-zoom-out", function() { - ee.emit("zoomout"); +$container.on("click", ".btn-zoom-out", function () { + ee.emit("zoomout"); }); -$container.on("click", ".btn-trim-audio", function() { - ee.emit("trim"); +$container.on("click", ".btn-trim-audio", function () { + ee.emit("trim"); }); -$container.on("click", ".btn-info", function() { - console.log(playlist.getInfo()); +$container.on("click", ".btn-info", function () { + console.log(playlist.getInfo()); }); $container.on("click", ".btn-download", function () { - ee.emit('startaudiorendering', 'wav'); + ee.emit('startaudiorendering', 'wav'); }); $container.on("click", ".btn-seektotime", function () { - var time = parseInt(document.getElementById("seektime").value, 10); - ee.emit("select", time, time); + var time = parseInt(document.getElementById("seektime").value, 10); + ee.emit("select", time, time); }); $container.on("change", ".select-seek-style", function (node) { - playlist.setSeekStyle(node.target.value); + playlist.setSeekStyle(node.target.value); }); //track drop -$container.on("dragenter", ".track-drop", function(e) { - e.preventDefault(); - e.target.classList.add("drag-enter"); +$container.on("dragenter", ".track-drop", function (e) { + e.preventDefault(); + e.target.classList.add("drag-enter"); }); -$container.on("dragover", ".track-drop", function(e) { - e.preventDefault(); +$container.on("dragover", ".track-drop", function (e) { + e.preventDefault(); }); -$container.on("dragleave", ".track-drop", function(e) { - e.preventDefault(); - e.target.classList.remove("drag-enter"); +$container.on("dragleave", ".track-drop", function (e) { + e.preventDefault(); + e.target.classList.remove("drag-enter"); }); -$container.on("drop", ".track-drop", function(e) { - e.preventDefault(); - e.target.classList.remove("drag-enter"); +$container.on("drop", ".track-drop", function (e) { + e.preventDefault(); + e.target.classList.remove("drag-enter"); - var dropEvent = e.originalEvent; + var dropEvent = e.originalEvent; - for (var i = 0; i < dropEvent.dataTransfer.files.length; i++) { - ee.emit("newtrack", dropEvent.dataTransfer.files[i]); - } + for (var i = 0; i < dropEvent.dataTransfer.files.length; i++) { + ee.emit("newtrack", dropEvent.dataTransfer.files[i]); + } +}); + +$container.on("change", ".time-format", function (e) { + format = $timeFormat.val(); + + updateSelect(startTime, endTime); + updateTime(audioPos); }); -$container.on("change", ".time-format", function(e) { - format = $timeFormat.val(); +$container.on("input change", ".master-gain", function (node) { + ee.emit("mastervolumechange", node.target.value); +}); - updateSelect(startTime, endTime); - updateTime(audioPos); +$container.on("input change", ".speed-slider", function (node) { + document.getElementById('speedValue').value = node.target.value; + ee.emit("speedchange", node.target.value); + displaySoundStatus("Playback speed is now " + node.target.value + "x !"); }); -$container.on("input change", ".master-gain", function(node){ - ee.emit("mastervolumechange", node.target.value); +$container.on("click", ".btn-speed-change", function () { + var value = document.getElementById('speedValue').value; + ee.emit("speedchange", value); + displaySoundStatus("Playback speed is now " + value + "x !"); }); function displaySoundStatus(status) { - $(".sound-status").html(status); + $(".sound-status").html(status); } function displayLoadingData(data) { - var info = $("
").append(data); - $(".loading-data").append(info); + var info = $("
").append(data); + $(".loading-data").append(info); } function displayDownloadLink(link) { - var dateString = (new Date()).toISOString(); - var $link = $("", { - 'href': link, - 'download': 'waveformplaylist' + dateString + '.wav', - 'text': 'Download mix ' + dateString, - 'class': 'btn btn-small btn-download-link' - }); - - $('.btn-download-link').remove(); - $('.btn-download').after($link); + var dateString = (new Date()).toISOString(); + var $link = $("", { + 'href': link, + 'download': 'waveformplaylist' + dateString + '.wav', + 'text': 'Download mix ' + dateString, + 'class': 'btn btn-small btn-download-link' + }); + + $('.btn-download-link').remove(); + $('.btn-download').after($link); } /* -* Code below receives updates from the playlist. -*/ + * Code below receives updates from the playlist. + */ ee.on("select", updateSelect); ee.on("timeupdate", updateTime); -ee.on("mute", function(track) { - displaySoundStatus("Mute button pressed for " + track.name); +ee.on("mute", function (track) { + displaySoundStatus("Mute button pressed for " + track.name); }); -ee.on("solo", function(track) { - displaySoundStatus("Solo button pressed for " + track.name); +ee.on("solo", function (track) { + displaySoundStatus("Solo button pressed for " + track.name); }); -ee.on("volumechange", function(volume, track) { - displaySoundStatus(track.name + " now has volume " + volume + "."); +ee.on("volumechange", function (volume, track) { + displaySoundStatus(track.name + " now has volume " + volume + "."); }); -ee.on("mastervolumechange", function(volume) { - displaySoundStatus("Master volume now has volume " + volume + "."); +ee.on("mastervolumechange", function (volume) { + displaySoundStatus("Master volume now has volume " + volume + "."); }); var audioStates = ["uninitialized", "loading", "decoding", "finished"]; -ee.on("audiorequeststatechange", function(state, src) { - var name = src; +ee.on("audiorequeststatechange", function (state, src) { + var name = src; - if (src instanceof File) { - name = src.name; - } + if (src instanceof File) { + name = src.name; + } - displayLoadingData("Track " + name + " is in state " + audioStates[state]); + displayLoadingData("Track " + name + " is in state " + audioStates[state]); }); -ee.on("loadprogress", function(percent, src) { - var name = src; +ee.on("loadprogress", function (percent, src) { + var name = src; - if (src instanceof File) { - name = src.name; - } + if (src instanceof File) { + name = src.name; + } - displayLoadingData("Track " + name + " has loaded " + percent + "%"); + displayLoadingData("Track " + name + " has loaded " + percent + "%"); }); ee.on('audiorenderingfinished', function (type, data) { - if (type == 'wav'){ - if (downloadUrl) { - window.URL.revokeObjectURL(downloadUrl); - } + if (type == 'wav') { + if (downloadUrl) { + window.URL.revokeObjectURL(downloadUrl); + } - downloadUrl = window.URL.createObjectURL(data); - displayDownloadLink(downloadUrl); - } + downloadUrl = window.URL.createObjectURL(data); + displayDownloadLink(downloadUrl); + } }); ee.on('finished', function () { - console.log("The cursor has reached the end of the selection !"); + console.log("The cursor has reached the end of the selection !"); +}); + +ee.on('speedchanged', function (speed) { + document.getElementById('speedValue').value = speed; + document.querySelector(".speed-slider").value = speed; + displaySoundStatus("Received speed : " + speed + "x "); }); diff --git a/dist/waveform-playlist/web-audio-editor.html b/dist/waveform-playlist/web-audio-editor.html index 22ab5097..affd9902 100644 --- a/dist/waveform-playlist/web-audio-editor.html +++ b/dist/waveform-playlist/web-audio-editor.html @@ -42,100 +42,112 @@

Full Waveform Editor

-
-
+
+
- + - + - + - + -
-
+
+
- + -
-
+
+
- + - + - + - + -
- - + + -
+
+
Print Playlist Info to Console -
-
+
+
+
-
-
- - - - -
-
- -
-
-
- - - Seek ! -
-
-
-
-
+
+ + + + +
+
+
+ + +
+
+
+
+ + + + Change speed +
+
+
+
+ + + Seek ! +
+
+
+
+
diff --git a/ghpages/_examples/99webaudioeditor.html b/ghpages/_examples/99webaudioeditor.html index 0c6e33f5..b35cdd90 100644 --- a/ghpages/_examples/99webaudioeditor.html +++ b/ghpages/_examples/99webaudioeditor.html @@ -8,98 +8,110 @@ ---
-
-
+
+
- + - + - + - + -
-
+
+
- + -
-
+
+
- + - + - + - + -
- - + + -
+
+
Print Playlist Info to Console -
-
+
+
+
-
-
- - - - -
-
- -
-
-
- - - Seek ! -
-
-
-
-
+
+ + + + +
+
+
+ + +
+
+
+
+ + + + Change speed +
+
+
+
+ + + Seek ! +
+
+
+
+
diff --git a/ghpages/js/emitter.js b/ghpages/js/emitter.js index 3450604b..27e065ee 100644 --- a/ghpages/js/emitter.js +++ b/ghpages/js/emitter.js @@ -1,7 +1,7 @@ /* * This script is provided to give an example how the playlist can be controlled using the event emitter. * This enables projects to create/control the useability of the project. -*/ + */ var ee = playlist.getEventEmitter(); var $container = $("body"); var $timeFormat = $container.find('.time-format'); @@ -16,319 +16,336 @@ var audioPos = 0; var downloadUrl = undefined; function toggleActive(node) { - var active = node.parentNode.querySelectorAll('.active'); - var i = 0, len = active.length; + var active = node.parentNode.querySelectorAll('.active'); + var i = 0, len = active.length; - for (; i < len; i++) { - active[i].classList.remove('active'); - } + for (; i < len; i++) { + active[i].classList.remove('active'); + } - node.classList.toggle('active'); + node.classList.toggle('active'); } function cueFormatters(format) { - function clockFormat(seconds, decimals) { - var hours, - minutes, - secs, - result; - - hours = parseInt(seconds / 3600, 10) % 24; - minutes = parseInt(seconds / 60, 10) % 60; - secs = seconds % 60; - secs = secs.toFixed(decimals); - - result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (secs < 10 ? "0" + secs : secs); - - return result; - } - - var formats = { - "seconds": function (seconds) { - return seconds.toFixed(0); - }, - "thousandths": function (seconds) { - return seconds.toFixed(3); - }, - "hh:mm:ss": function (seconds) { - return clockFormat(seconds, 0); - }, - "hh:mm:ss.u": function (seconds) { - return clockFormat(seconds, 1); - }, - "hh:mm:ss.uu": function (seconds) { - return clockFormat(seconds, 2); - }, - "hh:mm:ss.uuu": function (seconds) { - return clockFormat(seconds, 3); + function clockFormat(seconds, decimals) { + var hours, + minutes, + secs, + result; + + hours = parseInt(seconds / 3600, 10) % 24; + minutes = parseInt(seconds / 60, 10) % 60; + secs = seconds % 60; + secs = secs.toFixed(decimals); + + result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (secs < 10 ? "0" + secs : secs); + + return result; } - }; - return formats[format]; + var formats = { + "seconds": function (seconds) { + return seconds.toFixed(0); + }, + "thousandths": function (seconds) { + return seconds.toFixed(3); + }, + "hh:mm:ss": function (seconds) { + return clockFormat(seconds, 0); + }, + "hh:mm:ss.u": function (seconds) { + return clockFormat(seconds, 1); + }, + "hh:mm:ss.uu": function (seconds) { + return clockFormat(seconds, 2); + }, + "hh:mm:ss.uuu": function (seconds) { + return clockFormat(seconds, 3); + } + }; + + return formats[format]; } function updateSelect(start, end) { - if (start < end) { - $('.btn-trim-audio').removeClass('disabled'); - } - else { - $('.btn-trim-audio').addClass('disabled'); - } - - $audioStart.val(cueFormatters(format)(start)); - $audioEnd.val(cueFormatters(format)(end)); - - startTime = start; - endTime = end; + if (start < end) { + $('.btn-trim-audio').removeClass('disabled'); + } + else { + $('.btn-trim-audio').addClass('disabled'); + } + + $audioStart.val(cueFormatters(format)(start)); + $audioEnd.val(cueFormatters(format)(end)); + + startTime = start; + endTime = end; } function updateTime(time) { - $time.html(cueFormatters(format)(time)); + $time.html(cueFormatters(format)(time)); - audioPos = time; + audioPos = time; } updateSelect(startTime, endTime); updateTime(audioPos); - /* -* Code below sets up events to send messages to the playlist. -*/ -$container.on("click", ".btn-playlist-state-group", function() { - //reset these for now. - $('.btn-fade-state-group').addClass('hidden'); - $('.btn-select-state-group').addClass('hidden'); - - if ($('.btn-select').hasClass('active')) { - $('.btn-select-state-group').removeClass('hidden'); - } + * Code below sets up events to send messages to the playlist. + */ +$container.on("click", ".btn-playlist-state-group", function () { + //reset these for now. + $('.btn-fade-state-group').addClass('hidden'); + $('.btn-select-state-group').addClass('hidden'); + + if ($('.btn-select').hasClass('active')) { + $('.btn-select-state-group').removeClass('hidden'); + } - if ($('.btn-fadein').hasClass('active') || $('.btn-fadeout').hasClass('active')) { - $('.btn-fade-state-group').removeClass('hidden'); - } + if ($('.btn-fadein').hasClass('active') || $('.btn-fadeout').hasClass('active')) { + $('.btn-fade-state-group').removeClass('hidden'); + } }); -$container.on("click", ".btn-play", function() { - ee.emit("play"); +$container.on("click", ".btn-play", function () { + ee.emit("play"); }); -$container.on("click", ".btn-pause", function() { - ee.emit("pause"); +$container.on("click", ".btn-pause", function () { + ee.emit("pause"); }); -$container.on("click", ".btn-stop", function() { - ee.emit("stop"); +$container.on("click", ".btn-stop", function () { + ee.emit("stop"); }); -$container.on("click", ".btn-rewind", function() { - ee.emit("rewind"); +$container.on("click", ".btn-rewind", function () { + ee.emit("rewind"); }); -$container.on("click", ".btn-fast-forward", function() { - ee.emit("fastforward"); +$container.on("click", ".btn-fast-forward", function () { + ee.emit("fastforward"); }); -$container.on("click", ".btn-record", function() { - ee.emit("record"); +$container.on("click", ".btn-record", function () { + ee.emit("record"); }); //track interaction states -$container.on("click", ".btn-cursor", function() { - ee.emit("statechange", "cursor"); - toggleActive(this); +$container.on("click", ".btn-cursor", function () { + ee.emit("statechange", "cursor"); + toggleActive(this); }); -$container.on("click", ".btn-select", function() { - ee.emit("statechange", "select"); - toggleActive(this); +$container.on("click", ".btn-select", function () { + ee.emit("statechange", "select"); + toggleActive(this); }); -$container.on("click", ".btn-shift", function() { - ee.emit("statechange", "shift"); - toggleActive(this); +$container.on("click", ".btn-shift", function () { + ee.emit("statechange", "shift"); + toggleActive(this); }); -$container.on("click", ".btn-fadein", function() { - ee.emit("statechange", "fadein"); - toggleActive(this); +$container.on("click", ".btn-fadein", function () { + ee.emit("statechange", "fadein"); + toggleActive(this); }); -$container.on("click", ".btn-fadeout", function() { - ee.emit("statechange", "fadeout"); - toggleActive(this); +$container.on("click", ".btn-fadeout", function () { + ee.emit("statechange", "fadeout"); + toggleActive(this); }); //fade types -$container.on("click", ".btn-logarithmic", function() { - ee.emit("fadetype", "logarithmic"); - toggleActive(this); +$container.on("click", ".btn-logarithmic", function () { + ee.emit("fadetype", "logarithmic"); + toggleActive(this); }); -$container.on("click", ".btn-linear", function() { - ee.emit("fadetype", "linear"); - toggleActive(this); +$container.on("click", ".btn-linear", function () { + ee.emit("fadetype", "linear"); + toggleActive(this); }); -$container.on("click", ".btn-scurve", function() { - ee.emit("fadetype", "sCurve"); - toggleActive(this); +$container.on("click", ".btn-scurve", function () { + ee.emit("fadetype", "sCurve"); + toggleActive(this); }); -$container.on("click", ".btn-exponential", function() { - ee.emit("fadetype", "exponential"); - toggleActive(this); +$container.on("click", ".btn-exponential", function () { + ee.emit("fadetype", "exponential"); + toggleActive(this); }); //zoom buttons -$container.on("click", ".btn-zoom-in", function() { - ee.emit("zoomin"); +$container.on("click", ".btn-zoom-in", function () { + ee.emit("zoomin"); }); -$container.on("click", ".btn-zoom-out", function() { - ee.emit("zoomout"); +$container.on("click", ".btn-zoom-out", function () { + ee.emit("zoomout"); }); -$container.on("click", ".btn-trim-audio", function() { - ee.emit("trim"); +$container.on("click", ".btn-trim-audio", function () { + ee.emit("trim"); }); -$container.on("click", ".btn-info", function() { - console.log(playlist.getInfo()); +$container.on("click", ".btn-info", function () { + console.log(playlist.getInfo()); }); $container.on("click", ".btn-download", function () { - ee.emit('startaudiorendering', 'wav'); + ee.emit('startaudiorendering', 'wav'); }); $container.on("click", ".btn-seektotime", function () { - var time = parseInt(document.getElementById("seektime").value, 10); - ee.emit("select", time, time); + var time = parseInt(document.getElementById("seektime").value, 10); + ee.emit("select", time, time); }); $container.on("change", ".select-seek-style", function (node) { - playlist.setSeekStyle(node.target.value); + playlist.setSeekStyle(node.target.value); }); //track drop -$container.on("dragenter", ".track-drop", function(e) { - e.preventDefault(); - e.target.classList.add("drag-enter"); +$container.on("dragenter", ".track-drop", function (e) { + e.preventDefault(); + e.target.classList.add("drag-enter"); }); -$container.on("dragover", ".track-drop", function(e) { - e.preventDefault(); +$container.on("dragover", ".track-drop", function (e) { + e.preventDefault(); }); -$container.on("dragleave", ".track-drop", function(e) { - e.preventDefault(); - e.target.classList.remove("drag-enter"); +$container.on("dragleave", ".track-drop", function (e) { + e.preventDefault(); + e.target.classList.remove("drag-enter"); }); -$container.on("drop", ".track-drop", function(e) { - e.preventDefault(); - e.target.classList.remove("drag-enter"); +$container.on("drop", ".track-drop", function (e) { + e.preventDefault(); + e.target.classList.remove("drag-enter"); - var dropEvent = e.originalEvent; + var dropEvent = e.originalEvent; - for (var i = 0; i < dropEvent.dataTransfer.files.length; i++) { - ee.emit("newtrack", dropEvent.dataTransfer.files[i]); - } + for (var i = 0; i < dropEvent.dataTransfer.files.length; i++) { + ee.emit("newtrack", dropEvent.dataTransfer.files[i]); + } +}); + +$container.on("change", ".time-format", function (e) { + format = $timeFormat.val(); + + updateSelect(startTime, endTime); + updateTime(audioPos); }); -$container.on("change", ".time-format", function(e) { - format = $timeFormat.val(); +$container.on("input change", ".master-gain", function (node) { + ee.emit("mastervolumechange", node.target.value); +}); - updateSelect(startTime, endTime); - updateTime(audioPos); +$container.on("input change", ".speed-slider", function (node) { + document.getElementById('speedValue').value = node.target.value; + ee.emit("speedchange", node.target.value); + displaySoundStatus("Playback speed is now " + node.target.value + "x !"); }); -$container.on("input change", ".master-gain", function(node){ - ee.emit("mastervolumechange", node.target.value); +$container.on("click", ".btn-speed-change", function () { + var value = document.getElementById('speedValue').value; + ee.emit("speedchange", value); + displaySoundStatus("Playback speed is now " + value + "x !"); }); function displaySoundStatus(status) { - $(".sound-status").html(status); + $(".sound-status").html(status); } function displayLoadingData(data) { - var info = $("
").append(data); - $(".loading-data").append(info); + var info = $("
").append(data); + $(".loading-data").append(info); } function displayDownloadLink(link) { - var dateString = (new Date()).toISOString(); - var $link = $("", { - 'href': link, - 'download': 'waveformplaylist' + dateString + '.wav', - 'text': 'Download mix ' + dateString, - 'class': 'btn btn-small btn-download-link' - }); - - $('.btn-download-link').remove(); - $('.btn-download').after($link); + var dateString = (new Date()).toISOString(); + var $link = $("", { + 'href': link, + 'download': 'waveformplaylist' + dateString + '.wav', + 'text': 'Download mix ' + dateString, + 'class': 'btn btn-small btn-download-link' + }); + + $('.btn-download-link').remove(); + $('.btn-download').after($link); } /* -* Code below receives updates from the playlist. -*/ + * Code below receives updates from the playlist. + */ ee.on("select", updateSelect); ee.on("timeupdate", updateTime); -ee.on("mute", function(track) { - displaySoundStatus("Mute button pressed for " + track.name); +ee.on("mute", function (track) { + displaySoundStatus("Mute button pressed for " + track.name); }); -ee.on("solo", function(track) { - displaySoundStatus("Solo button pressed for " + track.name); +ee.on("solo", function (track) { + displaySoundStatus("Solo button pressed for " + track.name); }); -ee.on("volumechange", function(volume, track) { - displaySoundStatus(track.name + " now has volume " + volume + "."); +ee.on("volumechange", function (volume, track) { + displaySoundStatus(track.name + " now has volume " + volume + "."); }); -ee.on("mastervolumechange", function(volume) { - displaySoundStatus("Master volume now has volume " + volume + "."); +ee.on("mastervolumechange", function (volume) { + displaySoundStatus("Master volume now has volume " + volume + "."); }); var audioStates = ["uninitialized", "loading", "decoding", "finished"]; -ee.on("audiorequeststatechange", function(state, src) { - var name = src; +ee.on("audiorequeststatechange", function (state, src) { + var name = src; - if (src instanceof File) { - name = src.name; - } + if (src instanceof File) { + name = src.name; + } - displayLoadingData("Track " + name + " is in state " + audioStates[state]); + displayLoadingData("Track " + name + " is in state " + audioStates[state]); }); -ee.on("loadprogress", function(percent, src) { - var name = src; +ee.on("loadprogress", function (percent, src) { + var name = src; - if (src instanceof File) { - name = src.name; - } + if (src instanceof File) { + name = src.name; + } - displayLoadingData("Track " + name + " has loaded " + percent + "%"); + displayLoadingData("Track " + name + " has loaded " + percent + "%"); }); ee.on('audiorenderingfinished', function (type, data) { - if (type == 'wav'){ - if (downloadUrl) { - window.URL.revokeObjectURL(downloadUrl); - } + if (type == 'wav') { + if (downloadUrl) { + window.URL.revokeObjectURL(downloadUrl); + } - downloadUrl = window.URL.createObjectURL(data); - displayDownloadLink(downloadUrl); - } + downloadUrl = window.URL.createObjectURL(data); + displayDownloadLink(downloadUrl); + } }); ee.on('finished', function () { - console.log("The cursor has reached the end of the selection !"); + console.log("The cursor has reached the end of the selection !"); +}); + +ee.on('speedchanged', function (speed) { + document.getElementById('speedValue').value = speed; + document.querySelector(".speed-slider").value = speed; + displaySoundStatus("Received speed : " + speed + "x "); }); From 5864d05afa0bc0b1c9b6143c2febb37548154f52 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 12:32:21 +0200 Subject: [PATCH 08/13] Fix bug when recording track --- src/Track.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/Track.js b/src/Track.js index 49b12ae9..db0db4e0 100644 --- a/src/Track.js +++ b/src/Track.js @@ -1,20 +1,16 @@ 'use strict'; -import _assign from 'lodash.assign'; -import _forOwn from 'lodash.forown'; - -import uuid from 'uuid'; -import h from 'virtual-dom/h'; - -import {secondsToPixels, secondsToSamples} from './utils/conversions'; -import extractPeaks from 'webaudio-peaks'; -import stateClasses from './track/states'; - -import CanvasHook from './render/CanvasHook'; -import FadeCanvasHook from './render/FadeCanvasHook'; -import VolumeSliderHook from './render/VolumeSliderHook'; - -import {FADEIN, FADEOUT} from 'fade-maker'; +import _assign from "lodash.assign"; +import _forOwn from "lodash.forown"; +import uuid from "uuid"; +import h from "virtual-dom/h"; +import {secondsToPixels, secondsToSamples} from "./utils/conversions"; +import extractPeaks from "webaudio-peaks"; +import stateClasses from "./track/states"; +import CanvasHook from "./render/CanvasHook"; +import FadeCanvasHook from "./render/FadeCanvasHook"; +import VolumeSliderHook from "./render/VolumeSliderHook"; +import {FADEIN, FADEOUT} from "fade-maker"; const MAX_CANVAS_WIDTH = 1000; @@ -24,6 +20,7 @@ export default class { this.name = "Untitled"; this.gain = 1; + this.speed = 1; this.fades = {}; this.peakData = { type: "WebAudio", From 21e447e9f842548f230658987c5261310087e7f3 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 12:53:27 +0200 Subject: [PATCH 09/13] Playback speed improvements --- README.md | 2 +- src/Playlist.js | 93 ++++++++++++++++++++++++------------------------- src/Track.js | 1 - src/app.js | 1 - 4 files changed, 47 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index ef007263..e49cc041 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ An example of using the event emitter to control the playlist can be found in [/ | `mastervolumechange` | `volume` | Set a new master volume `volume` (0-100) | | `select` | `start, end, track:optional` | Seek to the start time or start/end selection optionally with active track `track`. | | `startaudiorendering` | (`wav | buffer`) | Request for a downloadable file or web Audio buffer that represent the current work | -| `speedchange` | `speed` | Change de playback speed.| +| `speedchange` | `speed` | Change de playback speed. Minimum is 0.5x and maximum is 4x | #### Events to Listen to diff --git a/src/Playlist.js b/src/Playlist.js index a07fe592..7f0c86cd 100644 --- a/src/Playlist.js +++ b/src/Playlist.js @@ -30,6 +30,7 @@ export default class { this.fadeType = "logarithmic"; this.masterGain = 1; + this.speed = 1; } initRecorder(stream) { @@ -129,7 +130,7 @@ export default class { let ee = this.ee; ee.on('speedchange', (speed) => { - this.setSpeed(speed); + this.setSpeed(speed); }); ee.on('select', (start, end, track) => { @@ -147,7 +148,7 @@ export default class { }); ee.on('startaudiorendering', (type) => { - this.startOfflineRender(type); + this.startOfflineRender(type); }); ee.on('statechange', (state) => { @@ -198,11 +199,11 @@ export default class { }); ee.on('volumechange', (volume, track) => { - track.setGainLevel(volume/100); + track.setGainLevel(volume / 100); }); ee.on('mastervolumechange', (volume) => { - this.masterGain = volume/100 + this.masterGain = volume / 100 this.tracks.forEach((track) => { track.setMasterGainLevel(this.masterGain); }); @@ -241,7 +242,7 @@ export default class { }); ee.on('zoomin', () => { - let zoomIndex = Math.max(0, this.zoomIndex-1); + let zoomIndex = Math.max(0, this.zoomIndex - 1); let zoom = this.zoomLevels[zoomIndex]; if (zoom !== this.samplesPerPixel) { @@ -251,7 +252,7 @@ export default class { }); ee.on('zoomout', () => { - let zoomIndex = Math.min(this.zoomLevels.length-1, this.zoomIndex+1); + let zoomIndex = Math.min(this.zoomLevels.length - 1, this.zoomIndex + 1); let zoom = this.zoomLevels[zoomIndex]; if (zoom !== this.samplesPerPixel) { @@ -265,7 +266,7 @@ export default class { }); } - load(trackList, options={}) { + load(trackList, options = {}) { let loadPromises = trackList.map((trackInfo) => { let loader = LoaderFactory.createLoader(trackInfo.src, this.ac, this.ee); return loader.load(); @@ -344,8 +345,8 @@ export default class { } /* - track instance of Track. - */ + track instance of Track. + */ setActiveTrack(track) { this.activeTrack = track; } @@ -359,9 +360,9 @@ export default class { } /* - start, end in seconds. - */ - setTimeSelection(start=0, end=undefined) { + start, end in seconds. + */ + setTimeSelection(start = 0, end = undefined) { this.timeSelection = { start, end: (end === undefined) ? start : end @@ -370,13 +371,13 @@ export default class { this.cursor = start; } - startOfflineRender(type){ + startOfflineRender(type) { if (this.isRendering) { return; } this.isRendering = true; - this.offlineAudioContext = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(2, 44100*this.duration, 44100); + this.offlineAudioContext = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(2, 44100 * this.duration, 44100); var currentTime = this.offlineAudioContext.currentTime, startTime = 0, @@ -386,14 +387,14 @@ export default class { track.setOfflinePlayout(new Playout(this.offlineAudioContext, track.buffer)); track.schedulePlay(currentTime, startTime, endTime, { shouldPlay: this.shouldTrackPlay(track), - masterGain : 0.8, - isOffline : true + masterGain: 0.8, + isOffline: true }); }); /* - TODO cleanup of different audio playouts handling. - */ + TODO cleanup of different audio playouts handling. + */ this.offlineAudioContext.startRendering().then((audioBuffer) => { if (type == 'buffer') { this.ee.emit('audiorenderingfinished', type, audioBuffer); @@ -477,12 +478,8 @@ export default class { }); } - setSpeed(speed){ - this.speed = (speed>=0.5 && speed <= 2)? speed : 1; - this.tracks.forEach((track) => { - track.setSpeed(this.speed); - }); - + setSpeed(speed) { + this.speed = (speed >= 0.5 && speed <= 4) ? speed : 1; if (this.isPlaying()) this.restartPlayFrom(this.playbackSeconds); this.ee.emit('speedchanged', this.speed); @@ -551,8 +548,8 @@ export default class { } /* - * returns the current point of time in the playlist in seconds. - */ + * returns the current point of time in the playlist in seconds. + */ getCurrentTime() { let cursorPos = this.lastSeeked || this.pausedAt || this.cursor; @@ -563,7 +560,7 @@ export default class { return this.ac.currentTime - this.lastPlay; } - setMasterGain(gain){ + setMasterGain(gain) { this.ee.emit('mastervolumechange', gain); } @@ -594,10 +591,11 @@ export default class { } this.tracks.forEach((track) => { + track.setSpeed(this.speed); track.setState('cursor'); playoutPromises.push(track.schedulePlay(currentTime, startTime, endTime, { shouldPlay: this.shouldTrackPlay(track), - masterGain : this.masterGain + masterGain: this.masterGain })); }); @@ -694,15 +692,15 @@ export default class { this.setActiveTrack(track || this.tracks[0]); this.pausedAt = start; this.setTimeSelection(start, end); - if (this.getSeekStyle() == 'fill'){ + if (this.getSeekStyle() == 'fill') { this.playbackSeconds = start; } } } /* - * Animation function for the playlist. - */ + * Animation function for the playlist. + */ updateEditor(cursorPos) { let currentTime = this.ac.currentTime; let playbackSeconds = 0; @@ -715,7 +713,7 @@ export default class { if (this.isPlaying()) { //console.log("speed " + this.speed); - playbackSeconds = cursorPos + elapsed*this.speed; + playbackSeconds = cursorPos + elapsed * this.speed; this.ee.emit('timeupdate', playbackSeconds); this.animationRequest = window.requestAnimationFrame( this.updateEditor.bind(this, playbackSeconds) @@ -723,7 +721,7 @@ export default class { } else { if ((cursorPos + elapsed) >= - (this.isSegmentSelection()) ? selection.end : this.duration) { + (this.isSegmentSelection()) ? selection.end : this.duration) { this.ee.emit('finished'); } @@ -747,14 +745,14 @@ export default class { //use for fast forwarding. this.viewDuration = pixelsToSeconds( - this.rootNode.clientWidth - this.controls.width, - this.samplesPerPixel, - this.sampleRate + this.rootNode.clientWidth - this.controls.width, + this.samplesPerPixel, + this.sampleRate ); }); } - getTrackRenderData(data={}) { + getTrackRenderData(data = {}) { let defaults = { "height": this.waveHeight, "resolution": this.samplesPerPixel, @@ -771,15 +769,15 @@ export default class { } isActiveTrack(track) { - let activeTrack = this.getActiveTrack(); - return this.isSegmentSelection() ? - ((activeTrack === track) ? true : false) : true; + let activeTrack = this.getActiveTrack(); + return this.isSegmentSelection() ? + ((activeTrack === track) ? true : false) : true; } render() { let controlWidth = this.controls.show ? this.controls.width : 0; let timeScale = new TimeScale(this.duration, this.scrollLeft, - this.samplesPerPixel, this.sampleRate, controlWidth); + this.samplesPerPixel, this.sampleRate, controlWidth); let trackElements = this.tracks.map((track) => { return track.render(this.getTrackRenderData({ @@ -796,9 +794,9 @@ export default class { }, "onscroll": (e) => { this.scrollLeft = pixelsToSeconds( - e.target.scrollLeft, - this.samplesPerPixel, - this.sampleRate + e.target.scrollLeft, + this.samplesPerPixel, + this.sampleRate ); this.ee.emit("scroll", this.scrollLeft); }, @@ -814,9 +812,10 @@ export default class { containerChildren.push(trackSection); return h("div.playlist", { - "attributes": { - "style": "overflow: hidden; position: relative;" - }}, + "attributes": { + "style": "overflow: hidden; position: relative;" + } + }, containerChildren ); } diff --git a/src/Track.js b/src/Track.js index db0db4e0..648e07d0 100644 --- a/src/Track.js +++ b/src/Track.js @@ -208,7 +208,6 @@ export default class { setSpeed(speed){ this.speed = speed; - this.playout.setSpeed(speed); } /* diff --git a/src/app.js b/src/app.js index daac889f..cd90571b 100644 --- a/src/app.js +++ b/src/app.js @@ -56,7 +56,6 @@ export function init(options={}, ee=EventEmitter()) { playlist.setMono(config.mono); playlist.setShowTimeScale(config.timescale); playlist.setSeekStyle(config.seekStyle); - playlist.setSpeed(1); //take care of initial virtual dom rendering. let tree = playlist.render(); From be778b27a9702432298378c41f39a70008743d26 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 19:13:21 +0200 Subject: [PATCH 10/13] Now possible to make loop in select mode #15 --- src/Playlist.js | 26 +++++++++++++++++++++++--- src/Playout.js | 1 - 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Playlist.js b/src/Playlist.js index 7f0c86cd..07ed25d7 100644 --- a/src/Playlist.js +++ b/src/Playlist.js @@ -31,6 +31,7 @@ export default class { this.fadeType = "logarithmic"; this.masterGain = 1; this.speed = 1; + this.loopNumber = 0; } initRecorder(stream) { @@ -133,6 +134,10 @@ export default class { this.setSpeed(speed); }); + ee.on('loopnumber', (number) => { + this.setLoop(number); + }); + ee.on('select', (start, end, track) => { if (this.isPlaying()) { this.lastSeeked = start; @@ -478,6 +483,11 @@ export default class { }); } + setLoop(number) { + this.loopNumber = number + } + + setSpeed(speed) { this.speed = (speed >= 0.5 && speed <= 4) ? speed : 1; if (this.isPlaying()) @@ -720,9 +730,19 @@ export default class { ); } else { - if ((cursorPos + elapsed) >= - (this.isSegmentSelection()) ? selection.end : this.duration) { - this.ee.emit('finished'); + if ((cursorPos + elapsed) >= (this.isSegmentSelection()) ? selection.end : this.duration) { + if (this.loopNumber > 0) { + this.loopNumber--; + this.ee.emit('newloop', this.loopNumber); + this.restartPlayFrom(selection.start, selection.end) + } + else if (this.loopNumber == -1) { + this.ee.emit('newloop', this.loopNumber); + this.restartPlayFrom(selection.start, selection.end) + + } + else + this.ee.emit('finished'); } this.stopAnimation(); diff --git a/src/Playout.js b/src/Playout.js index ef9ce6b8..d9448373 100644 --- a/src/Playout.js +++ b/src/Playout.js @@ -102,7 +102,6 @@ export default class { } setSpeed(speed) { - console.log("callaed ! " + speed); this.speed = speed; } From e59cd1064a26af8864a65009407dc3a6c71c0baf Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Sun, 2 Oct 2016 19:19:09 +0200 Subject: [PATCH 11/13] Upated readme and dist for loops in #15 --- README.md | 2 ++ ghpages/_examples/99webaudioeditor.html | 7 +++++++ ghpages/js/emitter.js | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/README.md b/README.md index e49cc041..268902d5 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,7 @@ An example of using the event emitter to control the playlist can be found in [/ | `select` | `start, end, track:optional` | Seek to the start time or start/end selection optionally with active track `track`. | | `startaudiorendering` | (`wav | buffer`) | Request for a downloadable file or web Audio buffer that represent the current work | | `speedchange` | `speed` | Change de playback speed. Minimum is 0.5x and maximum is 4x | +| `loopnumber` | `number` | -1 : infinite loop. `number` > 1 : perform `number` loops. 0 : disable loops | #### Events to Listen to @@ -283,6 +284,7 @@ An example of using the event emitter to control the playlist can be found in [/ | `finished` | _none_ | Event fired when cursor ( while playing ) reaches the end (maximum duration) | | `audiorenderingfinished` | `type, data` | Return the result of the rendering in the desired format. `type` can be `buffer` or `wav` and can be used to dertermine the `data` type. When `type` is `wav`, data is a `blob` object that represent the wav file. | | `speedchanged` | `speed` | When speed is changed, return the value applied to tracks. | +| `newloop` | `remaining` |Event fired when one loop finish, and give the number of remaining loops to come | ## Tests diff --git a/ghpages/_examples/99webaudioeditor.html b/ghpages/_examples/99webaudioeditor.html index b35cdd90..5572bbc3 100644 --- a/ghpages/_examples/99webaudioeditor.html +++ b/ghpages/_examples/99webaudioeditor.html @@ -111,6 +111,13 @@ Seek !
+
+
+ + + Set loop +
+
diff --git a/ghpages/js/emitter.js b/ghpages/js/emitter.js index 27e065ee..04cad4b4 100644 --- a/ghpages/js/emitter.js +++ b/ghpages/js/emitter.js @@ -254,6 +254,10 @@ $container.on("input change", ".speed-slider", function (node) { displaySoundStatus("Playback speed is now " + node.target.value + "x !"); }); +$container.on('click', ".set-loop-number", function () { + ee.emit("loopnumber", document.getElementById("loopValue").value); +}); + $container.on("click", ".btn-speed-change", function () { var value = document.getElementById('speedValue').value; ee.emit("speedchange", value); @@ -349,3 +353,8 @@ ee.on('speedchanged', function (speed) { document.querySelector(".speed-slider").value = speed; displaySoundStatus("Received speed : " + speed + "x "); }); + +ee.on('newloop', function (number) { + //document.getElementById("loopValue").value = number; + displaySoundStatus(number + " remaining loop ..."); +}); From 623b1da8a9627bd542fd7e8d36a7bd198297b274 Mon Sep 17 00:00:00 2001 From: Guillaume Villena Date: Mon, 9 Jan 2017 19:32:32 +0100 Subject: [PATCH 12/13] Merge branch 'master' of https://github.com/naomiaro/waveform-playlist --- .eslintrc.json | 18 +- dist/waveform-playlist/css/main.css | 9 +- dist/waveform-playlist/exclusive-solo.html | 153 +- dist/waveform-playlist/index.html | 22 +- dist/waveform-playlist/js/emitter.js | 31 +- .../js/waveform-playlist.var.js | 9578 +++++++++-------- .../js/waveform-playlist.var.js.map | 668 +- dist/waveform-playlist/multi-channel.html | 132 +- dist/waveform-playlist/web-audio-editor.html | 133 +- ghpages/_examples/11exclusivesolo.html | 19 +- ghpages/_examples/12multichannel.html | 12 +- ghpages/_examples/99webaudioeditor.html | 58 +- ghpages/js/emitter.js | 98 +- ghpages/js/exclusivesolo.js | 2 +- ghpages/js/multi-channel.js | 2 +- src/Playlist.js | 223 +- src/Playout.js | 19 +- src/TimeScale.js | 11 +- src/Track.js | 71 +- src/render/FadeCanvasHook.js | 8 +- src/render/ScrollHook.js | 2 +- src/track/loader/BlobLoader.js | 6 +- src/track/loader/Loader.js | 2 +- src/track/loader/LoaderFactory.js | 4 +- src/track/loader/XHRLoader.js | 2 +- src/track/states.js | 10 +- src/track/states/CursorState.js | 2 +- src/track/states/FadeInState.js | 2 +- src/track/states/FadeOutState.js | 2 +- src/track/states/SelectState.js | 2 +- src/track/states/ShiftState.js | 2 +- src/utils/exportWavWorker.js | 2 +- src/utils/recorderWorker.js | 26 +- 33 files changed, 6052 insertions(+), 5279 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 84ec4503..2fe3da86 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,11 +1,11 @@ { - "extends": "airbnb-base", - "plugins": [ - "import" - ], - "env": { - "browser": true, - "worker": true, - "es6": true - } + "extends": "airbnb-base", + "plugins": [ + "import" + ], + "env": { + "browser": true, + "worker": true, + "es6": true + } } diff --git a/dist/waveform-playlist/css/main.css b/dist/waveform-playlist/css/main.css index b27343c0..f544296b 100644 --- a/dist/waveform-playlist/css/main.css +++ b/dist/waveform-playlist/css/main.css @@ -102,9 +102,12 @@ background: #ccc; } .playlist .vocals { - background-color: #c0dce0; } - .playlist .vocals header { - background-color: green; } + background-color: #c0dce0; +} + +.playlist .vocals header { + background-color: green; +} .track-drop { border: 2px dashed blue; diff --git a/dist/waveform-playlist/exclusive-solo.html b/dist/waveform-playlist/exclusive-solo.html index b735a8e9..d7d8d0c6 100644 --- a/dist/waveform-playlist/exclusive-solo.html +++ b/dist/waveform-playlist/exclusive-solo.html @@ -1,97 +1,112 @@ - - - - - - Stem Tracks with exclusive solo - - - - - - - + + + + + + Stem Tracks with exclusive solo + + + + + + + - + -