Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: naomiaro/waveform-playlist
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: Treora/waveform-playlist
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 19 commits
  • 11 files changed
  • 1 contributor

Commits on Oct 21, 2019

  1. Copy the full SHA
    fd42f6a View commit details
  2. Copy the full SHA
    aa947f9 View commit details
  3. Copy the full SHA
    ad5d18c View commit details
  4. Copy the full SHA
    b5c9a33 View commit details

Commits on Nov 4, 2019

  1. Set "box-sizing: border-box" for annotation boxes

    To make annotations the correct size. Note the problem did not appear in
    the examples because there Bootstrap already sets this attribute for all
    elements.
    Treora committed Nov 4, 2019
    Copy the full SHA
    2d94350 View commit details
  2. Do not round pixel values, fixes misalignments

    Decimals are fine in CSS. Also, sometimes small misalignments appeared.
    The reason was that the width calculation of annotation boxes ignored
    the rounding of the 'left' value. For example, assuming pixPerSec=1 for
    simplicity, a note ranging from 5.4s to 8.8s (= 3.4 seconds) would lead
    to 'left: 5px; width: 3px', whereas a width of 4px would got the right
    side closer to the desired 8.8px. Instead of fixing this calculation, it
    seemed better to remove the rounding altogether.
    Treora committed Nov 4, 2019
    Copy the full SHA
    3eaf2a7 View commit details
  3. Fix selection cursor misalignment

    Just set the width to 1px for point selections, instead of always
    adding 1px to the width, which was inappropriate for segments.
    Also, for a point, subtract half the width to center the line.
    
    Refactor a little to more clearly distinguish the two scenarios.
    Treora committed Nov 4, 2019
    Copy the full SHA
    d1672a5 View commit details
  4. Don't break when cursor is not (yet) rendered.

    Problem occured in practice, not sure due to which factors.
    Treora committed Nov 4, 2019
    Copy the full SHA
    add85cb View commit details
  5. Center the cursor

    Treora committed Nov 4, 2019
    Copy the full SHA
    352850e View commit details

Commits on Nov 17, 2019

  1. Support point annotations

    Instead of creating negative width, create a square annotation centered
    at the annotated point.
    Treora committed Nov 17, 2019
    Copy the full SHA
    631538a View commit details
  2. Copy the full SHA
    4f537fe View commit details
  3. Copy the full SHA
    e2ad86c View commit details
  4. Listen on <html>, not <body>

    This way (at least) Chromium gives us mouse events even if the cursor is
    outside the document or browser window (while the button is pressed).
    Treora committed Nov 17, 2019
    Copy the full SHA
    621f633 View commit details
  5. Copy the full SHA
    047e5d1 View commit details

Commits on Nov 18, 2019

  1. Copy the full SHA
    f445ae7 View commit details
  2. Copy the full SHA
    ee0e155 View commit details
  3. Copy the full SHA
    174d838 View commit details
  4. Revise playlist’s 'select' handler and seek()

    - 'select' event should always update selection, also if isPlaying().
    - emit 'timeupdate' and redraw in seek(), not in 'select' handler.
    - deduplicate code between 'select' handler and seek().
    
    I hope these changes do not cause subtle issues in untested use cases.
    Treora committed Nov 18, 2019
    Copy the full SHA
    b3b7339 View commit details
  5. Revise selection clamping

    Revises commit 4f537fe to clamp to the whole playlist’s duration instead
    of to the individual track the cursor happens to be clicking on.
    Treora committed Nov 18, 2019
    Copy the full SHA
    1d41952 View commit details
3 changes: 2 additions & 1 deletion dist/waveform-playlist/css/main.css
Original file line number Diff line number Diff line change
@@ -104,7 +104,8 @@
text-align: center; }
.playlist .annotations .annotation-box {
border: 2px dashed grey;
padding: 0 10px; }
padding: 0 10px;
box-sizing: border-box; }
.playlist .annotations .annotation-box .resize-handle {
background: grey;
opacity: 0.3;
1 change: 1 addition & 0 deletions ghpages/_sass/_playlist.scss
Original file line number Diff line number Diff line change
@@ -165,6 +165,7 @@
.annotation-box {
border: 2px dashed grey;
padding: 0 10px;
box-sizing: border-box;

.resize-handle {
background: grey;
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -54,7 +54,9 @@
"fade-maker": "^1.0.3",
"inline-worker": "^1.1.0",
"lodash.assign": "^4.0.0",
"lodash.clamp": "^4.0.3",
"lodash.defaults": "^4.0.0",
"lodash.defaultsdeep": "^4.6.1",
"lodash.forown": "^4.0.0",
"mucss": "^1.1.5",
"uuid": "^2.0.1",
@@ -65,14 +67,15 @@
"clean": "rm -Rf dist && rm -Rf lib && rm -Rf styles",
"styles": "mkdir -p styles && cp ghpages/_sass/_playlist.scss styles/playlist.scss",
"compile": "babel src --out-dir lib",
"compile-watch": "babel src --out-dir lib -w",
"jekyll": "jekyll build -s ghpages -d dist/waveform-playlist",
"jekyll:dev": "jekyll build -s ghpages -d dist/waveform-playlist --watch &",
"ghpages": "gh-pages --repo https://$GH_TOKEN@github.com/naomiaro/waveform-playlist.git -d dist/waveform-playlist",
"build": "npm run clean && npm run jekyll && webpack",
"preversion": "npm run clean",
"version": "npm run build && git add -A dist",
"postversion": "git push && git push --tags",
"prepublish": "npm run compile && npm run styles",
"prepare": "npm run compile && npm run styles",
"webpack:server": "node_modules/webpack-dev-server/bin/webpack-dev-server.js --content-base dist/ --colors --host 0.0.0.0 --inline",
"start": "npm run webpack:server",
"dev": "npm run jekyll:dev && npm run webpack:server",
40 changes: 23 additions & 17 deletions src/Playlist.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import _clamp from 'lodash.clamp';
import _defaults from 'lodash.defaults';

import h from 'virtual-dom/h';
@@ -146,6 +147,7 @@ export default class {
this.annotationList = new AnnotationList(
this,
config.annotations,
config.annotationFormat,
config.controls,
config.editable,
config.linkEndpoints,
@@ -173,17 +175,11 @@ export default class {
this.drawRequest();
});

ee.on('select', (start, end, track) => {
if (this.isPlaying()) {
this.lastSeeked = start;
this.pausedAt = undefined;
this.restartPlayFrom(start);
} else {
// reset if it was paused.
this.seek(start, end, track);
this.ee.emit('timeupdate', start);
this.drawRequest();
}
ee.on('select', (chosenStart, chosenEnd, track) => {
const start = (chosenStart !== undefined) ? _clamp(chosenStart, 0, this.duration) : undefined;
const end = (chosenEnd !== undefined) ? _clamp(chosenEnd, 0, this.duration) : undefined;
this.setTimeSelection(start, end);
this.seek(start, end, track);
});

ee.on('startaudiorendering', (type) => {
@@ -592,7 +588,11 @@ export default class {
* returns the current point of time in the playlist in seconds.
*/
getCurrentTime() {
const cursorPos = this.lastSeeked || this.pausedAt || this.cursor;
const cursorPos = (this.lastSeeked !== undefined)
? this.lastSeeked
: (this.pausedAt !== undefined)
? this.pausedAt
: this.cursor;

return cursorPos + this.getElapsedTime();
}
@@ -622,10 +622,14 @@ export default class {
const selected = this.getTimeSelection();
const playoutPromises = [];

const start = startTime || this.pausedAt || this.cursor;
const start = (startTime !== undefined)
? startTime
: (this.pausedAt !== undefined)
? this.pausedAt
: this.cursor;
let end = endTime;

if (!end && selected.end !== selected.start && selected.end > start) {
if (end === undefined && selected.end !== selected.start && selected.end > start) {
end = selected.end;
}

@@ -712,6 +716,7 @@ export default class {
this.duration = 0;
this.scrollLeft = 0;

this.setTimeSelection(0, 0);
this.seek(0, 0, undefined);
});
}
@@ -751,10 +756,11 @@ export default class {
// reset if it was paused.
this.setActiveTrack(track || this.tracks[0]);
this.pausedAt = start;
this.setTimeSelection(start, end);
if (this.getSeekStyle() === 'fill') {
this.playbackSeconds = start;
}
this.ee.emit('timeupdate', start);
this.drawRequest();
}
}

@@ -765,7 +771,7 @@ export default class {
updateEditor(cursor) {
const currentTime = this.ac.currentTime;
const selection = this.getTimeSelection();
const cursorPos = cursor || this.cursor;
const cursorPos = (cursor !== undefined) ? cursor : this.cursor;
const elapsed = currentTime - this.lastDraw;

if (this.isPlaying()) {
@@ -791,7 +797,7 @@ export default class {
this.lastSeeked = undefined;
this.setState(this.getState());

this.playbackSeconds = 0;
this.playbackSeconds = this.getTimeSelection().start;
this.draw(this.render());
}, 0);
}
19 changes: 14 additions & 5 deletions src/Track.js
Original file line number Diff line number Diff line change
@@ -407,10 +407,11 @@ export default class {
}
}

const left = playbackX - 0.5; // subtract half the cursor's width to center it.
const waveformChildren = [
h('div.cursor', {
attributes: {
style: `position: absolute; width: 1px; margin: 0; padding: 0; top: 0; left: ${playbackX}px; bottom: 0; z-index: 5;`,
style: `position: absolute; width: 1px; margin: 0; padding: 0; top: 0; left: ${left}px; bottom: 0; z-index: 5;`,
},
}),
];
@@ -525,10 +526,18 @@ export default class {

// draw cursor selection on active track.
if (data.isActive === true) {
const cStartX = secondsToPixels(data.timeSelection.start, data.resolution, data.sampleRate);
const cEndX = secondsToPixels(data.timeSelection.end, data.resolution, data.sampleRate);
const cWidth = (cEndX - cStartX) + 1;
const cClassName = (cWidth > 1) ? '.segment' : '.point';
const isPoint = data.timeSelection.start === data.timeSelection.end;
let cStartX = secondsToPixels(data.timeSelection.start, data.resolution, data.sampleRate);
let cClassName, cWidth;
if (isPoint) {
cClassName = '.point';
cWidth = 1;
cStartX -= 0.5;
} else {
cClassName = '.segment';
const cEndX = secondsToPixels(data.timeSelection.end, data.resolution, data.sampleRate);
cWidth = cEndX - cStartX;
}

waveformChildren.push(h(`div.selection${cClassName}`, {
attributes: {
39 changes: 31 additions & 8 deletions src/annotation/AnnotationList.js
Original file line number Diff line number Diff line change
@@ -7,15 +7,27 @@ import DragInteraction from '../interaction/DragInteraction';
import ScrollTopHook from './render/ScrollTopHook';
import timeformat from '../utils/timeformat';

const annotationConverters = {
'aeneas': { input: inputAeneas, output: outputAeneas },
'raw': { input: a => a, output: a => a },
}

class AnnotationList {
constructor(playlist, annotations, controls = [], editable = false,
constructor(playlist, annotations, annotationFormat, controls = [], editable = false,
linkEndpoints = false, isContinuousPlay = false) {
this.playlist = playlist;
this.resizeHandlers = [];
this.editable = editable;
this.annotations = annotations.map(a =>
// TODO support different formats later on.
inputAeneas(a),

const converters = annotationConverters[annotationFormat];
if (converters === undefined) {
throw new Error(`Unknown annotation format ${annotationFormat}`);
}
this.inputAnnotation = converters.input;
this.outputAnnotation = converters.output;

this.annotations = annotations.map(annotation =>
this.inputAnnotation(annotation),
);
this.setupInteractions();

@@ -109,7 +121,7 @@ class AnnotationList {
}

export() {
const output = this.annotations.map(a => outputAeneas(a));
const output = this.annotations.map(a => this.outputAnnotation(a));
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(output))}`;
const a = document.createElement('a');

@@ -181,15 +193,26 @@ class AnnotationList {
const sampleRate = this.playlist.sampleRate;
const pixPerSec = sampleRate / samplesPerPixel;
const pixOffset = secondsToPixels(this.playlist.scrollLeft, samplesPerPixel, sampleRate);
const left = Math.floor((note.start * pixPerSec) - pixOffset);
const width = Math.ceil((note.end * pixPerSec) - (note.start * pixPerSec));
let left = note.start * pixPerSec - pixOffset;
let className;
let width;
if (note.end !== undefined && note.end !== 0 && note.end !== note.start) {
className = '.segment-annotation';
width = note.end * pixPerSec - note.start * pixPerSec;
} else {
className = '.point-annotation';
width = 30;
left -= 15;
}
const element = note.elementType || 'div';

return h('div.annotation-box',
return h(`${element}.annotation-box${className}`,
{
attributes: {
style: `position: absolute; height: 30px; width: ${width}px; left: ${left}px`,
'data-id': note.id,
},
...note.elementAttributes,
},
[
this.renderResizeLeft(i),
2 changes: 1 addition & 1 deletion src/annotation/input/aeneas.js
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import uuid from 'uuid';

export default function (aeneas) {
const annotation = {
id: aeneas.id || uuid.v4(),
id: (aeneas.id !== undefined) ? aeneas.id : uuid.v4(),
start: Number(aeneas.begin) || 0,
end: Number(aeneas.end) || 0,
lines: aeneas.lines || [''],
5 changes: 3 additions & 2 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _assign from 'lodash.assign';
import _defaultsDeep from 'lodash.defaultsdeep';
import createElement from 'virtual-dom/create-element';
import EventEmitter from 'event-emitter';
import Playlist from './Playlist';
@@ -37,6 +37,7 @@ export function init(options = {}, ee = EventEmitter()) {
zoomLevels: [512, 1024, 2048, 4096],
annotationList: {
annotations: [],
annotationFormat: 'aeneas',
controls: [],
editable: false,
linkEndpoints: false,
@@ -45,7 +46,7 @@ export function init(options = {}, ee = EventEmitter()) {
isAutomaticScroll: false,
};

const config = _assign(defaults, options);
const config = _defaultsDeep(options, defaults);
const zoomIndex = config.zoomLevels.indexOf(config.samplesPerPixel);

if (zoomIndex === -1) {
4 changes: 3 additions & 1 deletion src/render/ScrollHook.js
Original file line number Diff line number Diff line change
@@ -15,7 +15,9 @@ export default class {

if (playlist.isAutomaticScroll) {
const rect = node.getBoundingClientRect();
const cursorRect = node.querySelector('.cursor').getBoundingClientRect();
const cursor = node.querySelector('.cursor');
if (!cursor) return;
const cursorRect = cursor.getBoundingClientRect();

if (cursorRect.right > rect.right || cursorRect.right < 0) {
playlist.scrollLeft = playlist.playbackSeconds;
Loading