Skip to content

Commit 36885e9

Browse files
committed
add playback
1 parent c7f58da commit 36885e9

File tree

17 files changed

+957
-73
lines changed

17 files changed

+957
-73
lines changed

audio/piano/a1.mp3

94.3 KB
Binary file not shown.

audio/piano/a2.mp3

91.2 KB
Binary file not shown.

audio/piano/a3.mp3

90.4 KB
Binary file not shown.

audio/piano/a4.mp3

77.1 KB
Binary file not shown.

audio/piano/a5.mp3

94.7 KB
Binary file not shown.

audio/piano/a6.mp3

94.1 KB
Binary file not shown.

audio/piano/a7.mp3

39.6 KB
Binary file not shown.

src/editor/editor.js

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ export class Editor
4242
.upsertMeterChange(new MeterChange(new Rational(0, 4), 4, 4))
4343
.upsertMeterChange(new MeterChange(new Rational(11, 4), 5, 4))
4444

45-
.upsertKeyChange(new KeyChange(new Rational(0, 4), new Key(0, 0, scales.major.pitches)))
46-
.upsertKeyChange(new KeyChange(new Rational(7, 4), new Key(5, 1, scales.minor.pitches)))
47-
.upsertKeyChange(new KeyChange(new Rational(9, 4), new Key(7, -1, scales.doubleHarmonic.pitches)))
45+
.upsertKeyChange(new KeyChange(new Rational(0, 4), new Key(0, 0, scales[0].pitches)))
46+
.upsertKeyChange(new KeyChange(new Rational(7, 4), new Key(5, 1, scales[5].pitches)))
47+
.upsertKeyChange(new KeyChange(new Rational(9, 4), new Key(7, -1, scales[7].pitches)))
4848

4949
this.timeScale = 200
5050
this.timeScroll = 0
@@ -60,10 +60,14 @@ export class Editor
6060
this.refreshLayout()
6161

6262
this.cursorTime = new Range(new Rational(0), new Rational(0))
63-
this.cursorTrack = { start: 0, end: 0 }
63+
this.cursorTrack = { start: 1, end: 1 }
6464
this.cursorShow = true
6565
this.insertionDuration = new Rational(1, 4)
6666

67+
this.playing = false
68+
this.playbackTime = 0
69+
this.playbackTimeRational = new Rational(0)
70+
6771
this.mouseDown = false
6872
this.mouseDownDate = new Date()
6973
this.mouseDownData = { pos: { x: -1, y: -1 }, time: new Rational(0) }
@@ -120,6 +124,43 @@ export class Editor
120124
setSong(song)
121125
{
122126
this.song = song
127+
this.toolboxRefreshFn()
128+
this.draw()
129+
}
130+
131+
132+
setPlayback(playing)
133+
{
134+
this.playing = playing
135+
this.playbackTime = this.cursorTime.min().asFloat()
136+
this.scrollTimeIntoView(this.cursorTime.min())
137+
138+
this.toolboxRefreshFn()
139+
this.draw()
140+
141+
this.onPlaybackToggle(this.playing)
142+
}
143+
144+
145+
rewind()
146+
{
147+
this.selectionClear()
148+
149+
this.cursorTime = new Range(new Rational(0), new Rational(0))
150+
this.cursorShow = true
151+
this.playbackTime = 0
152+
this.playbackTimeRational = new Rational(0)
153+
154+
if (this.playing)
155+
{
156+
this.setPlayback(false)
157+
this.setPlayback(true)
158+
}
159+
else
160+
this.scrollTimeIntoView(this.cursorTime.start)
161+
162+
this.toolboxRefreshFn()
163+
this.draw()
123164
}
124165

125166

@@ -183,6 +224,18 @@ export class Editor
183224
}
184225

185226

227+
scrollPlaybackIntoView(time)
228+
{
229+
const margin = this.timeSnap.multiply(new Rational(16))
230+
231+
if (time.compare(this.screenRange.end.subtract(margin)) > 0)
232+
this.timeScroll = time.subtract(margin).asFloat()
233+
234+
else if (time.compare(this.screenRange.start.add(margin)) < 0)
235+
this.timeScroll = time.subtract(margin).asFloat()
236+
}
237+
238+
186239
*enumerateTracksUnderCursor()
187240
{
188241
const trackMin = Math.min(this.cursorTrack.start, this.cursorTrack.end)
@@ -368,6 +421,14 @@ export class Editor
368421
this.mouseTime = this.getTimeAtPos(this.mousePos)
369422
this.mouseTrack = this.tracks.findIndex(track => track.area.contains(this.mousePos))
370423

424+
const edgeAutoScroll = () =>
425+
{
426+
if (this.mousePos.x > this.width - this.mouseEdgeScrollThreshold)// && this.mousePos.x > mousePosPrev.x)
427+
this.timeScroll += this.timeSnap.asFloat() * this.mouseEdgeScrollSpeed
428+
else if (this.mousePos.x < this.mouseEdgeScrollThreshold)// && this.mousePos.x < mousePosPrev.x)
429+
this.timeScroll -= this.timeSnap.asFloat() * this.mouseEdgeScrollSpeed
430+
}
431+
371432
if (this.mouseDown)
372433
{
373434
if (this.mouseDownAction == Editor.ACTION_PAN)
@@ -384,17 +445,15 @@ export class Editor
384445
this.cursorTrack = { ...this.cursorTrack, end: this.mouseTrack }
385446

386447
this.selectUnderCursor()
387-
388-
if (this.mousePos.x > this.width - this.mouseEdgeScrollThreshold)// && this.mousePos.x > mousePosPrev.x)
389-
this.timeScroll += this.timeSnap.asFloat() * this.mouseEdgeScrollSpeed
390-
else if (this.mousePos.x < this.mouseEdgeScrollThreshold)// && this.mousePos.x < mousePosPrev.x)
391-
this.timeScroll -= this.timeSnap.asFloat() * this.mouseEdgeScrollSpeed
448+
edgeAutoScroll()
392449
}
393450
else
394451
{
395452
this.curSanitizationMode = "mouse"
396453
for (const track of this.tracks)
397454
track.onDrag({ x: this.mousePos.x - track.area.x, y: this.mousePos.y - track.area.y })
455+
456+
edgeAutoScroll()
398457
}
399458
}
400459
else
@@ -548,6 +607,12 @@ export class Editor
548607
}
549608
break
550609
}
610+
case " ":
611+
{
612+
this.setPlayback(!this.playing)
613+
handled = true
614+
break
615+
}
551616
case "enter":
552617
case "escape":
553618
{
@@ -661,6 +726,7 @@ export class Editor
661726
this.ctx.fillRect(0, 0, this.width, this.height)
662727

663728
this.screenRange = new Range(this.getTimeAtPos({ x: 0, y: 0 }), this.getTimeAtPos({ x: this.width, y: 0 }).add(this.timeSnap))
729+
this.playbackTimeRational = Rational.fromFloat(this.playbackTime, new Rational(1, 64))
664730

665731
for (const [curMeter, nextMeter] of this.song.meterChanges.enumerateAffectingRangePairwise(this.screenRange))
666732
{
@@ -702,7 +768,7 @@ export class Editor
702768
}
703769
}
704770

705-
if (this.cursorShow)
771+
if (this.cursorShow && !this.playing)
706772
this.drawCursorRect()
707773

708774
for (const keyChange of this.song.keyChanges.enumerateOverlappingRange(this.screenRange))
@@ -741,12 +807,15 @@ export class Editor
741807
this.ctx.restore()
742808
}
743809

744-
if (this.cursorShow)
810+
if (this.cursorShow && !this.playing)
745811
{
746812
this.drawCursorBeam(this.cursorTime.min(), true)
747813
this.drawCursorBeam(this.cursorTime.max(), false)
748814
}
749815

816+
if (this.playing)
817+
this.drawPlaybackBeam(this.playbackTime)
818+
750819
this.ctx.restore()
751820
}
752821

@@ -807,4 +876,27 @@ export class Editor
807876

808877
this.ctx.restore()
809878
}
879+
880+
881+
drawPlaybackBeam(time)
882+
{
883+
const trackMin = 0
884+
const trackMax = this.tracks.length - 1
885+
886+
this.ctx.save()
887+
888+
const offset = time - this.timeScroll
889+
const x = offset * this.timeScale
890+
891+
this.ctx.strokeStyle = "#f00"
892+
this.ctx.lineCap = "round"
893+
this.ctx.lineWidth = 3
894+
895+
this.ctx.beginPath()
896+
this.ctx.moveTo(this.tracks[trackMin].area.x + x, this.tracks[trackMin].area.y)
897+
this.ctx.lineTo(this.tracks[trackMax].area.x + x, this.tracks[trackMax].area.y + this.tracks[trackMax].area.h)
898+
this.ctx.stroke()
899+
900+
this.ctx.restore()
901+
}
810902
}

src/editor/editorChords.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export class EditorChords
106106

107107
this.alterSelectedChords((data, origData, changes) =>
108108
{
109-
const keyChange = this.owner.song.keyChanges.findActiveAt(data.range.start) || new KeyChange(data.range.start, new Key(0, 0, scales.major.pitches))
109+
const keyChange = this.owner.song.keyChanges.findActiveAt(data.range.start) || new KeyChange(data.range.start, new Key(0, 0, scales[0].pitches))
110110
const scaleDegree = getScaleDegreeForPitch(keyChange.key, data.chord.rootPitch)
111111
changes.chord = data.chord.withChanges({ rootPitch: getPitchForScaleDegree(keyChange.key, scaleDegree + offset) })
112112
})
@@ -300,8 +300,8 @@ export class EditorChords
300300
{
301301
for (const pair of this.owner.song.keyChanges.enumerateAffectingRangePairwise(this.owner.screenRange))
302302
{
303-
const curKey = pair[0] || new KeyChange(this.owner.screenRange.start, new Key(0, 0, scales.major.pitches))
304-
const nextKey = pair[1] || new KeyChange(this.owner.screenRange.end, new Key(0, 0, scales.major.pitches))
303+
const curKey = pair[0] || new KeyChange(this.owner.screenRange.start, new Key(0, 0, scales[0].pitches))
304+
const nextKey = pair[1] || new KeyChange(this.owner.screenRange.end, new Key(0, 0, scales[0].pitches))
305305

306306
const xStart = (curKey .time.asFloat() - this.owner.timeScroll) * this.owner.timeScale
307307
const xEnd = (nextKey.time.asFloat() - this.owner.timeScroll) * this.owner.timeScale
@@ -359,7 +359,9 @@ export class EditorChords
359359
this.owner.ctx.fillText(chordData.symbol[2], rect.x + rect.w / 2 + mainStrWidth.width / 2, rect.y + rect.h / 2 - 8, rect.w - 6)
360360
}
361361

362-
if (this.owner.selection.has(chord.id))
362+
const playbackOverlaps = (this.owner.playing && chord.range.overlapsPoint(this.owner.playbackTimeRational))
363+
364+
if (playbackOverlaps || (!this.owner.playing && this.owner.selection.has(chord.id)))
363365
{
364366
this.owner.ctx.globalAlpha = 0.5
365367
this.owner.ctx.fillStyle = "#fff"
@@ -368,7 +370,7 @@ export class EditorChords
368370
this.owner.ctx.globalAlpha = 1
369371
}
370372

371-
if (this.hoverId == chord.id)
373+
if (playbackOverlaps || this.hoverId == chord.id)
372374
{
373375
this.owner.ctx.globalAlpha = 0.5
374376
this.owner.ctx.fillStyle = "#fee"

src/editor/editorNotes.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ export class EditorNotes
5555
this.hoverId = -1
5656
for (const pair of this.owner.song.keyChanges.enumerateAffectingRangePairwise(this.owner.screenRange))
5757
{
58-
const curKey = pair[0] || new KeyChange(this.owner.screenRange.start, new Key(0, 0, scales.major.pitches))
59-
const nextKey = pair[1] || new KeyChange(this.owner.screenRange.end, new Key(0, 0, scales.major.pitches))
58+
const curKey = pair[0] || new KeyChange(this.owner.screenRange.start, new Key(0, 0, scales[0].pitches))
59+
const nextKey = pair[1] || new KeyChange(this.owner.screenRange.end, new Key(0, 0, scales[0].pitches))
6060

6161
const xStart = (curKey .time.asFloat() - this.owner.timeScroll) * this.owner.timeScale
6262
const xEnd = (nextKey.time.asFloat() - this.owner.timeScroll) * this.owner.timeScale
@@ -129,7 +129,7 @@ export class EditorNotes
129129

130130
this.alterSelectedNotes((data, origData, changes) =>
131131
{
132-
const keyChange = this.owner.song.keyChanges.findActiveAt(data.range.start) || new KeyChange(data.range.start, new Key(0, 0, scales.major.pitches))
132+
const keyChange = this.owner.song.keyChanges.findActiveAt(data.range.start) || new KeyChange(data.range.start, new Key(0, 0, scales[0].pitches))
133133
const scaleDegree = getScaleDegreeForPitch(keyChange.key, data.pitch)
134134
changes.pitch = getPitchForScaleDegree(keyChange.key, scaleDegree + offset)
135135
})
@@ -318,7 +318,7 @@ export class EditorNotes
318318

319319
if (this.owner.mouseDownAction & Editor.ACTION_DRAG_PITCH)
320320
{
321-
const keyChange = this.owner.song.keyChanges.findActiveAt(noteOrigData.range.start) || new KeyChange(noteOrigData.range.start, new Key(0, 0, scales.major.pitches))
321+
const keyChange = this.owner.song.keyChanges.findActiveAt(noteOrigData.range.start) || new KeyChange(noteOrigData.range.start, new Key(0, 0, scales[0].pitches))
322322
const scaleDegree = getScaleDegreeForPitch(keyChange.key, noteOrigData.pitch)
323323
const newPitch = getPitchForScaleDegree(keyChange.key, scaleDegree + rowOffset)
324324
changes.pitch = newPitch
@@ -339,8 +339,8 @@ export class EditorNotes
339339
{
340340
for (const pair of this.owner.song.keyChanges.enumerateAffectingRangePairwise(this.owner.screenRange))
341341
{
342-
const curKey = pair[0] || new KeyChange(this.owner.screenRange.start, new Key(0, 0, scales.major.pitches))
343-
const nextKey = pair[1] || new KeyChange(this.owner.screenRange.end, new Key(0, 0, scales.major.pitches))
342+
const curKey = pair[0] || new KeyChange(this.owner.screenRange.start, new Key(0, 0, scales[0].pitches))
343+
const nextKey = pair[1] || new KeyChange(this.owner.screenRange.end, new Key(0, 0, scales[0].pitches))
344344

345345
const xStart = (curKey .time.asFloat() - this.owner.timeScroll) * this.owner.timeScale
346346
const xEnd = (nextKey.time.asFloat() - this.owner.timeScroll) * this.owner.timeScale
@@ -384,7 +384,9 @@ export class EditorNotes
384384
this.owner.ctx.fillStyle = color
385385
this.owner.ctx.fillRect(rect.x + 1, rect.y, rect.w - 2, rect.h)
386386

387-
if (this.owner.selection.has(note.id))
387+
const playbackOverlaps = (this.owner.playing && note.range.overlapsPoint(this.owner.playbackTimeRational))
388+
389+
if (playbackOverlaps || (!this.owner.playing && this.owner.selection.has(note.id)))
388390
{
389391
this.owner.ctx.globalAlpha = 0.5
390392
this.owner.ctx.fillStyle = "#fff"
@@ -393,7 +395,7 @@ export class EditorNotes
393395
this.owner.ctx.globalAlpha = 1
394396
}
395397

396-
if (this.hoverId == note.id)
398+
if (playbackOverlaps || this.hoverId == note.id)
397399
{
398400
this.owner.ctx.globalAlpha = 0.5
399401
this.owner.ctx.fillStyle = "#fee"

0 commit comments

Comments
 (0)