mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
support sliders with profiles
This commit is contained in:
parent
510ad757bb
commit
fec281dece
@ -0,0 +1,127 @@
|
||||
import React from "react"
|
||||
import { Select, Input, Button, Modal } from "antd"
|
||||
import { Icons } from "components/Icons"
|
||||
|
||||
import Sliders from "../sliderValues"
|
||||
|
||||
export default (props) => {
|
||||
const [selectedPreset, setSelectedPreset] = React.useState(props.controller.presets.currentPresetKey)
|
||||
const [presets, setPresets] = React.useState(props.controller.presets.presets ?? {})
|
||||
|
||||
const createPreset = (key) => {
|
||||
setPresets(props.controller.createPreset(key))
|
||||
setSelectedPreset(key)
|
||||
}
|
||||
|
||||
const handleCreateNewPreset = () => {
|
||||
app.layout.modal.open("create_preset", (props) => {
|
||||
const [presetKey, setPresetKey] = React.useState("")
|
||||
|
||||
return <div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<h3>New preset</h3>
|
||||
|
||||
<Input
|
||||
placeholder="New preset name"
|
||||
value={presetKey}
|
||||
onChange={(e) => {
|
||||
setPresetKey(e.target.value.trim())
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!presetKey || presetKey.length === 0}
|
||||
onClick={() => {
|
||||
createPreset(presetKey)
|
||||
|
||||
props.close()
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeletePreset = () => {
|
||||
Modal.confirm({
|
||||
title: "Delete preset",
|
||||
content: "Are you sure you want to delete this preset?",
|
||||
onOk: () => {
|
||||
props.controller.deletePreset(selectedPreset)
|
||||
setPresets(props.controller.presets.presets ?? {})
|
||||
setSelectedPreset(props.controller.presets.currentPresetKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: "new",
|
||||
label: <span><Icons.MdAdd /> Create new</span>,
|
||||
},
|
||||
...Object.keys(presets).map((key) => {
|
||||
return {
|
||||
value: key,
|
||||
label: key,
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
React.useEffect(() => {
|
||||
const presets = props.controller.presets.presets ?? {}
|
||||
const preset = presets[selectedPreset]
|
||||
|
||||
if (props.controller.presets.currentPresetKey !== selectedPreset) {
|
||||
props.controller.changePreset(selectedPreset)
|
||||
}
|
||||
|
||||
props.ctx.updateCurrentValue(preset)
|
||||
}, [selectedPreset])
|
||||
|
||||
return <>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
}}
|
||||
>
|
||||
<Icons.MdList
|
||||
style={{
|
||||
margin: "0"
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
style={{
|
||||
width: "50%"
|
||||
}}
|
||||
value={selectedPreset}
|
||||
options={options}
|
||||
onChange={(key) => {
|
||||
if (key === "new") {
|
||||
handleCreateNewPreset()
|
||||
} else {
|
||||
setSelectedPreset(key)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleDeletePreset}
|
||||
icon={<Icons.MdDelete />}
|
||||
disabled={selectedPreset === "default"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Sliders
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import loadable from "@loadable/component"
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
id: "player",
|
||||
icon: "PlayCircleOutlined",
|
||||
@ -104,27 +106,10 @@ export default {
|
||||
group: "general",
|
||||
description: "Adjust compression values (Warning: may cause distortion when changing values)",
|
||||
experimental: true,
|
||||
extraActions: [
|
||||
{
|
||||
id: "reset",
|
||||
title: "Reset",
|
||||
icon: "MdRefresh",
|
||||
onClick: (ctx) => {
|
||||
const values = app.cores.player.compressor.resetDefaultValues()
|
||||
|
||||
ctx.updateCurrentValue(values)
|
||||
}
|
||||
}
|
||||
],
|
||||
defaultValue: () => {
|
||||
return app.cores.player.compressor.values
|
||||
dependsOn: {
|
||||
"player.compressor": true
|
||||
},
|
||||
onUpdate: (value) => {
|
||||
app.cores.player.compressor.modifyValues(value)
|
||||
|
||||
return value
|
||||
},
|
||||
component: loadable(() => import("../components/sliderValues")),
|
||||
component: loadable(() => import("./items/player.compressor")),
|
||||
props: {
|
||||
valueFormat: (value) => `${value}dB`,
|
||||
sliders: [
|
||||
@ -163,8 +148,22 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
dependsOn: {
|
||||
"player.compressor": true
|
||||
extraActions: [
|
||||
{
|
||||
id: "reset",
|
||||
title: "Reset",
|
||||
icon: "MdRefresh",
|
||||
onClick: async (ctx) => {
|
||||
const values = await app.cores.player.compressor.resetDefaultValues()
|
||||
|
||||
ctx.updateCurrentValue(values)
|
||||
}
|
||||
}
|
||||
],
|
||||
onUpdate: (value) => {
|
||||
app.cores.player.compressor.modifyValues(value)
|
||||
|
||||
return value
|
||||
},
|
||||
storaged: false,
|
||||
},
|
||||
@ -201,7 +200,7 @@ export default {
|
||||
group: "general",
|
||||
icon: "MdGraphicEq",
|
||||
description: "Enable equalizer for audio output",
|
||||
component: loadable(() => import("../components/sliderValues")),
|
||||
component: loadable(() => import("./items/player.eq")),
|
||||
extraActions: [
|
||||
{
|
||||
id: "reset",
|
||||
@ -212,12 +211,12 @@ export default {
|
||||
|
||||
ctx.updateCurrentValue(values)
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
usePadding: false,
|
||||
props: {
|
||||
valueFormat: (value) => `${value}dB`,
|
||||
marks:[
|
||||
marks: [
|
||||
{
|
||||
value: 0,
|
||||
}
|
||||
@ -286,20 +285,9 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
defaultValue: () => {
|
||||
const values = app.cores.player.eq.values().eqValues
|
||||
|
||||
return Object.keys(values).reduce((acc, key) => {
|
||||
acc[key] = values[key].gain
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
onUpdate: (value) => {
|
||||
const values = Object.keys(value).reduce((acc, key) => {
|
||||
acc[key] = {
|
||||
gain: value[key]
|
||||
}
|
||||
acc[key] = value[key]
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
|
@ -0,0 +1,8 @@
|
||||
import SlidersWithPresets from "../../../components/slidersWithPresets"
|
||||
|
||||
export default (props) => {
|
||||
return <SlidersWithPresets
|
||||
{...props}
|
||||
controller={app.cores.player.compressor}
|
||||
/>
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import SlidersWithPresets from "../../../components/slidersWithPresets"
|
||||
|
||||
export default (props) => {
|
||||
return <SlidersWithPresets
|
||||
{...props}
|
||||
controller={app.cores.player.eq}
|
||||
/>
|
||||
}
|
117
packages/app/src/cores/player/presets.js
Normal file
117
packages/app/src/cores/player/presets.js
Normal file
@ -0,0 +1,117 @@
|
||||
import AudioPlayerStorage from "./player.storage"
|
||||
|
||||
export default class Presets {
|
||||
constructor({
|
||||
storage_key,
|
||||
defaultPresetValue,
|
||||
}) {
|
||||
if (!storage_key) {
|
||||
throw new Error("storage_key is required")
|
||||
}
|
||||
|
||||
this.storage_key = storage_key
|
||||
this.defaultPresetValue = defaultPresetValue
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
get presets() {
|
||||
return AudioPlayerStorage.get(`${this.storage_key}_presets`) ?? {
|
||||
default: this.defaultPresetValue
|
||||
}
|
||||
}
|
||||
|
||||
set presets(presets) {
|
||||
AudioPlayerStorage.set(`${this.storage_key}_presets`, presets)
|
||||
|
||||
return presets
|
||||
}
|
||||
|
||||
set currentPresetKey(key) {
|
||||
AudioPlayerStorage.set(`${this.storage_key}_current-key`, key)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
get currentPresetKey() {
|
||||
return AudioPlayerStorage.get(`${this.storage_key}_current-key`) ?? "default"
|
||||
}
|
||||
|
||||
get currentPresetValues() {
|
||||
const presets = this.presets
|
||||
const key = this.currentPresetKey
|
||||
|
||||
if (!presets || !presets[key]) {
|
||||
return this.defaultPresetValue
|
||||
}
|
||||
|
||||
return presets[key]
|
||||
}
|
||||
|
||||
deletePreset(key) {
|
||||
if (key === "default") {
|
||||
app.message.error("Cannot delete default preset")
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.currentPresetKey === key) {
|
||||
this.changePreset("default")
|
||||
}
|
||||
|
||||
let presets = this.presets
|
||||
|
||||
delete presets[key]
|
||||
|
||||
this.presets = presets
|
||||
|
||||
return presets
|
||||
}
|
||||
|
||||
createPreset(key, values) {
|
||||
let presets = this.presets
|
||||
|
||||
if (presets[key]) {
|
||||
app.message.error("Preset already exists")
|
||||
return false
|
||||
}
|
||||
|
||||
presets[key] = values ?? this.defaultPresetValue
|
||||
|
||||
this.presets = presets
|
||||
|
||||
return presets[key]
|
||||
}
|
||||
|
||||
changePreset(key) {
|
||||
let presets = this.presets
|
||||
|
||||
// create new one
|
||||
if (!presets[key]) {
|
||||
presets[key] = this.defaultPresetValue
|
||||
|
||||
this.presets = presets
|
||||
}
|
||||
|
||||
this.currentPresetKey = key
|
||||
|
||||
return presets[key]
|
||||
}
|
||||
|
||||
setToCurrent(values) {
|
||||
let preset = this.currentPresetValues
|
||||
|
||||
preset = {
|
||||
...preset,
|
||||
...values,
|
||||
}
|
||||
|
||||
// update presets
|
||||
let presets = this.presets
|
||||
|
||||
presets[this.currentPresetKey] = preset
|
||||
|
||||
this.presets = presets
|
||||
|
||||
return preset
|
||||
}
|
||||
}
|
@ -1,40 +1,110 @@
|
||||
import AudioPlayerStorage from "../../player.storage"
|
||||
import { Modal } from "antd"
|
||||
import ProcessorNode from "../node"
|
||||
import Presets from "../../presets"
|
||||
|
||||
export default class CompressorProcessorNode extends ProcessorNode {
|
||||
static refName = "compressor"
|
||||
static dependsOnSettings = ["player.compressor"]
|
||||
static defaultCompressorValues = {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.presets_controller = new Presets({
|
||||
storage_key: "compressor",
|
||||
defaultPresetValue: {
|
||||
threshold: -50,
|
||||
knee: 40,
|
||||
ratio: 12,
|
||||
attack: 0.003,
|
||||
release: 0.25,
|
||||
},
|
||||
})
|
||||
|
||||
this.state = {
|
||||
compressorValues: this.presets_controller.currentPresetValues,
|
||||
}
|
||||
|
||||
state = {
|
||||
compressorValues: AudioPlayerStorage.get("compressor") ?? CompressorProcessorNode.defaultCompressorValues,
|
||||
this.exposeToPublic = {
|
||||
presets: new Proxy(this.presets_controller, {
|
||||
get: function (target, key) {
|
||||
if (!key) {
|
||||
return target
|
||||
}
|
||||
|
||||
exposeToPublic = {
|
||||
modifyValues: function (values) {
|
||||
return target[key]
|
||||
}
|
||||
}),
|
||||
deletePreset: this.deletePreset.bind(this),
|
||||
createPreset: this.createPreset.bind(this),
|
||||
changePreset: this.changePreset.bind(this),
|
||||
resetDefaultValues: this.resetDefaultValues.bind(this),
|
||||
modifyValues: this.modifyValues.bind(this),
|
||||
detach: this._detach.bind(this),
|
||||
attach: this._attach.bind(this),
|
||||
values: this.state.compressorValues,
|
||||
}
|
||||
}
|
||||
|
||||
static refName = "compressor"
|
||||
static dependsOnSettings = ["player.compressor"]
|
||||
|
||||
deletePreset(key) {
|
||||
this.changePreset("default")
|
||||
|
||||
this.presets_controller.deletePreset(key)
|
||||
|
||||
return this.presets_controller.presets
|
||||
}
|
||||
|
||||
createPreset(key, values) {
|
||||
this.state = {
|
||||
...this.state,
|
||||
compressorValues: this.presets_controller.createPreset(key, values),
|
||||
}
|
||||
|
||||
this.presets_controller.changePreset(key)
|
||||
|
||||
return this.presets_controller.presets
|
||||
}
|
||||
|
||||
changePreset(key) {
|
||||
const values = this.presets_controller.changePreset(key)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
compressorValues: values,
|
||||
}
|
||||
|
||||
this.applyValues()
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
modifyValues(values) {
|
||||
values = this.presets_controller.setToCurrent(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 resetDefaultValues() {
|
||||
return await new Promise((resolve) => {
|
||||
Modal.confirm({
|
||||
title: "Reset to default values?",
|
||||
content: "Are you sure you want to reset to default values?",
|
||||
onOk: () => {
|
||||
this.modifyValues(this.presets_controller.defaultPresetValue)
|
||||
|
||||
resolve(this.state.compressorValues)
|
||||
},
|
||||
onCancel: () => {
|
||||
resolve(this.state.compressorValues)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async init(AudioContext) {
|
||||
|
@ -1,70 +1,119 @@
|
||||
import { Modal } from "antd"
|
||||
import ProcessorNode from "../node"
|
||||
import AudioPlayerStorage from "../../player.storage"
|
||||
import Presets from "../../presets"
|
||||
|
||||
export default class EqProcessorNode extends ProcessorNode {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.presets_controller = new Presets({
|
||||
storage_key: "eq",
|
||||
defaultPresetValue: {
|
||||
32: 0,
|
||||
64: 0,
|
||||
125: 0,
|
||||
250: 0,
|
||||
500: 0,
|
||||
1000: 0,
|
||||
2000: 0,
|
||||
4000: 0,
|
||||
8000: 0,
|
||||
16000: 0,
|
||||
},
|
||||
})
|
||||
|
||||
this.state = {
|
||||
eqValues: this.presets_controller.currentPresetValues,
|
||||
}
|
||||
|
||||
this.exposeToPublic = {
|
||||
presets: new Proxy(this.presets_controller, {
|
||||
get: function (target, key) {
|
||||
if (!key) {
|
||||
return target
|
||||
}
|
||||
|
||||
return target[key]
|
||||
},
|
||||
}),
|
||||
deletePreset: this.deletePreset.bind(this),
|
||||
createPreset: this.createPreset.bind(this),
|
||||
changePreset: this.changePreset.bind(this),
|
||||
modifyValues: this.modifyValues.bind(this),
|
||||
resetDefaultValues: this.resetDefaultValues.bind(this),
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
deletePreset(key) {
|
||||
this.changePreset("default")
|
||||
|
||||
this.presets_controller.deletePreset(key)
|
||||
|
||||
return this.presets_controller.presets
|
||||
}
|
||||
|
||||
state = {
|
||||
eqValues: AudioPlayerStorage.get("eq_values") ?? EqProcessorNode.defaultEqValue,
|
||||
createPreset(key, values) {
|
||||
this.state = {
|
||||
...this.state,
|
||||
eqValues: this.presets_controller.createPreset(key, values),
|
||||
}
|
||||
|
||||
exposeToPublic = {
|
||||
modifyValues: function (values) {
|
||||
Object.keys(values).forEach((key) => {
|
||||
if (isNaN(key)) {
|
||||
delete values[key]
|
||||
this.presets_controller.changePreset(key)
|
||||
|
||||
return this.presets_controller.presets
|
||||
}
|
||||
|
||||
changePreset(key) {
|
||||
const values = this.presets_controller.changePreset(key)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
eqValues: values,
|
||||
}
|
||||
|
||||
this.applyValues()
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
modifyValues(values) {
|
||||
values = this.presets_controller.setToCurrent(values)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
eqValues: values,
|
||||
}
|
||||
|
||||
this.applyValues()
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
resetDefaultValues() {
|
||||
Modal.confirm({
|
||||
title: "Reset to default values?",
|
||||
content: "Are you sure you want to reset to default values?",
|
||||
onOk: () => {
|
||||
this.modifyValues(this.presets_controller.defaultPresetValue)
|
||||
}
|
||||
})
|
||||
|
||||
this.state.eqValues = {
|
||||
...this.state.eqValues,
|
||||
...values,
|
||||
return this.state.eqValues
|
||||
}
|
||||
|
||||
AudioPlayerStorage.set("eq_values", this.state.eqValues)
|
||||
applyValues() {
|
||||
// apply to current instance
|
||||
this.processor.eqNodes.forEach((processor) => {
|
||||
const gainValue = this.state.eqValues[processor.frequency.value]
|
||||
|
||||
this.applyValues()
|
||||
}.bind(this),
|
||||
resetDefaultValues: function () {
|
||||
this.exposeToPublic.modifyValues(EqProcessorNode.defaultEqValue)
|
||||
|
||||
return this.state
|
||||
}.bind(this),
|
||||
values: () => this.state,
|
||||
if (processor.gain.value !== gainValue) {
|
||||
console.debug(`[EQ] Applying values to ${processor.frequency.value} Hz frequency with gain ${gainValue}`)
|
||||
processor.gain.value = gainValue
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async init() {
|
||||
@ -81,7 +130,7 @@ export default class EqProcessorNode extends ProcessorNode {
|
||||
const values = Object.entries(this.state.eqValues).map((entry) => {
|
||||
return {
|
||||
freq: parseFloat(entry[0]),
|
||||
gain: parseFloat(entry[1].gain),
|
||||
gain: parseFloat(entry[1]),
|
||||
}
|
||||
})
|
||||
|
||||
@ -116,16 +165,4 @@ export default class EqProcessorNode extends ProcessorNode {
|
||||
// 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) {
|
||||
console.debug(`[EQ] Applying values to ${processor.frequency.value} Hz frequency with gain ${gainValue}`)
|
||||
processor.gain.value = this.state.eqValues[processor.frequency.value].gain
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user