chomens-bot-js/plugins/music.js
2023-03-01 07:43:53 +07:00

187 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const path = require('path')
const { Midi } = require('@tonejs/midi')
const { convertMidi } = require('../util/midi_converter')
const convertNBS = require('../util/nbs_converter')
const parseTXTSong = require('../util/txt_song_parser')
const soundNames = {
harp: 'minecraft:block.note_block.harp',
basedrum: 'minecraft:block.note_block.basedrum',
snare: 'minecraft:block.note_block.snare',
hat: 'minecraft:block.note_block.hat',
bass: 'minecraft:block.note_block.bass',
flute: 'minecraft:block.note_block.flute',
bell: 'minecraft:block.note_block.bell',
guitar: 'minecraft:block.note_block.guitar',
chime: 'minecraft:block.note_block.chime',
xylophone: 'minecraft:block.note_block.xylophone',
iron_xylophone: 'minecraft:block.note_block.iron_xylophone',
cow_bell: 'minecraft:block.note_block.cow_bell',
didgeridoo: 'minecraft:block.note_block.didgeridoo',
bit: 'minecraft:block.note_block.bit',
banjo: 'minecraft:block.note_block.banjo',
pling: 'minecraft:block.note_block.pling'
}
function inject (bot) {
bot.music = function () {}
bot.music.song = null
bot.music.loop = 0
bot.music.queue = []
let time = 0
let startTime = 0
let noteIndex = 0
bot.music.skip = function () {
if (bot.music.loop === 2) {
bot.music.queue.push(bot.music.queue.shift())
bot.music.play(bot.music.queue[0])
} else {
bot.music.queue.shift()
}
resetTime()
}
const bossbarName = 'chomens_bot:music' // maybe make this in the config?
const selector = '@a[tag=!nomusic,tag=!chomens_bot_nomusic]'
const interval = setInterval(async () => {
if (!bot.music.queue.length) return
bot.music.song = bot.music.queue[0]
time = Date.now() - startTime
// bot.core.run('minecraft:title @a[tag=!nomusic] actionbar ' + JSON.stringify(toComponent()))
// is spamming commands in core as a self care a good idea?
// btw this is totallynotskidded™ from my song bot except the bossbarName (above)
bot.core.run(`minecraft:bossbar add ${bossbarName} ""`) // is setting the name to "" as a placeholder a good idea?
bot.core.run(`minecraft:bossbar set ${bossbarName} players ${selector}`)
bot.core.run(`minecraft:bossbar set ${bossbarName} name ${JSON.stringify(toComponent())}`)
bot.core.run(`minecraft:bossbar set ${bossbarName} color yellow`) // should i use purple lol
bot.core.run(`minecraft:bossbar set ${bossbarName} visible true`)
bot.core.run(`minecraft:bossbar set ${bossbarName} value ${Math.floor(time)}`)
bot.core.run(`minecraft:bossbar set ${bossbarName} max ${bot.music.song.length}`)
while (bot.music.song?.notes[noteIndex]?.time <= time) {
const note = bot.music.song.notes[noteIndex]
const floatingPitch = 2 ** ((note.pitch - 12) / 12.0)
bot.core.run(`minecraft:execute as ${selector} at @s run playsound ${soundNames[note.instrument]} record @s ~ ~ ~ ${note.volume} ${floatingPitch}`)
noteIndex++
if (noteIndex >= bot.music.song.notes.length) {
bot.tellraw('@a', [
{
text: 'Finished playing '
},
{
text: bot.music.song.name,
color: 'gold'
}
])
if (bot.music.loop === 1) {
resetTime()
return
}
if (bot.music.loop === 2) {
resetTime()
bot.music.queue.push(bot.music.queue.shift())
bot.music.play(bot.music.queue[0])
return
}
bot.music.queue.shift()
bot.music.song = null // useless?
if (!bot.music.queue[0]) {
bot.music.stop()
return
}
if (bot.music.queue[0].notes.length > 0) {
if (bot.music.queue.length !== 1) resetTime()
bot.music.play(bot.music.queue[0])
return
}
}
}
}, 50)
bot.on('end', () => {
clearInterval(interval)
})
bot.music.load = async function (buffer, fallbackName = '[unknown]') {
let song
switch (path.extname(fallbackName)) {
case '.nbs':
song = convertNBS(buffer)
if (song.name === '') song.name = fallbackName
break
case '.txt':
song = parseTXTSong(buffer.toString())
song.name = fallbackName
break
default:
// TODO: use worker_threads so the entire bot doesn't freeze (for example parsing we are number 1 black midi)
// eslint-disable-next-line no-case-declarations
const midi = new Midi(buffer)
song = convertMidi(midi)
if (song.name === '') song.name = fallbackName
break
}
return song
}
bot.music.play = function (song) {
if (bot.music.queue.length === 1) resetTime()
}
bot.music.stop = function () {
bot.music.song = null
bot.music.loop = 0
bot.music.queue = []
resetTime()
}
function resetTime () {
time = 0
startTime = Date.now()
noteIndex = 0
bot.core.run(`minecraft:bossbar remove ${bossbarName}`) // maybe not a good place to put it here but idk
}
function formatTime (time) {
const seconds = Math.floor(time / 1000)
return `${Math.floor(seconds / 60)}:${(seconds % 60).toString().padStart(2, '0')}`
}
function toComponent () {
const component = [
// { text: '[', color: 'dark_gray' },
// { text: 'ChomeNS Bot', color: 'yellow' },
// { text: '] ', color: 'dark_gray' },
// { text: 'Now Playing', color: 'gold' },
// { text: ' | ', color: 'dark_gray' },
{ text: bot.music.song.name, color: 'green' },
{ text: ' | ', color: 'dark_gray' },
{ text: formatTime(time), color: 'gray' },
{ text: ' / ', color: 'dark_gray' },
{ text: formatTime(bot.music.song.length), color: 'gray' },
{ text: ' | ', color: 'dark_gray' },
{ text: noteIndex, color: 'gray' },
{ text: ' / ', color: 'dark_gray' },
{ text: bot.music.song.notes.length, color: 'gray' }
]
if (bot.music.loop === 1) {
component.push({ text: ' | ', color: 'dark_gray' })
component.push({ text: 'Looping Current', color: 'green' })
}
if (bot.music.loop === 2) {
component.push({ text: ' | ', color: 'dark_gray' })
component.push({ text: 'Looping All', color: 'green' })
}
return component
}
}
module.exports = { inject }