mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
remove old player core
This commit is contained in:
parent
1911b53712
commit
7748e59439
@ -109,9 +109,30 @@ export default class Player extends Core {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internalEvents = {
|
||||||
|
"player.state.update:loading": () => {
|
||||||
|
app.cores.sync.music.dispatchEvent("music.player.state.update", this.state)
|
||||||
|
},
|
||||||
|
"player.state.update:track_manifest": () => {
|
||||||
|
app.cores.sync.music.dispatchEvent("music.player.state.update", this.state)
|
||||||
|
},
|
||||||
|
"player.state.update:playback_status": () => {
|
||||||
|
app.cores.sync.music.dispatchEvent("music.player.state.update", this.state)
|
||||||
|
},
|
||||||
|
"player.seeked": (to) => {
|
||||||
|
app.cores.sync.music.dispatchEvent("music.player.seek", to)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
async onInitialize() {
|
async onInitialize() {
|
||||||
|
this.native_controls.initialize()
|
||||||
|
|
||||||
this.initializeAudioProcessors()
|
this.initializeAudioProcessors()
|
||||||
|
|
||||||
|
for (const [eventName, eventHandler] of Object.entries(this.internalEvents)) {
|
||||||
|
this.eventBus.on(eventName, eventHandler)
|
||||||
|
}
|
||||||
|
|
||||||
Observable.observe(this.state, async (changes) => {
|
Observable.observe(this.state, async (changes) => {
|
||||||
try {
|
try {
|
||||||
changes.forEach((change) => {
|
changes.forEach((change) => {
|
File diff suppressed because it is too large
Load Diff
@ -1,172 +0,0 @@
|
|||||||
export default class ProcessorNode {
|
|
||||||
constructor(PlayerCore) {
|
|
||||||
if (!PlayerCore) {
|
|
||||||
throw new Error("PlayerCore is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.PlayerCore = PlayerCore
|
|
||||||
this.audioContext = PlayerCore.audioContext
|
|
||||||
}
|
|
||||||
|
|
||||||
async _init() {
|
|
||||||
// check if has init method
|
|
||||||
if (typeof this.init === "function") {
|
|
||||||
await this.init(this.audioContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if has declared bus events
|
|
||||||
if (typeof this.busEvents === "object") {
|
|
||||||
Object.entries(this.busEvents).forEach((event, fn) => {
|
|
||||||
app.eventBus.on(event, fn)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.processor._last === "undefined") {
|
|
||||||
this.processor._last = this.processor
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
_attach(instance, index) {
|
|
||||||
if (typeof instance !== "object") {
|
|
||||||
instance = this.PlayerCore.currentAudioInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if has dependsOnSettings
|
|
||||||
if (Array.isArray(this.constructor.dependsOnSettings)) {
|
|
||||||
// check if the instance has the settings
|
|
||||||
if (!this.constructor.dependsOnSettings.every((setting) => app.cores.settings.get(setting))) {
|
|
||||||
this.console.warn(`Skipping attachment for [${this.constructor.refName ?? this.constructor.name}] node, cause is not passing the settings dependecy > ${this.constructor.dependsOnSettings.join(", ")}`)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if index is not defined, attach to the last node
|
|
||||||
if (!index) {
|
|
||||||
index = instance.attachedProcessors.length
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevNode = instance.attachedProcessors[index - 1]
|
|
||||||
const nextNode = instance.attachedProcessors[index + 1]
|
|
||||||
|
|
||||||
const currentIndex = this._findIndex(instance)
|
|
||||||
|
|
||||||
// check if is already attached
|
|
||||||
if (currentIndex !== false) {
|
|
||||||
this.console.warn(`[${this.constructor.refName ?? this.constructor.name}] node is already attached`)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// first check if has prevNode and if is connected to something
|
|
||||||
// if has, disconnect it
|
|
||||||
// if it not has, its means that is the first node, so connect to the media source
|
|
||||||
if (prevNode && prevNode.processor._last.numberOfOutputs > 0) {
|
|
||||||
//this.console.log(`[${this.constructor.refName ?? this.constructor.name}] node is already attached to the previous node, disconnecting...`)
|
|
||||||
// if has outputs, disconnect from the next node
|
|
||||||
prevNode.processor._last.disconnect()
|
|
||||||
|
|
||||||
// now, connect to the processor
|
|
||||||
prevNode.processor._last.connect(this.processor)
|
|
||||||
} else {
|
|
||||||
//this.console.log(`[${this.constructor.refName ?? this.constructor.name}] node is the first node, connecting to the media source...`)
|
|
||||||
instance.media.connect(this.processor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// now, check if it has a next node
|
|
||||||
// if has, connect to it
|
|
||||||
// if not, connect to the destination
|
|
||||||
if (nextNode) {
|
|
||||||
this.processor.connect(nextNode.processor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to the attachedProcessors
|
|
||||||
instance.attachedProcessors.splice(index, 0, this)
|
|
||||||
|
|
||||||
// handle instance mutation
|
|
||||||
if (typeof this.mutateInstance === "function") {
|
|
||||||
instance = this.mutateInstance(instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
_detach(instance) {
|
|
||||||
if (typeof instance !== "object") {
|
|
||||||
instance = this.PlayerCore.currentAudioInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
// find index of the node within the attachedProcessors serching for matching refName
|
|
||||||
const index = this._findIndex(instance)
|
|
||||||
|
|
||||||
if (!index) {
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve the previous and next nodes
|
|
||||||
const prevNode = instance.attachedProcessors[index - 1]
|
|
||||||
const nextNode = instance.attachedProcessors[index + 1]
|
|
||||||
|
|
||||||
// check if has previous node and if has outputs
|
|
||||||
if (prevNode && prevNode.processor._last.numberOfOutputs > 0) {
|
|
||||||
// if has outputs, disconnect from the previous node
|
|
||||||
prevNode.processor._last.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// disconnect
|
|
||||||
instance = this._destroy(instance)
|
|
||||||
|
|
||||||
// now, connect the previous node to the next node
|
|
||||||
if (prevNode && nextNode) {
|
|
||||||
prevNode.processor._last.connect(nextNode.processor)
|
|
||||||
} else {
|
|
||||||
// it means that this is the last node, so connect to the destination
|
|
||||||
prevNode.processor._last.connect(this.audioContext.destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
_destroy(instance) {
|
|
||||||
if (typeof instance !== "object") {
|
|
||||||
instance = this.PlayerCore.currentAudioInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = this._findIndex(instance)
|
|
||||||
|
|
||||||
if (!index) {
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processor.disconnect()
|
|
||||||
|
|
||||||
instance.attachedProcessors.splice(index, 1)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
_findIndex(instance) {
|
|
||||||
if (!instance) {
|
|
||||||
instance = this.PlayerCore.currentAudioInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!instance) {
|
|
||||||
this.console.warn(`Instance is not defined`)
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// find index of the node within the attachedProcessors serching for matching refName
|
|
||||||
const index = instance.attachedProcessors.findIndex((node) => {
|
|
||||||
return node.constructor.refName === this.constructor.refName
|
|
||||||
})
|
|
||||||
|
|
||||||
if (index === -1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
import AudioPlayerStorage from "../../storage"
|
import AudioPlayerStorage from "../../player.storage"
|
||||||
import ProcessorNode from "../../processorNode"
|
import ProcessorNode from "../node"
|
||||||
|
|
||||||
export default class CompressorProcessorNode extends ProcessorNode {
|
export default class CompressorProcessorNode extends ProcessorNode {
|
||||||
static refName = "compressor"
|
static refName = "compressor"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import ProcessorNode from "../../processorNode"
|
import ProcessorNode from "../node"
|
||||||
import AudioPlayerStorage from "../../storage"
|
import AudioPlayerStorage from "../../player.storage"
|
||||||
|
|
||||||
export default class EqProcessorNode extends ProcessorNode {
|
export default class EqProcessorNode extends ProcessorNode {
|
||||||
static refName = "eq"
|
static refName = "eq"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import AudioPlayerStorage from "../../storage"
|
import AudioPlayerStorage from "../../player.storage"
|
||||||
import ProcessorNode from "../../processorNode"
|
import ProcessorNode from "../node"
|
||||||
|
|
||||||
export default class GainProcessorNode extends ProcessorNode {
|
export default class GainProcessorNode extends ProcessorNode {
|
||||||
static refName = "gain"
|
static refName = "gain"
|
||||||
|
@ -4,17 +4,19 @@ export default {
|
|||||||
"tidal": async (manifest) => {
|
"tidal": async (manifest) => {
|
||||||
const resolvedManifest = await SyncModel.tidalCore.getTrackManifest(manifest.id)
|
const resolvedManifest = await SyncModel.tidalCore.getTrackManifest(manifest.id)
|
||||||
|
|
||||||
this.console.log(resolvedManifest)
|
|
||||||
|
|
||||||
manifest.source = resolvedManifest.playback.url
|
manifest.source = resolvedManifest.playback.url
|
||||||
|
|
||||||
manifest.title = resolvedManifest.metadata.title
|
if (!manifest.metadata) {
|
||||||
manifest.artist = resolvedManifest.metadata.artists.map(artist => artist.name).join(", ")
|
manifest.metadata = {}
|
||||||
manifest.album = resolvedManifest.metadata.album.title
|
}
|
||||||
|
|
||||||
|
manifest.metadata.title = resolvedManifest.metadata.title
|
||||||
|
manifest.metadata.artist = resolvedManifest.metadata.artists.map(artist => artist.name).join(", ")
|
||||||
|
manifest.metadata.album = resolvedManifest.metadata.album.title
|
||||||
|
|
||||||
const coverUID = resolvedManifest.metadata.album.cover.replace(/-/g, "/")
|
const coverUID = resolvedManifest.metadata.album.cover.replace(/-/g, "/")
|
||||||
|
|
||||||
manifest.cover = `https://resources.tidal.com/images/${coverUID}/1280x1280.jpg`
|
manifest.metadata.cover = `https://resources.tidal.com/images/${coverUID}/1280x1280.jpg`
|
||||||
|
|
||||||
return manifest
|
return manifest
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import store from "store"
|
|
||||||
|
|
||||||
export default class AudioPlayerStorage {
|
|
||||||
static storeKey = "audioPlayer"
|
|
||||||
|
|
||||||
static get(key) {
|
|
||||||
const data = store.get(AudioPlayerStorage.storeKey)
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
return data[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
static set(key, value) {
|
|
||||||
const data = store.get(AudioPlayerStorage.storeKey) ?? {}
|
|
||||||
|
|
||||||
data[key] = value
|
|
||||||
|
|
||||||
store.set(AudioPlayerStorage.storeKey, data)
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import AudioPlayerStorage from "../../player.storage"
|
|
||||||
import ProcessorNode from "../node"
|
|
||||||
|
|
||||||
export default class CompressorProcessorNode extends ProcessorNode {
|
|
||||||
static refName = "compressor"
|
|
||||||
static dependsOnSettings = ["player.compressor"]
|
|
||||||
static defaultCompressorValues = {
|
|
||||||
threshold: -50,
|
|
||||||
knee: 40,
|
|
||||||
ratio: 12,
|
|
||||||
attack: 0.003,
|
|
||||||
release: 0.25,
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
compressorValues: AudioPlayerStorage.get("compressor") ?? CompressorProcessorNode.defaultCompressorValues,
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeToPublic = {
|
|
||||||
modifyValues: function (values) {
|
|
||||||
this.state.compressorValues = {
|
|
||||||
...this.state.compressorValues,
|
|
||||||
...values,
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioPlayerStorage.set("compressor", this.state.compressorValues)
|
|
||||||
|
|
||||||
this.applyValues()
|
|
||||||
}.bind(this),
|
|
||||||
resetDefaultValues: function () {
|
|
||||||
this.exposeToPublic.modifyValues(CompressorProcessorNode.defaultCompressorValues)
|
|
||||||
|
|
||||||
return this.state.compressorValues
|
|
||||||
}.bind(this),
|
|
||||||
detach: this._detach.bind(this),
|
|
||||||
attach: this._attach.bind(this),
|
|
||||||
values: this.state.compressorValues,
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(AudioContext) {
|
|
||||||
if (!AudioContext) {
|
|
||||||
throw new Error("AudioContext is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processor = AudioContext.createDynamicsCompressor()
|
|
||||||
|
|
||||||
this.applyValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
applyValues() {
|
|
||||||
Object.keys(this.state.compressorValues).forEach((key) => {
|
|
||||||
this.processor[key].value = this.state.compressorValues[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
import ProcessorNode from "../node"
|
|
||||||
import AudioPlayerStorage from "../../player.storage"
|
|
||||||
|
|
||||||
export default class EqProcessorNode extends ProcessorNode {
|
|
||||||
static refName = "eq"
|
|
||||||
static lock = true
|
|
||||||
|
|
||||||
static defaultEqValue = {
|
|
||||||
32: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
64: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
125: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
250: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
500: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
1000: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
2000: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
4000: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
8000: {
|
|
||||||
gain: 0,
|
|
||||||
},
|
|
||||||
16000: {
|
|
||||||
gain: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
eqValues: AudioPlayerStorage.get("eq_values") ?? EqProcessorNode.defaultEqValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeToPublic = {
|
|
||||||
modifyValues: function (values) {
|
|
||||||
Object.keys(values).forEach((key) => {
|
|
||||||
if (isNaN(key)) {
|
|
||||||
delete values[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.state.eqValues = {
|
|
||||||
...this.state.eqValues,
|
|
||||||
...values,
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioPlayerStorage.set("eq_values", this.state.eqValues)
|
|
||||||
|
|
||||||
this.applyValues()
|
|
||||||
}.bind(this),
|
|
||||||
resetDefaultValues: function () {
|
|
||||||
this.exposeToPublic.modifyValues(EqProcessorNode.defaultEqValue)
|
|
||||||
|
|
||||||
return this.state
|
|
||||||
}.bind(this),
|
|
||||||
values: () => this.state,
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
if (!this.audioContext) {
|
|
||||||
throw new Error("audioContext is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processor = this.audioContext.createGain()
|
|
||||||
|
|
||||||
this.processor.gain.value = 1
|
|
||||||
|
|
||||||
this.processor.eqNodes = []
|
|
||||||
|
|
||||||
const values = Object.entries(this.state.eqValues).map((entry) => {
|
|
||||||
return {
|
|
||||||
freq: parseFloat(entry[0]),
|
|
||||||
gain: parseFloat(entry[1].gain),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
values.forEach((eqValue, index) => {
|
|
||||||
// chekc if freq and gain is valid
|
|
||||||
if (isNaN(eqValue.freq)) {
|
|
||||||
eqValue.freq = 0
|
|
||||||
}
|
|
||||||
if (isNaN(eqValue.gain)) {
|
|
||||||
eqValue.gain = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processor.eqNodes[index] = this.audioContext.createBiquadFilter()
|
|
||||||
this.processor.eqNodes[index].type = "peaking"
|
|
||||||
this.processor.eqNodes[index].frequency.value = eqValue.freq
|
|
||||||
this.processor.eqNodes[index].gain.value = eqValue.gain
|
|
||||||
})
|
|
||||||
|
|
||||||
// connect nodes
|
|
||||||
for await (let [index, eqNode] of this.processor.eqNodes.entries()) {
|
|
||||||
const nextNode = this.processor.eqNodes[index + 1]
|
|
||||||
|
|
||||||
if (index === 0) {
|
|
||||||
this.processor.connect(eqNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextNode) {
|
|
||||||
eqNode.connect(nextNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set last processor for processor node can properly connect to the next node
|
|
||||||
this.processor._last = this.processor.eqNodes.at(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
applyValues() {
|
|
||||||
// apply to current instance
|
|
||||||
this.processor.eqNodes.forEach((processor) => {
|
|
||||||
const gainValue = this.state.eqValues[processor.frequency.value].gain
|
|
||||||
|
|
||||||
if (processor.gain.value !== gainValue) {
|
|
||||||
this.console.debug(`[EQ] Applying values to ${processor.frequency.value} Hz frequency with gain ${gainValue}`)
|
|
||||||
processor.gain.value = this.state.eqValues[processor.frequency.value].gain
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
import AudioPlayerStorage from "../../player.storage"
|
|
||||||
import ProcessorNode from "../node"
|
|
||||||
|
|
||||||
export default class GainProcessorNode extends ProcessorNode {
|
|
||||||
static refName = "gain"
|
|
||||||
|
|
||||||
static lock = true
|
|
||||||
|
|
||||||
static defaultValues = {
|
|
||||||
gain: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
gain: AudioPlayerStorage.get("gain") ?? GainProcessorNode.defaultValues.gain,
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeToPublic = {
|
|
||||||
modifyValues: function (values) {
|
|
||||||
this.state = {
|
|
||||||
...this.state,
|
|
||||||
...values,
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioPlayerStorage.set("gain", this.state.gain)
|
|
||||||
|
|
||||||
this.applyValues()
|
|
||||||
}.bind(this),
|
|
||||||
resetDefaultValues: function () {
|
|
||||||
this.exposeToPublic.modifyValues(GainProcessorNode.defaultValues)
|
|
||||||
|
|
||||||
return this.state
|
|
||||||
}.bind(this),
|
|
||||||
values: () => this.state,
|
|
||||||
}
|
|
||||||
|
|
||||||
applyValues() {
|
|
||||||
// apply to current instance
|
|
||||||
this.processor.gain.value = app.cores.player.volume() * this.state.gain
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
if (!this.audioContext) {
|
|
||||||
throw new Error("audioContext is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processor = this.audioContext.createGain()
|
|
||||||
|
|
||||||
this.applyValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
mutateInstance(instance) {
|
|
||||||
if (!instance) {
|
|
||||||
throw new Error("instance is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.gainNode = this.processor
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import SyncModel from "comty.js/models/sync"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
"tidal": async (manifest) => {
|
|
||||||
const resolvedManifest = await SyncModel.tidalCore.getTrackManifest(manifest.id)
|
|
||||||
|
|
||||||
manifest.source = resolvedManifest.playback.url
|
|
||||||
|
|
||||||
if (!manifest.metadata) {
|
|
||||||
manifest.metadata = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest.metadata.title = resolvedManifest.metadata.title
|
|
||||||
manifest.metadata.artist = resolvedManifest.metadata.artists.map(artist => artist.name).join(", ")
|
|
||||||
manifest.metadata.album = resolvedManifest.metadata.album.title
|
|
||||||
|
|
||||||
const coverUID = resolvedManifest.metadata.album.cover.replace(/-/g, "/")
|
|
||||||
|
|
||||||
manifest.metadata.cover = `https://resources.tidal.com/images/${coverUID}/1280x1280.jpg`
|
|
||||||
|
|
||||||
return manifest
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user