playlist/playlist.js

211 lines
4.6 KiB
JavaScript

// jshint asi:true
// HTML5 Playlist by Neale Pickett
// https://github.com/nealey/playlst
var base = "."
function loadTrack(e) {
let li = e.target
let audio = document.querySelector("#audio")
audio.src = base + "/" + li.textContent
audio.load()
// Update "current"
for (let cur of document.querySelectorAll(".current")) {
cur.classList.remove("current")
}
li.classList.add("current")
}
function clickOn(element) {
let e = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true
})
element.dispatchEvent(e)
}
function prev() {
let cur = document.querySelector(".current")
let prev = cur.previousElementSibling
if (prev) {
cur = prev
}
clickOn(cur)
}
function next() {
let cur = document.querySelector(".current")
let next = cur.nextElementSibling
if (next) {
cur = next
}
clickOn(cur)
}
function ended() {
next()
}
function mmss(s) {
let mm = Math.floor(s / 60)
let ss = Math.floor(s % 60)
if (ss < 10) {
ss = "0" + ss
}
return mm + ":" + ss
}
function durationchange(e) {
let duration = e.target.duration
document.querySelector("#duration").textContent = mmss(duration)
timeupdate(e)
}
function volumechange(e) {
document.querySelector("#vol").value = e.target.volume
}
function timeupdate(e) {
let currentTime = e.target.currentTime
let duration = e.target.duration || 1
let tgt = document.querySelector("#currentTime")
let pos = document.querySelector("#pos")
pos.value = currentTime / duration
tgt.textContent = mmss(currentTime)
if (duration - currentTime < 20) {
tgt.classList.add("fin")
} else {
tgt.classList.remove("fin")
}
}
function setPos(e) {
let val = e.target.value
let audio = document.querySelector("#audio")
audio.currentTime = audio.duration * val
}
function setGain(e) {
let val = e.target.value
let audio = document.querySelector("#audio")
audio.volume = val
}
function keydown(e) {
let audio = document.querySelector("#audio")
switch (event.key) {
case " ": // space bar
if (audio.paused) {
audio.play()
} else {
audio.pause()
}
break
case "ArrowDown": // Next track
next()
break
case "ArrowUp": // Previous track
prev()
break
}
}
function midiMessage(e) {
let audio = document.querySelector("#audio")
let data = e.data
let ctrl = data[1]
let val = data[2]
if ((data[0] == 0xb0) || (data[0] == 0xbf)) {
switch (ctrl) {
case 0: // master volume slider
audio.volume = val / 127
document.querySelector("#vol").value = audio.volume
break
case 41: // play button
if (val == 127) {
audio.play()
}
break
case 42: // stop button
if (val == 127) {
audio.pause()
}
break
case 58: // prev button
if (val == 127) {
prev()
}
break
case 59: // next button
if (val == 127) {
next()
}
break
}
}
}
function handleMidiAccess(access) {
for (let input of access.inputs.values()) {
input.addEventListener("midimessage", midiMessage)
}
for (let output of access.outputs.values()) {
if (output.name == "nanoKONTROL2 MIDI 1") {
controller = output
output.send([0xf0, 0x42, 0x40, 0x00, 0x01, 0x13, 0x00, 0x00, 0x00, 0x01, 0xf7]); // Native Mode (lets us control LEDs, requires sysex privilege)
output.send([0xbf, 0x2a, 0x7f]); // Stop
output.send([0xbf, 0x29, 0x7f]); // Play
}
}
}
function run() {
let audio = document.querySelector("#audio")
// Set up events:
// - Prev/Next buttons
// - ended / timeupdate events on audio
// - Track items
document.querySelector("#prev").addEventListener("click", prev)
document.querySelector("#next").addEventListener("click", next)
document.querySelector("#pos").addEventListener("input", setPos)
document.querySelector("#vol").addEventListener("input", setGain)
audio.addEventListener("ended", ended)
audio.addEventListener("timeupdate", timeupdate)
audio.addEventListener("durationchange", durationchange)
audio.addEventListener("volumechange", volumechange)
for (let li of document.querySelectorAll("#playlist li")) {
li.addEventListener("click", loadTrack)
}
document.querySelector("#vol").value = audio.volume
// Bind keypress events
// - space: play/pause
//
document.addEventListener("keydown", keydown)
// Load up first track
document.querySelector("#playlist li").classList.add("current")
prev()
navigator.requestMIDIAccess({sysex: true}).then(handleMidiAccess)
}
window.addEventListener("load", run)