Skip to content

Instantly share code, notes, and snippets.

@johanbaaij
Created October 1, 2019 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johanbaaij/ffc2701376a6ffb3f3729e7511638c97 to your computer and use it in GitHub Desktop.
Save johanbaaij/ffc2701376a6ffb3f3729e7511638c97 to your computer and use it in GitHub Desktop.
Reasonably clever tap-tempo by Damien Clarke but as a Vue mixin
// Simplified version of this codepen https://codepen.io/dxinteractive/pen/bpaMMy authored by Damien Clark
const TOTAL_TAP_VALUES = 5
const MS_UNTIL_CHAIN_RESET = 2000
const SKIPPED_TAP_THRESHOLD_LOW = 1.75
const SKIPPED_TAP_THRESHOLD_HIGH = 2.75
export default {
data() {
return {
bpm: null,
buttonDown: false,
buttonDownOld: false,
sinceResetMS: 0,
sinceResetMSOld: 0,
beatMS: 500,
resetMS: this.millis(),
lastTapMS: 0,
lastTapSkipped: false,
beatProgress: 0,
tapDurations: [0, 0, 0, 0, 0],
tapDurationIndex: 0,
tapsInChain: 0
}
},
methods: {
millis() {
const d = new Date()
return d.getTime()
},
getAverageTapDuration() {
let amount = this.tapsInChain - 1
if (amount > TOTAL_TAP_VALUES) {
amount = TOTAL_TAP_VALUES
}
let runningTotal = 0
for (let i = 0; i < amount; i++) {
runningTotal += this.tapDurations[i]
}
return Math.floor(runningTotal / amount)
},
tap(ms) {
this.tapsInChain++
// eslint-disable-next-line eqeqeq
if (this.tapsInChain == 1) {
this.lastTapMS = ms
return -1
}
let duration = ms - this.lastTapMS
// detect if last duration was unreasonable
if (
this.tapsInChain > 1 &&
!this.lastTapSkipped &&
duration > this.beatMS * SKIPPED_TAP_THRESHOLD_LOW &&
duration < this.beatMS * SKIPPED_TAP_THRESHOLD_HIGH
) {
duration = Math.floor(duration * 0.5)
this.lastTapSkipped = true
} else {
this.lastTapSkipped = false
}
this.tapDurations[this.tapDurationIndex] = duration
this.tapDurationIndex++
// eslint-disable-next-line eqeqeq
if (this.tapDurationIndex == TOTAL_TAP_VALUES) {
this.tapDurationIndex = 0
}
const newBeatMS = this.getAverageTapDuration()
this.lastTapMS = ms
return newBeatMS
},
resetTapChain(ms) {
this.tapsInChain = 0
this.tapDurationIndex = 0
this.resetMS = ms
for (let i = 0; i < TOTAL_TAP_VALUES; i++) {
this.tapDurations[i] = 0
}
},
loop() {
const ms = this.millis()
// if a tap has occured...
if (this.buttonDown && !this.buttonDownOld) {
// start a new tap chain if last tap was over an amount of beats ago
if (this.lastTapMS + MS_UNTIL_CHAIN_RESET < ms) {
this.resetTapChain(ms)
}
const newBeatMS = this.tap(ms)
// eslint-disable-next-line eqeqeq
if (newBeatMS != -1) {
this.beatMS = newBeatMS
}
}
this.beatprogress = (this.sinceResetMS / this.beatMS) % 1
this.bpm = 60000 / this.beatMS
// set old vars
this.buttonDownOld = this.buttonDown
this.sinceResetMSOld = this.sinceResetMS
}
},
mounted() {
// begin loop
setInterval(this.loop, 10)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment