添加音效用户预设

pull/1395/head
lyswhut 2023-05-08 18:04:04 +08:00
parent df58c8eb80
commit 04337ef4b8
22 changed files with 571 additions and 60 deletions

View File

@ -12,5 +12,6 @@
],
"i18n-ally.sortKeys": true,
"javascript.preferences.importModuleSpecifier": "non-relative",
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"vue.codeActions.enabled": false
}

View File

@ -1,3 +1,3 @@
### 新增
- 新增音效设置实验性功能支持10段均衡器设置、3D立体环绕音效、内置的一些环境混响音效
- 新增音效设置实验性功能支持10段均衡器设置、内置的一些环境混响音效、3D立体环绕音效

View File

@ -10,6 +10,7 @@ export const STORE_NAMES = {
LRC_RAW: 'lyrics',
LRC_EDITED: 'lyrics_edited',
THEME: 'theme',
SOUND_EFFECT: 'sound_effect',
} as const
export const APP_EVENT_NAMES = {

View File

@ -90,6 +90,10 @@ const modules = {
get_other_source_count: 'get_other_source_count',
get_data: 'get_data',
save_data: 'save_data',
get_sound_effect_eq_preset: 'get_sound_effect_eq_preset',
save_sound_effect_eq_preset: 'save_sound_effect_eq_preset',
get_sound_effect_convolution_preset: 'get_sound_effect_convolution_preset',
save_sound_effect_convolution_preset: 'save_sound_effect_convolution_preset',
get_hot_key: 'get_hot_key',
import_user_api: 'import_user_api',

25
src/common/types/sound_effect.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
declare namespace LX {
namespace SoundEffect {
interface EQPreset {
id: string
name: string
hz31: number
hz62: number
hz125: number
hz250: number
hz500: number
hz1000: number
hz2000: number
hz4000: number
hz8000: number
hz16000: number
}
interface ConvolutionPreset {
id: string
name: string
source: string
mainGain: number
sendGain: number
}
}
}

View File

@ -228,9 +228,20 @@
"player__playing": "Now playing...",
"player__prev": "Prev",
"player__refresh_url": "Music URL expired, refreshing...",
"player__sound_effect": "Sound settings",
"player__sound_effect": "Sound settings (experimental)",
"player__sound_effect_biquad_filter": "Equalizer",
"player__sound_effect_biquad_filter_reset_btn": "Reset equalizer",
"player__sound_effect_biquad_filter_preset_classical": "Classical",
"player__sound_effect_biquad_filter_preset_dance": "Dance",
"player__sound_effect_biquad_filter_preset_electronic": "Electronic",
"player__sound_effect_biquad_filter_preset_pop": "Pop",
"player__sound_effect_biquad_filter_preset_rock": "Rock",
"player__sound_effect_biquad_filter_preset_slow": "Slow",
"player__sound_effect_biquad_filter_preset_soft": "Soft",
"player__sound_effect_biquad_filter_preset_subwoofer": "Subwoofer",
"player__sound_effect_biquad_filter_preset_vocal": "Vocal",
"player__sound_effect_biquad_filter_reset_btn": "Reset",
"player__sound_effect_biquad_filter_save_btn": "Save preset as",
"player__sound_effect_biquad_filter_save_input": "New presets...",
"player__sound_effect_convolution": "Ambient reverb sound effect",
"player__sound_effect_convolution_file_bright_hall": "Hall",
"player__sound_effect_convolution_file_cardiod_35_10_spread": "Rock",
@ -241,7 +252,7 @@
"player__sound_effect_convolution_file_matrix_1": "Matrix",
"player__sound_effect_convolution_file_matrix_2": "Matrix 2",
"player__sound_effect_convolution_file_s2_r4_bd": "Church",
"player__sound_effect_convolution_file_s3_r1_bd": "Church 2",
"player__sound_effect_convolution_file_s3_r1_bd": "Stereo",
"player__sound_effect_convolution_file_spreader25_125ms": "Indoor 2",
"player__sound_effect_convolution_file_spreader50_65ms": "Indoor",
"player__sound_effect_convolution_file_telephone": "Telephone",

View File

@ -228,24 +228,34 @@
"player__playing": "播放中...",
"player__prev": "上一首",
"player__refresh_url": "URL过期正在刷新URL...",
"player__sound_effect": "音效设置",
"player__sound_effect": "音效设置(实验性)",
"player__sound_effect_biquad_filter": "均衡器",
"player__sound_effect_biquad_filter_reset_btn": "重置均衡器",
"player__sound_effect_biquad_filter_preset_classical": "古典",
"player__sound_effect_biquad_filter_preset_dance": "舞曲",
"player__sound_effect_biquad_filter_preset_electronic": "电子乐",
"player__sound_effect_biquad_filter_preset_pop": "流行",
"player__sound_effect_biquad_filter_preset_rock": "摇滚",
"player__sound_effect_biquad_filter_preset_slow": "慢歌",
"player__sound_effect_biquad_filter_preset_soft": "柔和",
"player__sound_effect_biquad_filter_preset_subwoofer": "重低音",
"player__sound_effect_biquad_filter_preset_vocal": "人声",
"player__sound_effect_biquad_filter_reset_btn": "重置",
"player__sound_effect_biquad_filter_save_btn": "另存预设",
"player__sound_effect_biquad_filter_save_input": "新预设...",
"player__sound_effect_convolution": "环境混响音效",
"player__sound_effect_convolution_file_bright_hall": "大厅",
"player__sound_effect_convolution_file_cardiod_35_10_spread": "摇滚",
"player__sound_effect_convolution_file_cardiod_35_10_spread": "心形扩散",
"player__sound_effect_convolution_file_cinema_diningroom": "电影院",
"player__sound_effect_convolution_file_dining_living_true_stereo": "餐厅",
"player__sound_effect_convolution_file_feedback_spring": "反馈弹簧",
"player__sound_effect_convolution_file_living_bedroom_leveled": "卫生间",
"player__sound_effect_convolution_file_matrix_1": "矩阵",
"player__sound_effect_convolution_file_matrix_2": "矩阵2",
"player__sound_effect_convolution_file_matrix_1": "矩阵混响",
"player__sound_effect_convolution_file_matrix_2": "矩阵混响2",
"player__sound_effect_convolution_file_s2_r4_bd": "教堂",
"player__sound_effect_convolution_file_s3_r1_bd": "教堂2",
"player__sound_effect_convolution_file_spreader25_125ms": "室内2",
"player__sound_effect_convolution_file_s3_r1_bd": "立体声",
"player__sound_effect_convolution_file_spreader50_65ms": "室内",
"player__sound_effect_convolution_file_telephone": "电话",
"player__sound_effect_convolution_file_tim_omni_35_10_magnetic": "摇滚2",
"player__sound_effect_convolution_file_tim_omni_35_10_magnetic": "磁性立体声",
"player__sound_effect_convolution_main_gain": "原始音频增益",
"player__sound_effect_convolution_send_gain": "环境音效增益",
"player__sound_effect_panner": "3D立体环绕需使用耳机",

View File

@ -228,9 +228,20 @@
"player__playing": "播放中...",
"player__prev": "上一首",
"player__refresh_url": "URL過期正在刷新URL...",
"player__sound_effect": "音效設置",
"player__sound_effect": "音效設置(實驗性)",
"player__sound_effect_biquad_filter": "均衡器",
"player__sound_effect_biquad_filter_reset_btn": "重置均衡器",
"player__sound_effect_biquad_filter_preset_classical": "古典",
"player__sound_effect_biquad_filter_preset_dance": "舞曲",
"player__sound_effect_biquad_filter_preset_electronic": "電子樂",
"player__sound_effect_biquad_filter_preset_pop": "流行",
"player__sound_effect_biquad_filter_preset_rock": "搖滾",
"player__sound_effect_biquad_filter_preset_slow": "慢歌",
"player__sound_effect_biquad_filter_preset_soft": "柔和",
"player__sound_effect_biquad_filter_preset_subwoofer": "重低音",
"player__sound_effect_biquad_filter_preset_vocal": "人聲",
"player__sound_effect_biquad_filter_reset_btn": "重置",
"player__sound_effect_biquad_filter_save_btn": "另存預設",
"player__sound_effect_biquad_filter_save_input": "新預設...",
"player__sound_effect_convolution": "環境混響音效",
"player__sound_effect_convolution_file_bright_hall": "大廳",
"player__sound_effect_convolution_file_cardiod_35_10_spread": "搖滾",
@ -241,7 +252,7 @@
"player__sound_effect_convolution_file_matrix_1": "矩陣",
"player__sound_effect_convolution_file_matrix_2": "矩陣2",
"player__sound_effect_convolution_file_s2_r4_bd": "教堂",
"player__sound_effect_convolution_file_s3_r1_bd": "教堂2",
"player__sound_effect_convolution_file_s3_r1_bd": "立體聲",
"player__sound_effect_convolution_file_spreader25_125ms": "室內2",
"player__sound_effect_convolution_file_spreader50_65ms": "室內",
"player__sound_effect_convolution_file_telephone": "電話",

View File

@ -9,6 +9,7 @@ import sync from './sync'
import data from './data'
import music from './music'
import download from './download'
import soundEffect from './soundEffect'
import { sendEvent } from '../main'
export * from './app'
@ -33,6 +34,7 @@ export default () => {
data()
music()
download()
soundEffect()
global.lx.event_app.on('updated_config', (keys, setting) => {
sendConfigChange(setting)

View File

@ -0,0 +1,20 @@
import { STORE_NAMES } from '@common/constants'
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
import { mainOn, mainHandle } from '@common/mainIpc'
import getStore from '@main/utils/store'
export default () => {
mainHandle<LX.SoundEffect.EQPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_sound_effect_eq_preset, async() => {
return getStore(STORE_NAMES.SOUND_EFFECT).get('eqPreset') as LX.SoundEffect.EQPreset[] | null ?? []
})
mainOn<LX.SoundEffect.EQPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.save_sound_effect_eq_preset, ({ params }) => {
getStore(STORE_NAMES.SOUND_EFFECT).set('eqPreset', params)
})
mainHandle<LX.SoundEffect.ConvolutionPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_sound_effect_convolution_preset, async() => {
return getStore(STORE_NAMES.SOUND_EFFECT).get('convolutionPreset') as LX.SoundEffect.ConvolutionPreset[] | null ?? []
})
mainOn<LX.SoundEffect.ConvolutionPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.save_sound_effect_convolution_preset, ({ params }) => {
getStore(STORE_NAMES.SOUND_EFFECT).set('convolutionPreset', params)
})
}

View File

@ -10,3 +10,4 @@ import '@common/types/player'
import '@common/types/desktop_lyric'
import '@common/types/theme'
import '@common/types/ipc_main'
import '@common/types/sound_effect'

View File

@ -0,0 +1,92 @@
<template>
<base-btn min :class="[$style.newPreset, {[$style.editing]: isEditing}]" :aria-label="$t('player__sound_effect_biquad_filter_save_btn')" @click="handleEditing($event)">
<svg-icon name="plus" />
<base-input ref="input" :class="$style.newPresetInput" :value="newPresetName" :placeholder="$t('player__sound_effect_biquad_filter_save_input')" @keyup.enter="handleSave($event)" @blur="handleSave($event)" />
</base-btn>
</template>
<script setup>
import { ref, nextTick } from '@common/utils/vueTools'
import { appSetting } from '@renderer/store/setting'
import { saveUserConvolutionPreset } from '@renderer/store/soundEffect'
const isEditing = ref(false)
const input = ref(false)
const newPresetName = ref('')
const handleEditing = () => {
if (isEditing.value) return
// if (!this.newPresetName) this.newPresetName = this.listName
isEditing.value = true
nextTick(() => {
input.value.$el.focus()
})
}
const handleSave = (event) => {
let name = event.target.value.trim()
newPresetName.value = event.target.value = ''
isEditing.value = false
if (!name) return
if (name.length > 20) name = name.substring(0, 20)
saveUserConvolutionPreset({
id: Date.now().toString(),
name,
source: appSetting['player.soundEffect.convolution.fileName'],
mainGain: appSetting['player.soundEffect.convolution.mainGain'],
sendGain: appSetting['player.soundEffect.convolution.sendGain'],
})
}
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.newPreset {
position: relative;
border: 1px dashed var(--color-primary-font-hover);
// background-color: var(--color-main-background);
color: var(--color-primary-font-hover);
opacity: .7;
height: 22px;
&.editing {
opacity: 1;
width: 90px;
svg {
display: none;
}
.newPresetInput {
display: block;
}
}
:global {
.svg-icon {
vertical-align: 0;
}
}
}
.newPresetInput {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
// line-height: 16px;
background: none !important;
font-size: 12px;
text-align: center;
font-family: inherit;
box-sizing: border-box;
padding: 0 3px;
border-radius: 0;
display: none;
&::placeholder {
font-size: 12px;
}
}
</style>

View File

@ -0,0 +1,99 @@
<template>
<base-btn min :class="[$style.newPreset, {[$style.editing]: isEditing}]" :aria-label="$t('player__sound_effect_biquad_filter_save_btn')" @click="handleEditing($event)">
<svg-icon name="plus" />
<base-input ref="input" :class="$style.newPresetInput" :value="newPresetName" :placeholder="$t('player__sound_effect_biquad_filter_save_input')" @keyup.enter="handleSave($event)" @blur="handleSave($event)" />
</base-btn>
</template>
<script setup>
import { ref, nextTick } from '@common/utils/vueTools'
import { appSetting } from '@renderer/store/setting'
import { saveUserEQPreset } from '@renderer/store/soundEffect'
const isEditing = ref(false)
const input = ref(false)
const newPresetName = ref('')
const handleEditing = () => {
if (isEditing.value) return
// if (!this.newPresetName) this.newPresetName = this.listName
isEditing.value = true
nextTick(() => {
input.value.$el.focus()
})
}
const handleSave = (event) => {
let name = event.target.value.trim()
newPresetName.value = event.target.value = ''
isEditing.value = false
if (!name) return
if (name.length > 20) name = name.substring(0, 20)
saveUserEQPreset({
id: Date.now().toString(),
name,
hz31: appSetting['player.soundEffect.biquadFilter.hz31'],
hz62: appSetting['player.soundEffect.biquadFilter.hz62'],
hz125: appSetting['player.soundEffect.biquadFilter.hz125'],
hz250: appSetting['player.soundEffect.biquadFilter.hz250'],
hz500: appSetting['player.soundEffect.biquadFilter.hz500'],
hz1000: appSetting['player.soundEffect.biquadFilter.hz1000'],
hz2000: appSetting['player.soundEffect.biquadFilter.hz2000'],
hz4000: appSetting['player.soundEffect.biquadFilter.hz4000'],
hz8000: appSetting['player.soundEffect.biquadFilter.hz8000'],
hz16000: appSetting['player.soundEffect.biquadFilter.hz16000'],
})
}
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.newPreset {
position: relative;
border: 1px dashed var(--color-primary-font-hover);
// background-color: var(--color-main-background);
color: var(--color-primary-font-hover);
opacity: .7;
height: 22px;
&.editing {
opacity: 1;
width: 90px;
svg {
display: none;
}
.newPresetInput {
display: block;
}
}
:global {
.svg-icon {
vertical-align: 0;
}
}
}
.newPresetInput {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
// line-height: 16px;
background: none !important;
font-size: 12px;
text-align: center;
font-family: inherit;
box-sizing: border-box;
padding: 0 3px;
border-radius: 0;
display: none;
&::placeholder {
font-size: 12px;
}
}
</style>

View File

@ -27,13 +27,19 @@
</div>
</div>
</div>
<div :class="['scroll', $style.saveList]">
<base-btn v-for="item in userPresetList" :key="item.id" min @click="handleSetPreset(item)" @contextmenu="handleRemovePreset(item.id)">{{ item.name }}</base-btn>
<AddConvolutionPresetBtn />
</div>
</div>
</template>
<script setup>
// import { ref } from '@common/utils/vueTools'
import { ref, onMounted } from '@common/utils/vueTools'
import { appSetting, updateSetting } from '@renderer/store/setting'
import { convolutions } from '@renderer/plugins/player'
import AddConvolutionPresetBtn from './AddConvolutionPresetBtn'
import { getUserConvolutionPresetList, removeUserConvolutionPreset } from '@renderer/store/soundEffect'
const updateConvolution = val => {
const target = convolutions.find(c => c.source == val)
@ -54,6 +60,24 @@ const handleUpdateSendGain = (value) => {
updateSetting({ 'player.soundEffect.convolution.sendGain': Math.round(value) })
}
const handleSetPreset = (item) => {
updateSetting({
'player.soundEffect.convolution.fileName': item.source,
'player.soundEffect.convolution.mainGain': item.mainGain,
'player.soundEffect.convolution.sendGain': item.sendGain,
})
}
const userPresetList = ref([])
const handleRemovePreset = id => {
removeUserConvolutionPreset(id)
}
onMounted(() => {
getUserConvolutionPresetList().then(list => {
userPresetList.value = list
})
})
</script>
@ -63,6 +87,7 @@ const handleUpdateSendGain = (value) => {
display: flex;
flex-flow: column nowrap;
gap: 3px;
min-height: 0;
}
.convolution {
display: flex;
@ -110,4 +135,11 @@ const handleUpdateSendGain = (value) => {
color: var(--color-primary-font);
}
}
.saveList {
display: flex;
flex-flow: row wrap;
margin-top: 10px;
gap: 10px;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div :class="$style.contnet">
<div :class="$style.header">
<h3 class="player__sound_effect_title">{{ $t('player__sound_effect_panner') }}</h3>
<div class="player__sound_effect_title" :class="$style.header">
<h3>{{ $t('player__sound_effect_panner') }}</h3>
<base-checkbox
id="player__sound_effect_panner_enabled"
:class="$style.checkbox"
@ -11,16 +11,16 @@
/>
</div>
<div :class="$style.eqList">
<div :class="$style.eqItem">
<span :class="$style.label">{{ $t('player__sound_effect_panner_sound_r') }}</span>
<base-slider-bar :class="$style.slider" :value="appSetting['player.soundEffect.panner.soundR']" :min="1" :max="30" @change="handleUpdateSoundR" />
<span :class="[$style.value, { [$style.active]: appSetting['player.soundEffect.panner.soundR'] != 5 }]">{{ appSetting['player.soundEffect.panner.soundR'] }}</span>
</div>
<div :class="$style.eqItem">
<span :class="$style.label">{{ $t('player__sound_effect_panner_sound_speed') }}</span>
<base-slider-bar :class="$style.slider" :value="appSetting['player.soundEffect.panner.speed']" :min="1" :max="50" @change="handleUpdateSpeed" />
<span :class="[$style.value, { [$style.active]: appSetting['player.soundEffect.panner.speed'] != 25 }]">{{ appSetting['player.soundEffect.panner.speed'] }}</span>
</div>
<div :class="$style.eqItem">
<span :class="$style.label">{{ $t('player__sound_effect_panner_sound_r') }}</span>
<base-slider-bar :class="$style.slider" :value="appSetting['player.soundEffect.panner.soundR']" :min="1" :max="30" @change="handleUpdateSoundR" />
<span :class="[$style.value, { [$style.active]: appSetting['player.soundEffect.panner.soundR'] != 5 }]">{{ appSetting['player.soundEffect.panner.soundR'] }}</span>
</div>
</div>
</div>
</template>
@ -53,9 +53,19 @@ const handleUpdateSpeed = (value) => {
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.contnet {
padding-top: 15px;
position: relative;
display: flex;
flex-flow: column nowrap;
gap: 8px;
&:before {
.mixin-after;
position: absolute;
top: 0;
height: 1px;
width: 100%;
border-top: 1px dashed var(--color-primary-light-100-alpha-700);
}
}
.header {
display: flex;
@ -63,7 +73,7 @@ const handleUpdateSpeed = (value) => {
justify-content: space-between;
align-items: center;
padding-bottom: 5px;
padding-top: 5px;
// padding-top: 5px;
}
.eqList {
display: flex;

View File

@ -1,16 +1,22 @@
<template>
<div :class="$style.contnet">
<div :class="$style.header">
<h3 class="player__sound_effect_title">{{ $t('player__sound_effect_biquad_filter') }}</h3>
<div class="player__sound_effect_title" :class="$style.header">
<h3>{{ $t('player__sound_effect_biquad_filter') }}</h3>
<base-btn min @click="handleReset">{{ $t('player__sound_effect_biquad_filter_reset_btn') }}</base-btn>
</div>
<div :class="$style.eqList">
<div v-for="(v, i) in freqs" :key="v" :class="$style.eqItem">
<span :class="$style.label">{{ labels[i] }}</span>
<base-slider-bar :class="$style.slider" :value="appSetting[`player.soundEffect.biquadFilter.hz${v}`]" :min="-20" :max="20" @change="handleUpdate(v, $event)" />
<base-slider-bar :class="$style.slider" :value="appSetting[`player.soundEffect.biquadFilter.hz${v}`]" :min="-15" :max="15" @change="handleUpdate(v, $event)" />
<span :class="$style.value">{{ appSetting[`player.soundEffect.biquadFilter.hz${v}`] }}db</span>
</div>
</div>
<div :class="['scroll', $style.saveList]">
<!-- <base-btn min @click="handleSetPreset(item)">{{ $t(`player__sound_effect_biquad_filter_preset_slow`) }}</base-btn> -->
<base-btn v-for="item in freqsPreset" :key="item.name" min @click="handleSetPreset(item)">{{ $t(`player__sound_effect_biquad_filter_preset_${item.name}`) }}</base-btn>
<base-btn v-for="item in userPresetList" :key="item.id" min @click="handleSetPreset(item)" @contextmenu="handleRemovePreset(item.id)">{{ item.name }}</base-btn>
<AddEQPresetBtn />
</div>
<!-- <div :class="$style.footer">
<base-btn min @click="handleReset">{{ $t('player__sound_effect_biquad_filter_reset_btn') }}</base-btn>
</div> -->
@ -18,9 +24,11 @@
</template>
<script setup>
// import { reactive } from '@common/utils/vueTools'
import { freqs } from '@renderer/plugins/player'
import { onMounted, ref } from '@common/utils/vueTools'
import { freqs, freqsPreset } from '@renderer/plugins/player'
import { appSetting, updateSetting } from '@renderer/store/setting'
import AddEQPresetBtn from './AddEQPresetBtn'
import { getUserEQPresetList, removeUserEQPreset } from '@renderer/store/soundEffect'
const labels = freqs.map(num => num < 1000 ? num : `${num / 1000}k`)
@ -39,6 +47,33 @@ const handleReset = () => {
updateSetting(setting)
}
const handleSetPreset = (item) => {
updateSetting({
'player.soundEffect.biquadFilter.hz31': item.hz31,
'player.soundEffect.biquadFilter.hz62': item.hz62,
'player.soundEffect.biquadFilter.hz125': item.hz125,
'player.soundEffect.biquadFilter.hz250': item.hz250,
'player.soundEffect.biquadFilter.hz500': item.hz500,
'player.soundEffect.biquadFilter.hz1000': item.hz1000,
'player.soundEffect.biquadFilter.hz2000': item.hz2000,
'player.soundEffect.biquadFilter.hz4000': item.hz4000,
'player.soundEffect.biquadFilter.hz8000': item.hz8000,
'player.soundEffect.biquadFilter.hz16000': item.hz16000,
})
}
const userPresetList = ref([])
const handleRemovePreset = id => {
removeUserEQPreset(id)
}
onMounted(() => {
getUserEQPresetList().then(list => {
userPresetList.value = list
})
})
</script>
<style lang="less" module>
@ -47,6 +82,7 @@ const handleReset = () => {
display: flex;
flex-flow: column nowrap;
gap: 8px;
min-height: 0;
}
.header {
display: flex;
@ -54,18 +90,40 @@ const handleReset = () => {
justify-content: space-between;
align-items: center;
padding-bottom: 5px;
padding-top: 5px;
// padding-top: 5px;
}
.eqList {
display: flex;
flex-flow: column nowrap;
gap: 15px;
flex-flow: row wrap;
// gap: 15px;
width: 100%;
justify-content: space-between;
position: relative;
&:before {
.mixin-after;
position: absolute;
left: 50%;
height: 100%;
border-left: 1px dashed var(--color-primary-light-100-alpha-700);
}
}
.eqItem {
display: flex;
flex-flow: row nowrap;
width: 50%;
gap: 8px;
margin-bottom: 15px;
box-sizing: border-box;
&:nth-child(odd) {
padding-right: 10px;
}
&:nth-child(even) {
padding-left: 10px;
}
&:nth-last-child(1), &:nth-last-child(2) {
margin-bottom: 0;
}
}
.label {
flex: none;
@ -95,4 +153,11 @@ const handleReset = () => {
flex: auto;
}
.saveList {
display: flex;
flex-flow: row wrap;
margin-top: 10px;
gap: 10px;
}
</style>

View File

@ -1,22 +1,27 @@
<template>
<material-popup-btn :class="$style.btnContent">
<button :class="$style.btn" :aria-label="$t('player__sound_effect')">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" width="90%" viewBox="0 0 24 24" space="preserve">
<use xlink:href="#icon-tune-variant" />
</svg>
</button>
<template #content>
<div :class="$style.content">
<button :class="$style.btn" :aria-label="$t('player__sound_effect')" @click="visible = true">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" width="90%" viewBox="0 0 24 24" space="preserve">
<use xlink:href="#icon-tune-variant" />
</svg>
</button>
<material-modal :show="visible" bg-close="bg-close" :teleport="teleport" @close="visible = false">
<!-- <main :class="$style.main"> -->
<!-- <h2 :class="$style.title">{{ $t('theme_edit_modal__title') }}</h2> -->
<div :class="$style.content">
<div :class="$style.row">
<AudioConvolution />
<AudioPanner />
</div>
<div :class="$style.row">
<BiquadFilter />
</div>
</template>
</material-popup-btn>
</div>
<!-- </main> -->
</material-modal>
</template>
<script setup>
// import { reactive, ref } from '@common/utils/vueTools'
import { ref } from '@common/utils/vueTools'
// import useNextTogglePlay from '@renderer/utils/compositions/useNextTogglePlay'
// import useToggleDesktopLyric from '@renderer/utils/compositions/useToggleDesktopLyric'
// import { musicInfo, playMusicInfo } from '@renderer/store/player/state'
@ -27,16 +32,20 @@ import BiquadFilter from './BiquadFilter'
import AudioPanner from './AudioPanner'
import AudioConvolution from './AudioConvolution'
defineProps({
teleport: {
type: String,
default: '#root',
},
})
const visible = ref(false)
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.btnContent {
flex: none;
height: 100%;
}
.btn {
position: relative;
// color: var(--color-button-font);
@ -68,12 +77,40 @@ import AudioConvolution from './AudioConvolution'
}
}
.content {
.main {
min-width: 300px;
// max-height: 100%;
// overflow: hidden;
display: flex;
flex-flow: column nowrap;
padding: 5px 3px;
gap: 15px;
width: 400px;
justify-content: center;
min-height: 0;
}
// .title {
// flex: none;
// font-size: 16px;
// color: var(--color-font);
// line-height: 1.3;
// text-align: center;
// padding: 10px;
// }
.content {
display: flex;
flex-flow: row nowrap;
padding: 0 15px;
margin: 15px 0;
gap: 30px;
position: relative;
min-height: 0;
&:before {
.mixin-after;
position: absolute;
left: 50%;
height: 100%;
border-left: 1px dashed var(--color-primary-light-100-alpha-700);
}
// width: 400px;
:global {
// .player__sound_effect_contnet {
@ -82,10 +119,16 @@ import AudioConvolution from './AudioConvolution'
.player__sound_effect_title {
// margin-bottom: 10px;
font-size: 14px;
padding-bottom: 5px;
padding-bottom: 8px;
}
}
}
.row {
width: 50%;
display: flex;
gap: 15px;
flex-flow: column nowrap;
}
</style>

View File

@ -7,21 +7,32 @@ let analyser: AnalyserNode
export const freqs = [31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000] as const
type Freqs = (typeof freqs)[number]
let biquads: Map<`hz${Freqs}`, BiquadFilterNode>
export const freqsPreset = [
{ name: 'pop', hz31: 6, hz62: 5, hz125: -3, hz250: -2, hz500: 5, hz1000: 4, hz2000: -4, hz4000: -3, hz8000: 6, hz16000: 4 },
{ name: 'dance', hz31: 4, hz62: 3, hz125: -4, hz250: -6, hz500: 0, hz1000: 0, hz2000: 3, hz4000: 4, hz8000: 4, hz16000: 5 },
{ name: 'rock', hz31: 7, hz62: 6, hz125: 2, hz250: 1, hz500: -3, hz1000: -4, hz2000: 2, hz4000: 1, hz8000: 4, hz16000: 5 },
{ name: 'classical', hz31: 6, hz62: 7, hz125: 1, hz250: 2, hz500: -1, hz1000: 1, hz2000: -4, hz4000: -6, hz8000: -7, hz16000: -8 },
{ name: 'vocal', hz31: -5, hz62: -6, hz125: -4, hz250: -3, hz500: 3, hz1000: 4, hz2000: 5, hz4000: 4, hz8000: -3, hz16000: -3 },
{ name: 'slow', hz31: 5, hz62: 4, hz125: 2, hz250: 0, hz500: -2, hz1000: 0, hz2000: 3, hz4000: 6, hz8000: 7, hz16000: 8 },
{ name: 'electronic', hz31: 6, hz62: 5, hz125: 0, hz250: -5, hz500: -4, hz1000: 0, hz2000: 6, hz4000: 8, hz8000: 8, hz16000: 7 },
{ name: 'subwoofer', hz31: 8, hz62: 7, hz125: 5, hz250: 4, hz500: 0, hz1000: 0, hz2000: 0, hz4000: 0, hz8000: 0, hz16000: 0 },
{ name: 'soft', hz31: -5, hz62: -5, hz125: -4, hz250: -4, hz500: 3, hz1000: 2, hz2000: 4, hz4000: 4, hz8000: 0, hz16000: 0 },
] as const
export const convolutions = [
{ name: 'telephone', mainGain: 0.0, sendGain: 3.0, source: 'filter-telephone.wav' }, // 电话
{ name: 's2_r4_bd', mainGain: 1.8, sendGain: 0.9, source: 's2_r4_bd.wav' }, // 教堂
{ name: 's3_r1_bd', mainGain: 1.8, sendGain: 0.8, source: 's3_r1_bd.wav' },
{ name: 'matrix_1', mainGain: 1.5, sendGain: 0.9, source: 'matrix-reverb1.wav' },
{ name: 'matrix_2', mainGain: 1.3, sendGain: 1, source: 'matrix-reverb2.wav' },
{ name: 'bright_hall', mainGain: 0.8, sendGain: 2.4, source: 'bright-hall.wav' },
{ name: 'cinema_diningroom', mainGain: 0.6, sendGain: 2.3, source: 'cinema-diningroom.wav' },
{ name: 'dining_living_true_stereo', mainGain: 0.6, sendGain: 1.8, source: 'dining-living-true-stereo.wav' },
{ name: 'living_bedroom_leveled', mainGain: 0.6, sendGain: 2.1, source: 'living-bedroom-leveled.wav' },
{ name: 'spreader50_65ms', mainGain: 1, sendGain: 2.5, source: 'spreader50-65ms.wav' },
{ name: 'spreader25_125ms', mainGain: 1, sendGain: 2.5, source: 'spreader25-125ms.wav' },
// { name: 'spreader25_125ms', mainGain: 1, sendGain: 2.5, source: 'spreader25-125ms.wav' },
// { name: 'backslap', mainGain: 1.8, sendGain: 0.8, source: 'backslap1.wav' },
{ name: 'cardiod_35_10_spread', mainGain: 1.8, sendGain: 0.8, source: 'cardiod-35-10-spread.wav' },
{ name: 'tim_omni_35_10_magnetic', mainGain: 1.8, sendGain: 0.8, source: 'tim-omni-35-10-magnetic.wav' },
{ name: 's3_r1_bd', mainGain: 1.8, sendGain: 0.8, source: 's3_r1_bd.wav' },
{ name: 'matrix_1', mainGain: 1.5, sendGain: 0.9, source: 'matrix-reverb1.wav' },
{ name: 'matrix_2', mainGain: 1.3, sendGain: 1, source: 'matrix-reverb2.wav' },
{ name: 'cardiod_35_10_spread', mainGain: 1.8, sendGain: 0.6, source: 'cardiod-35-10-spread.wav' },
{ name: 'tim_omni_35_10_magnetic', mainGain: 1, sendGain: 0.2, source: 'tim-omni-35-10-magnetic.wav' },
// { name: 'spatialized', mainGain: 1.8, sendGain: 0.8, source: 'spatialized8.wav' },
// { name: 'zing_long_stereo', mainGain: 0.8, sendGain: 1.8, source: 'zing-long-stereo.wav' },
{ name: 'feedback_spring', mainGain: 1.8, sendGain: 0.8, source: 'feedback-spring.wav' },

View File

@ -0,0 +1,56 @@
import { reactive, toRaw } from '@common/utils/vueTools'
import { getUserSoundEffectConvolutionPresetList, getUserSoundEffectEQPresetList, saveUserSoundEffectConvolutionPresetList, saveUserSoundEffectEQPresetList } from '@renderer/utils/ipc'
let userEqPresetList: LX.SoundEffect.EQPreset[] | null = null
export const getUserEQPresetList = async() => {
if (userEqPresetList == null) {
userEqPresetList = reactive(await getUserSoundEffectEQPresetList())
}
return userEqPresetList
}
export const saveUserEQPreset = async(preset: LX.SoundEffect.EQPreset) => {
if (userEqPresetList == null) {
userEqPresetList = reactive(await getUserSoundEffectEQPresetList())
}
const target = userEqPresetList.find(p => p.id == preset.id)
if (target) Object.assign(target, preset)
else userEqPresetList.push(preset)
saveUserSoundEffectEQPresetList(toRaw(userEqPresetList))
}
export const removeUserEQPreset = async(id: string) => {
if (userEqPresetList == null) {
userEqPresetList = reactive(await getUserSoundEffectEQPresetList())
}
const index = userEqPresetList.findIndex(p => p.id == id)
if (index < 0) return
userEqPresetList.splice(index, 1)
saveUserSoundEffectEQPresetList(toRaw(userEqPresetList))
}
let userConvolutionPresetList: LX.SoundEffect.ConvolutionPreset[] | null = null
export const getUserConvolutionPresetList = async() => {
if (userEqPresetList == null) {
userConvolutionPresetList = reactive(await getUserSoundEffectConvolutionPresetList())
}
return userConvolutionPresetList
}
export const saveUserConvolutionPreset = async(preset: LX.SoundEffect.ConvolutionPreset) => {
if (userConvolutionPresetList == null) {
userConvolutionPresetList = reactive(await getUserSoundEffectConvolutionPresetList())
}
const target = userConvolutionPresetList.find(p => p.id == preset.id)
if (target) Object.assign(target, preset)
else userConvolutionPresetList.push(preset)
saveUserSoundEffectConvolutionPresetList(toRaw(userConvolutionPresetList))
}
export const removeUserConvolutionPreset = async(id: string) => {
if (userConvolutionPresetList == null) {
userConvolutionPresetList = reactive(await getUserSoundEffectConvolutionPresetList())
}
const index = userConvolutionPresetList.findIndex(p => p.id == id)
if (index < 0) return
userConvolutionPresetList.splice(index, 1)
saveUserSoundEffectConvolutionPresetList(toRaw(userConvolutionPresetList))
}

View File

@ -13,3 +13,4 @@ import '@common/types/desktop_lyric'
import '@common/types/ipc_renderer'
import '@common/types/config_files'
import '@common/types/music_metadata'
import '@common/types/sound_effect'

View File

@ -291,6 +291,22 @@ export const getSystemFonts = async() => {
})
}
export const getUserSoundEffectEQPresetList = async() => {
return await rendererInvoke<LX.SoundEffect.EQPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_sound_effect_eq_preset)
}
export const saveUserSoundEffectEQPresetList = (list: LX.SoundEffect.EQPreset[]) => {
rendererSend<LX.SoundEffect.EQPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.save_sound_effect_eq_preset, list)
}
export const getUserSoundEffectConvolutionPresetList = async() => {
return await rendererInvoke<LX.SoundEffect.ConvolutionPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_sound_effect_convolution_preset)
}
export const saveUserSoundEffectConvolutionPresetList = (list: LX.SoundEffect.ConvolutionPreset[]) => {
rendererSend<LX.SoundEffect.ConvolutionPreset[]>(WIN_MAIN_RENDERER_EVENT_NAME.save_sound_effect_convolution_preset, list)
}
export const allHotKeys = markRaw({
local: [